Eén kleur, twee weergaven

Een app die er in lichte modus prima uitziet, kan in donkere modus letterlijk leeg zijn. De gebruikelijke diagnose is “vergeten te testen”. Dat is het gevolg. De oorzaak is wiskundig: op een gewone kaartachtergrond bestaat er geen enkele vaste kleur die in beide weergaven leesbaar is.

Waar dit vandaan komt. Eén melding — “deze app is onleesbaar” — werd een controle over een hele collectie zelfgebouwde iOS-apps. De uitkomst: 48 apps met een vast palet, 153 kleuren die opnieuw moesten. Niet omdat iemand slordig was, maar omdat één vaste kleur de klus principieel niet kán klaren. Deze pagina rekent dat na en geeft je het gereedschap om je eigen palet te toetsen.

1

De dubbele meter

Kies een kleur en een achtergrondpaar. Je ziet meteen beide contrastverhoudingen — niet één.

Lichte modus
€ 1.284
Verbruik deze maand

Dit is gewone tekst in dezelfde kleur, zoals hij in een kaart terechtkomt.

Donkere modus
€ 1.284
Verbruik deze maand

Dit is gewone tekst in dezelfde kleur, zoals hij in een kaart terechtkomt.

Op de lichte achtergrond
Op de donkere achtergrond
2

De dode zone

Waarom dit geen kwestie van beter kiezen is.

Om leesbaar te zijn op een lichte achtergrond moet een kleur donker genoeg zijn. Om leesbaar te zijn op een donkere achtergrond moet hij licht genoeg zijn. Dat zijn twee eisen aan hetzelfde getal, en ze wijzen tegengesteld. De vraag is dus niet welke kleur je kiest, maar of er überhaupt nog iets overblijft tussen “licht genoeg” en “donker genoeg”.

Overzicht van welke lichtheden leesbaar zijn op de lichte achtergrond, op de donkere achtergrond, en op allebei
zwartmiddengrijswit
Breedte van de overlap
Kleuren die het halen
Grijstinten die het halen

Het scherpste geval. Tegen puur wit en puur zwart bestaat er nog een uiterst smalle marge: precies #757575 en #767676 zijn de enige twee grijstinten die aan beide kanten 4,5:1 halen, en van alle 16.777.216 sRGB-kleuren redden er 292.107 het — 1,74%. Zodra je achtergronden niet meer puur zijn — en dat zijn ze bijna nooit, want een kaart is lichtgrijs en een donkere kaart is niet echt zwart — krimpt die marge naar nul. Op de iOS-standaard #F2F2F7 / #1C1C1E is er geen enkele kleur meer over.

Hoe deze grens wordt berekend

De contrastverhouding uit WCAG 2.1 is (L1 + 0,05) / (L2 + 0,05), waarbij L de relatieve luminantie is: elk kanaal eerst van sRGB naar lineair licht (c ≤ 0,04045 ? c/12,92 : ((c+0,055)/1,055)^2,4), daarna gewogen met 0,2126 rood, 0,7152 groen en 0,0722 blauw.

Bij een lichte achtergrond met luminantie Llicht volgt uit de eis van 4,5:1 een bovengrens voor de tekstkleur: L ≤ (Llicht + 0,05) / 4,5 − 0,05. Bij een donkere achtergrond Ldonker volgt een ondergrens: L ≥ 4,5 × (Ldonker + 0,05) − 0,05. Er is alleen een oplossing als de ondergrens onder de bovengrens ligt. Bij wit en zwart is dat 0,1750 tot 0,1833 — een venster van nog geen procent van de luminantieschaal. Bij #F2F2F7 en #1C1C1E wordt het 0,2277 tot 0,1591, en dat is een leeg interval.

De as in de grafiek hierboven is niet lineair in luminantie maar in de sRGB-waarde zelf, zodat de posities overeenkomen met hoe lichtheid er op je scherm uitziet.

De uitzondering die telt. Voor grote tekst en voor niet-tekstuele elementen zoals iconen, randen en grafiekvlakken is de norm 3:1 in plaats van 4,5:1. Daar is het venster wél ruim: 38,6% van alle kleuren haalt dat aan beide kanten. Dáárom werken de standaard systeemtints als accentkleur prima en gaat het pas mis zodra diezelfde tint een getal of een zin moet dragen. Zet de rol hierboven op “grote tekst” en je ziet het verschil meteen.

3

Scan je palet

Plak je kleuren, één per regel. Een naam ervoor mag: amber #F59E0B.

KleurOp lichtOp donkerOordeelAdaptief paar

4

De val na de reparatie

Elke kleur voldoet, en toch is het scherm slechter geworden.

De voor de hand liggende reparatie is: til elke kleur precies tot de drempel. Iedere meting wordt groen. Maar een palet is geen verzameling losse kleuren — het draagt een rangorde. Hoofdtekst hoort zwaarder te wegen dan bijschrift, en dat verschil zít in het contrast. Til je alles naar hetzelfde minimum, dan schuiven ze naar elkaar toe en is de rangorde weg.

Lichte modus (het origineel)
Vaste lasten
bijgewerkt vanochtend
€ 842,60
Donkere modus (na reparatie)
Vaste lasten
bijgewerkt vanochtend
€ 842,60
RolOp lichtOp donkerOordeelVariant donkere modus

5

Vier dingen die de collectie-brede reparatie opleverde

Uit een omzetting over 48 apps en 153 kleuren.

Achtergronden horen mee te bewegen, niet te contrasteren

De eerste automatische omzetting behandelde elke kleur in het palet als tekstkleur — inclusief de papier- en paneelkleur. Resultaat: in lichte modus werd de achtergrond donker gemaakt, want dat gaf immers meer contrast. Precies verkeerd om. Een achtergrondkleur hoort met de weergave mee te kantelen; alleen de kleuren die eróp getekend worden moeten contrasteren. Splits je palet dus eerst in dragers en gedragenen, vóór je iets meet.

Een scherm dat op één weergave is vastgezet, heeft geen adaptief palet nodig

Negen van de apps stonden met preferredColorScheme vast op één weergave — een videospeler, een meetscherm, een app die altijd donker is. Daar landen de kleuren altijd op dezelfde achtergrond, dus de andere weergave bestaat niet en een adaptief paar voegt niets toe behalve onderhoud. De regel geldt alleen waar de gebruiker echt kan wisselen.

Semantische systeemkleuren zijn geen tekstkleuren

Groen voor “goed”, amber voor “let op”, rood voor “fout” — die tints zijn ontworpen als accent: een stip, een vulling, een icoon. In die rol is 3:1 de norm en halen ze dat ruimschoots. De fout ontstaat op het moment dat er een getal in wordt gezet, want dan geldt plotseling 4,5:1. Van de tien Apple-systeemtints haalt er geen enkele die grens op wit; groen komt uit op 2,22:1 en geel op 1,51:1. Niet omdat de kleuren slecht zijn, maar omdat ze voor iets anders bedoeld zijn.

Een omzetscript dat stil niets vindt, ziet er precies zo uit als een script dat klaar is

Twee keer op rij meldde de reparatie “klaar” terwijl er nog van alles openstond. De eerste keer was de zoekopdracht te smal: hij vond drie apps waar er 44 waren. De tweede keer eiste het patroon dat de regel op de sluithaak eindigt, en overal waar iemand een toelichting achter de kleur had gezet matchte er niets — zonder foutmelding. Wat het ving was nameten over alles in plaats van over de bestanden die het script zei te hebben aangeraakt. Vertrouw bij een bulkwijziging nooit de teller van het gereedschap dat de wijziging maakte.