Wisselen van lettertype kleur voor verschillende achtergronden met CSS

0
14

Ooit een van hen, “ik kan dat doen met CSS!” momenten tijdens het kijken naar iemand flex JavaScript hun spieren? Dat is precies het gevoel dat ik kreeg tijdens het kijken van Dag-Inge Aas & Ida Aalen praten op CSSconf EU-2018.

Ze zijn gevestigd in Noorwegen, waar WCAG toegankelijkheid is niet alleen goede oefening, maar eigenlijk vereist door de wet (ga, Noorwegen!). Als ze aan het ontwikkelen waren een functie waarmee de gebruiker selecteerbare kleur thema voor hun belangrijkste product, worden ze geconfronteerd met een uitdaging: het automatisch aanpassen van het lettertype, de kleur gebaseerd op de geselecteerde achtergrondkleur van de container. Als de achtergrond donker is, dan zou het ideaal zijn om een witte tekst te houden WCAG-contrast-compatibel zijn. Maar wat gebeurt er als een lichte achtergrond kleur is geselecteerd in de plaats? De tekst is zowel onleesbaar is en niet van de toegankelijkheid.

Ze gebruikt een elegante aanpak en loste het probleem op met behulp van de “kleur” npm-pakket, het toevoegen van voorwaardelijke grenzen en automatische secundaire kleur generatie, terwijl ze op.

Maar dat is een JavaScript oplossing. Hier is mijn pure CSS alternatief.

De uitdaging

Hier is de criteria die ik wilde bereiken:

  • Wijzigen van het lettertype, de kleur naar zwart of wit, afhankelijk van de achtergrond kleur
  • Het toepassen van dezelfde soort logica van grenzen, met een donkere variatie van de basis kleur van de achtergrond te verbeteren knop zichtbaarheid, alleen als achtergrond is echt licht
  • Het automatisch genereren van een secundaire, 60º hue-gedraaid kleur

Het werken met de HSL-kleuren-en CSS-variabelen

De eenvoudigste benadering die ik kon bedenken voor dit impliceert het uitvoeren van de HSL-kleuren. Het instellen van de achtergrond declaraties van de HSL, waar elke parameter is een aangepaste CSS eigenschap kan voor een heel eenvoudige manier om te bepalen lichtheid en gebruik het als basis voor onze voorwaarden.

:root {
–tint: 220;
–za: 100;
–licht: 81;
}

.btn {
achtergrond: de hsl(var(–tint), calc(var(–za) * 1%), calc(var(–licht) * 1%));
}

Dit moet ons toelaten om de swap op de achtergrond om het even welke kleur we willen graag bij uitvoering door het veranderen van de variabelen en het uitvoeren van een if/else statement om de voorgrondkleur te veranderen.

Maar… we hebben geen if/else statements over CSS… of doen we?

De introductie van CSS voorwaardelijke instructies

Sinds de introductie van CSS variabelen, kregen we ook voorwaardelijke instructies om te gaan met hen. Of de soort van.

Deze truc is gebaseerd op het feit dat enkele CSS parameters krijgen beperkt tot een min-en max-waarde. Denk er bijvoorbeeld eens dekking. Het geldige bereik is 0 tot 1, dus we normaal te houden. Maar we kunnen ook verklaren een dekking van 2, 3, of 1000, en het zal worden gemaximeerd op 1 en interpreteert deze. Op een vergelijkbare manier kunnen we ook verklaren van een negatieve dekkingswaarde, en krijgen het beperkt tot 0.

.iets {
dekking: -2; /* herleid tot 0, transparant */
dekking: -1; /* herleid tot 0, transparant */
dekking: 2; /*oplossing 1, volledig dekkend */
dekking: 100; /* oplossing 1, volledig dekkend */
}

Het toepassen van de truc om onze lettertype kleur verklaring

De lichtheid parameter van een HSL-kleur verklaring gedraagt zich op een soortgelijke manier, het afdekken van eventuele negatieve waarde op 0 (wat resulteert in het zwart, wat de tint en de verzadiging gebeurt te zijn) en alles boven de 100% is beperkt tot 100% (dat is altijd wit).

Dus, kunnen we verklaren dat de kleur als de HSL, aftrekken van de gewenste drempel van de lichtheid van de parameter, dan vermenigvuldigen met 100% te dwingen om te overshoot één van de beperkingen van de (sub-zero of hoger dan 100%). Daar moeten we negatieve resultaten op te lossen in het wit en positieve resultaten op te lossen in het zwart, wij hebben ook te inverteren het het resultaat te vermenigvuldigen met -1.

:root {
–licht: 80;
/* de drempel waarop kleuren worden beschouwd als “licht”. Bereik: gehele getallen van 0 tot 100,
aanbevolen 50 – 70 */
–drempel: 60;
}

.btn {
/* Lichtheid waarde onder de drempel zal resulteren in wit, alle bovenstaande regels zal resulteren in het zwart */
–schakelaar: calc (((var–licht) – var(–drempel)) * -100%);
kleur: hsl(0, 0%, var(–schakelaar));
}

Laten we het stukje code: vanaf een lichtheid van 80 en het overwegen van een 60-drempel, de aftrekken resultaten in 20, vermenigvuldigd met -100%, resulteert in -2000%, met een maximum op 0%. Onze achtergrond is lichter dan de drempel, dus we rekening houden met het licht en van toepassing zijn zwarte tekst.

Als we de –licht veranderlijk als de 20, de aftrek zou hebben geleid tot -40, waarmee vermenigvuldigd met -100% zou 4000%, met een maximum tot 100%. Onze lichte variabele is lager dan de drempel, daarom beschouwen wij het als een “donkere” achtergrond en toepassing van een witte tekst te houden met een hoog contrast.

Het genereren van een voorwaardelijke grens

Wanneer de achtergrond van een element wordt te licht, het kan gemakkelijk verloren tegen een witte achtergrond. We hebben een knop en het niet eens merken. Om een betere GEBRUIKERSINTERFACE voor het echt licht kleuren, kunnen we stellen een grens gebaseerd op de zeer dezelfde achtergrond kleur, alleen donkerder.

Een lichte achtergrond met een donkere rand op basis van die achtergrond kleur.

Om dat te bereiken, kunnen we gebruik maken van dezelfde techniek, maar breng het aan de alpha-kanaal van een HSLA-verklaring. Op die manier kunnen we de kleur aanpassen als dat nodig is, dan hebben beide volledig transparant of volledig ondoorzichtig.

:root {
/* (…) */
–licht: 85;
–grens-drempel: 80;
}

.btn {
/* hiermee wordt de grens-kleur als een 30% donkerder tint van dezelfde kleur*/
–grens-licht: calc(var(–licht) * 0.7%);
–grens-alpha: calc (((var–licht) – var(–grens-drempel)) * 10);

grens: .1em solide hsla(var(–tint), calc(var(–za) * 1%), var(–grens-licht), var(–grens-alpha));
}

Uitgaande van een tint van 0 en verzadiging op 100%, de bovenstaande code zal zorgen voor een volledig dekkend, pure red grens bij 70% van de originele helderheid als de achtergrond lichtheid hoger is dan 80, of een volledig transparante rand (en dus geen rand) als het donkerder is.

Het instellen van de secundaire, 60º hue-gedraaid kleur

Waarschijnlijk de eenvoudigste van de uitdagingen. Er zijn twee mogelijke benaderingen:

  1. filter: hue-draaien(60): Dit is het eerste dat opkomt, maar het is niet de beste oplossing, omdat het van invloed zou zijn op de kleur van de onderliggende elementen. Indien nodig, kan worden omgekeerd met een tegengestelde rotatie.
  2. HSL hue + 60: De andere optie is om onze tint variabele en het toevoegen van 60. Sinds de tint parameter niet hebben dat het afdekken gedrag op 360, maar in plaats daarvan wraps rond (als een CSS <hoek> type komt), het zou moeten werken zonder problemen. Denk 400deg=40deg, 480deg=120deg, enz.

Gezien dit, kunnen wij u een speciale klasse voor onze secundaire-gekleurde elementen die voegt 60 aan de tint waarde. Sinds self-modifying variabelen zijn niet beschikbaar in CSS (dat wil zeggen, er is niet zoiets als –tint: calc(var(–hue) + 60) ), kunnen we een nieuwe aux-variabele voor onze tint manipulatie aan de basis van de stijl en het gebruik van het op de achtergrond en rand verklaring.

.btn {
/* (…) */
–h: var(–tint);
achtergrond: de hsl(var(–h), calc(var(–za) * 1%), calc(var(–licht) * 1%));
grens:.1em solide hsla(var(–h), calc(var(–za) * 1%), var(–grens-licht), var(–grens-alpha));
}

Vervolgens opnieuw te verklaren dat de waarde in onze modifier:

.btn–secundaire {
–h: calc(var(–hue) + 60);
}

Het beste ding over deze aanpak is dat er automatisch aan te passen al onze berekeningen om de nieuwe tint en breng ze naar de eigenschappen, want van CSS aangepaste eigenschappen scoping.

En daar staan we dan. Putting het allemaal samen, hier is mijn pure CSS oplossing voor alle drie de uitdagingen. Dit zou moeten werken als een charme, en verlos ons van het met inbegrip van een externe JavaScript-bibliotheek.

Zie de Pen CSS-Automatische uitschakeling van het lettertype, de kleur is afhankelijk van element achtergrond…. MISLUKKEN door Facundo Corradini (@facundocorradini) op CodePen.

Behalve dat het niet. Enkele tinten echt problematisch (vooral geel en cyans), zoals ze zijn weergegeven manier helderder dan andere (bijvoorbeeld rood en blauw) ondanks het feit dat het dezelfde lichtheid waarde. In gevolg, sommige kleuren worden behandeld als donker en gegeven witte tekst ondanks dat het zeer helder.

Wat in de naam van de CSS aan de hand is?

De invoering van de waargenomen helderheid

Ik ben er zeker van dat velen van jullie misschien hebben gemerkt, is het weg, maar voor de rest van ons, blijkt de lichtheid die we waarnemen is niet hetzelfde als de HSL lichtheid. Gelukkig hebben we een aantal methoden om de wegen in de kleurtoon, de lichtheid en het aanpassen van onze code, zodat deze reageert op tint.

Om dat te doen, moeten we rekening houden met de waargenomen helderheid van de drie primaire kleuren geven elk een coëfficiënt aan hoe licht of hoe donker het menselijk oog waarneemt. Dit wordt gewoonlijk aangeduid als luma.

Er zijn verschillende methoden om dit te bereiken. Sommige zijn beter dan anderen in specifieke gevallen, maar niet 100% perfect is. Dus heb ik gekozen voor de twee meest populaire, die zijn goed genoeg:

  • sRGB Luma (ITU Rec. 709): L = (rood * 0.2126 + groen * 0.7152 + blauw * 0.0722) / 255
  • W3C methode (werkexemplaar): L = (rood * 0.299 + groen * 0.587 + blauw * 0.114) / 255

De uitvoering van luma-gecorrigeerde berekeningen

De eerste voor de hand liggende implicatie uit te gaan met een luma-gecorrigeerd aanpak is dat we geen gebruik maken van de HSL, sinds CSS niet native methoden om toegang te krijgen tot de RGB-waarden van een HSL-verklaring.

Wij moeten dus overschakelen naar een RBG verklaring voor de achtergronden, het berekenen van de luma uit welke methode we kiezen voor, en gebruiken op onze voorgrondkleur verklaring, die kan (en zal) nog steeds worden de HSL.

:root {
/* thema kleur te gebruiken variabelen in de RGB-declaraties */
–rood: 200;
–groen: 60;
–blauw: 255;
/* de drempel waarop kleuren worden beschouwd als “licht”.
Bereik: decimalen tussen 0 en 1, aanbevolen 0.5 – 0.6 */
–drempelwaarde: 0.5;
/* de drempel waarbij een donkere rand zal worden toegepast.
Bereik: decimalen tussen 0 en 1, aanbevolen 0.8+ */
–grens-drempel: 0.8;
}

.btn {
/* hiermee stelt u de achtergrond voor de base-class */
achtergrond: rgb(var(–rood), var(–groen), var(–blauw));

/* berekent de waargenomen helderheid met behulp van de sRGB-Luma-methode
Luma = (rood * 0.2126 + groen * 0.7152 + blauw * 0.0722) / 255 */
–r: calc(var(–rood) * 0.2126);
–g: calc(var(–groen) * 0.7152);
–b: calc(var(–blauw) * 0.0722);
–som: calc(var(–r) + var(–g) + var(–b));
–gezien-helderheid: calc(var(–som) / 255);

/* toont een zwarte of witte kleur, afhankelijk van de waargenomen duisternis */
kleur: hsl(0, 0%, calc (((var–waargenomen-lightness) – var(–drempel)) * -10000000%));
}

Voor de conditionele grenzen, moeten we de verklaring in een RGBA, en nogmaals, het alpha-kanaal te maken ofwel volledig transparant of volledig ondoorzichtig. Eigenlijk hetzelfde als voorheen, alleen draait RGBA in plaats van HSLA. De donkere schaduw wordt verkregen door het halveren van elk kleurkanaal.

.btn {
/* (…) */
/* geldt een donkere rand als u de helderheid hoger is dan de grens drempel */
–grens-alpha: calc (((var–waargenomen-lightness) – var(–grens-drempel)) * 100);
grens: .2em solide rgba(calc(var(–rood) * 0.5), calc(var(–groen) * 0.5), calc(var(–blauw) * 0.5), var(–grens-alpha));
}

Sinds we verloren onze eerste HSL-achtergrond verklaring, onze tweede thema kleur moet worden verkregen via hue rotatie:

.btn–secundaire {
filter: hue-draaien(60deg);
}

Dit is niet de beste in de wereld. Naast het toepassen van de hue rotatie naar mogelijke onderliggende elementen, zoals eerder besproken, betekent dit dat het overschakelen naar zwart/wit en de grens van zichtbaarheid op het secundaire element zal afhangen van het belangrijkste element is de tint en niet op zijn eigen. Maar voor zover ik kan zien, de JavaScript-uitvoering heeft hetzelfde probleem, dus ik noem het dicht genoeg in de buurt.

En daar hebben we het, deze keer voor goed.

Zie de Pen CSS Automatische WCAG contrast font-kleur afhankelijk van element achtergrond van Facundo Corradini (@facundocorradini) op CodePen.

Een pure CSS-oplossing die bereikt hetzelfde effect als de originele JavaScript aanpak, maar aanzienlijk bezuinigingen op de voetafdruk.

Browser ondersteuning

IE is uitgesloten als gevolg van het gebruik van CSS variabelen. Rand niet hebben dat het afdekken gedrag dat we gebruikt. Het ziet de verklaring, acht het onzin, en legt het helemaal zoals het zou een gebroken/onbekend regel. Elke andere grote browser moeten werken.