← Wszystkie wpisy

Nauka o kolorze dla projektantów interfejsów: czego nauczyłem się, budując stronę bez kolorów

Wytyczne W3C dotyczące dostępności treści internetowych (WCAG) wymagają minimalnego współczynnika kontrastu 4,5:1 dla zwykłego tekstu, jednak badanie WebAIM z 2023 roku wykazało, że 83,6% spośród miliona najpopularniejszych stron internetowych miało wykrywalne błędy kontrastu WCAG 2 na swoich stronach głównych. Stronę blakecrosley.com zbudowałem z odwrotnym problemem: maksymalny kontrast (21:1) wszędzie, a następnie selektywne jego obniżanie.1

TL;DR

Zaprojektowałem swoją osobistą stronę bez żadnych kolorów marki. Cała hierarchia wizualna opiera się na białym tekście na absolutnej czerni (#000000) w czterech warstwach przezroczystości: 100%, 65%, 40% i 10%. Ta decyzja zmusiła mnie do zgłębienia perceptualnej nauki o kolorze — dlaczego sRGB kłamie na temat równomiernego rozkładu, jak OKLCH rozwiązuje ten problem i dlaczego tryb ciemny wymaga innych relacji kontrastowych niż tryb jasny. Interaktywne narzędzia poniżej pozwalają eksplorować współczynniki kontrastu i różnice między przestrzeniami kolorów. Wniosek: zrozumienie nauki stojącej za percepcją koloru prowadzi do lepszych decyzji projektowych niż estetyczna intuicja.



Moja nie-paleta kolorów

Większość systemów projektowych zaczyna od palety kolorów. Mój zaczyna od jej braku:

:root {
  --color-bg-dark:        #000000;
  --color-bg-elevated:    #111111;
  --color-bg-surface:     #1a1a1a;
  --color-text-primary:   #ffffff;
  --color-text-secondary: rgba(255,255,255,0.65);
  --color-text-tertiary:  rgba(255,255,255,0.4);
  --color-border:         rgba(255,255,255,0.1);
  --color-border-subtle:  rgba(255,255,255,0.05);
  --color-accent:         #ffffff;
}

Dziesięć tokenów. Żadnych kolorów marki. Żadnej semantycznej palety błędów/sukcesu/ostrzeżeń. Cała hierarchia wizualna działa poprzez cztery warstwy przezroczystości.2

Dlaczego cztery warstwy wystarczają

Każda warstwa pełni określoną funkcję komunikacyjną:

Warstwa Przezroczystość Token CSS Funkcja Współczynnik WCAG (na #000)
Podstawowa 100% --color-text-primary Nagłówki, tekst główny, treść krytyczna 21:1 (AAA)
Drugorzędna 65% --color-text-secondary Podtytuły, nawigacja, metadane 13,7:1 (AAA)
Trzeciorzędna 40% --color-text-tertiary Znaczniki czasu, tekst pomocniczy, stany nieaktywne 8,4:1 (AAA)
Strukturalna 10% --color-border Obramowania, separatory, rozdzielanie tła N/D (nie dotyczy tekstu)

Każda warstwa spełnia standard WCAG AAA (7:1) dla kontrastu tekstu. Warstwa trzeciorzędna przy 40% przezroczystości daje współczynnik 8,4:1 — niemal dwukrotność minimum AA wynoszącego 4,5:1. Brutalistyczny wybór absolutnej czerni (#000000 zamiast #0a0a0a czy #1a1a1a) daje każdej warstwie tekstowej maksymalny zapas kontrastowy.3

Incydent z --spacing-2xs

Odkryłem wartość ścisłych tokenów projektowych, gdy użyłem --spacing-2xs jako marginesu podtytułu. Token nie istniał w mojej definicji :root. CSS po cichu zawiódł, układ się rozjechał i spędziłem 20 minut na debugowaniu problemu z odstępami, który powinien być błędem czasu kompilacji. Rozwiązanie: zmiana na --spacing-xs (najmniejszy zdefiniowany token). Lekcja: jeśli wartość nie istnieje w systemie, to projekt jest błędny, nie system.4


Dlaczego sRGB kłamie projektantom

Problem perceptualnej niejednorodności

sRGB (standardowa przestrzeń kolorów dla internetu) mapuje kolory na sześcian, w którym każda oś (czerwona, zielona, niebieska) mieści się w zakresie 0-255. Przesunięcie o 50 jednostek w kanale czerwonym nie daje takiej samej postrzeganej zmiany jak przesunięcie o 50 jednostek w kanale zielonym. Ludzkie oczy zawierają więcej czopków wrażliwych na zieleń niż na czerwień czy błękit, przez co przesunięcia w zieleni są bardziej zauważalne niż równoważne przesunięcia w czerwieni.5

Praktyczna konsekwencja: projektant, który tworzy paletę kolorów przez równomierne rozmieszczenie wartości hex, uzyskuje kolory wyglądające na nierównomiernie rozłożone. „Średnia” szarość między #000000 a #FFFFFF to nie #808080 (matematyczny punkt środkowy), lecz w przybliżeniu #777777 (perceptualny punkt środkowy), ponieważ ludzka percepcja jasności podlega prawu potęgowemu, a nie funkcji liniowej.6

Moja strona całkowicie omija ten problem. Używając wyłącznie bieli o różnej przezroczystości, unikam pułapki niejednorodności sRGB. Przezroczystość skaluje się liniowo z postrzeganą transparentnością na czarnym tle — właściwość, której mieszanie kolorów sRGB nie posiada.

Rozwiązanie OKLCH

OKLCH (jasność Oklab, chromatyczność, odcień) to perceptualnie jednorodna przestrzeń kolorów, w której równe odległości matematyczne odpowiadają równym postrzeganym różnicom. Krok jasności o 10 jednostek zawsze wygląda jak taka sama zmiana, niezależnie od koloru wyjściowego.7

/* sRGB: mathematically even, perceptually uneven */
--gray-100: #f5f5f5;
--gray-200: #e5e5e5;
--gray-300: #d4d4d4;

/* OKLCH: perceptually even steps */
--gray-100: oklch(96% 0 0);
--gray-200: oklch(88% 0 0);
--gray-300: oklch(80% 0 0);

Nowoczesne CSS obsługuje oklch() natywnie. W moim następnym projekcie wymagającym palety kolorów zdefiniuję ją w OKLCH. Dla mojej obecnej strony system oparty na przezroczystości osiąga tę samą perceptualną jednorodność innymi środkami.


Moja decyzja o trybie ciemnym: brak trybu jasnego

Moja strona nie ma zapytania medialnego prefers-color-scheme. Działa wyłącznie w trybie ciemnym. Decyzja była celowa.8

Argument za dwoma trybami: Użytkownicy oczekują opcji trybu jasnego. Integracja z preferencjami systemowymi respektuje wybór użytkownika.

Argument przeciw (który wybrałem): Utrzymywanie dwóch systemów wizualnych nieuchronnie prowadzi do kompromisów w obu. Projekt, który działa przy 65% przezroczystości na czarnym tle, wymaga innej przezroczystości na białym (bliżej 45%), aby osiągnąć tę samą postrzeganą wagę wizualną. Każdy stan interakcji, każde obramowanie, każdy cień wymaga ponownej kalibracji. Wybrałem projektowanie jednego systemu dobrze, zamiast dwóch systemów przeciętnie.

Absolutnie czarne tło (#000000) maksymalizuje kontrast dostępny dla każdej warstwy tekstu:

/* My actual typography contrast hierarchy */
.hero__title     { color: var(--color-text-primary); }   /* 21:1 */
.hero__subtitle  { color: var(--color-text-secondary); }  /* 13.7:1 */
.nav a           { color: var(--color-text-secondary); }  /* 13.7:1 */
.nav a:hover     { color: var(--color-text-primary); }    /* 21:1 */

Przejście stanu hover (drugorzędna → podstawowa) zapewnia funkcjonalną informację zwrotną wyłącznie poprzez zmianę kontrastu — bez potrzeby zmiany koloru.


Kontrast i czytelność

Wymagania kontrastu WCAG

Poziom Zwykły tekst (< 18pt) Duży tekst (≥ 18pt lub 14pt pogrubiony)
AA 4,5:1 3:1
AAA 7:1 4,5:1

Współczynnik kontrastu mierzy względną różnicę luminancji między kolorem pierwszego planu a kolorem tła. Współczynnik 1:1 oznacza brak kontrastu (identyczne kolory). Współczynnik 21:1 oznacza maksymalny kontrast (czarny na białym lub biały na czarnym).9

Poza WCAG: APCA

Algorytm kontrastu WCAG 2 ma znane ograniczenia. Traktuje wszystkie kolory jednakowo niezależnie od polaryzacji (ciemny na jasnym vs. jasny na ciemnym), pomimo badań wykazujących, że ludzka percepcja kontrastu znacząco różni się między tymi dwoma trybami.10

APCA (Accessible Perceptual Contrast Algorithm) uwzględnia te ograniczenia, biorąc pod uwagę: - Wrażliwość na polaryzację: Ciemny tekst na jasnym tle jest bardziej czytelny niż jasny tekst na ciemnym tle przy tym samym matematycznym współczynniku kontrastu - Częstotliwość przestrzenną: Cienkie kroje pisma wymagają większego kontrastu niż pogrubione kroje przy tym samym rozmiarze - Adaptację: Oko adaptuje się do luminancji otaczającej strony, co wpływa na postrzegany kontrast

APCA ma stanowić podstawę wymagań kontrastowych WCAG 3.0. Moja strona korzysta ze spostrzeżenia dotyczącego polaryzacji: ponieważ używam wyłącznie jasnego tekstu na ciemnym tle, potrzebuję wyższych współczynników kontrastu niż strona w trybie jasnym. Moja najniższa warstwa tekstu (40% przezroczystości, współczynnik 8,4:1) przekracza nawet zalecane minimum APCA dla tekstu głównego na ciemnych tłach.


Kolor semantyczny bez koloru

Produkcyjne systemy kolorów zazwyczaj przypisują kolory do funkcji (zielony dla sukcesu, czerwony dla błędu). Moja strona całkowicie unika funkcjonalnego koloru, ponieważ nie posiada transakcyjnego UI — żadnych formularzy, żadnych komunikatów statusu, żadnych stanów sukcesu/błędu. Treść jest statyczna.

Gdybym potrzebował koloru semantycznego, dodałbym go chirurgicznie:

Funkcja Typowe podejście Moje hipotetyczne podejście
Sukces Zielony Biały tekst + ikona znacznika ✓
Błąd Czerwony Biały tekst + ikona X + podkreślenie obramowaniem
Ostrzeżenie Bursztynowy Biały tekst + ikona wykrzyknika

Łączenie ikon z tekstem eliminuje komunikację wyłącznie poprzez kolor, która zawodzi u około 8% mężczyzn z zaburzeniami widzenia barw. Podejście to również zachowuje mój monochromatyczny system. Kolor pełniłby rolę akcentu, nie elementu strukturalnego.11


Hierarchia oparta na typografii

Gdy żaden kolor nie przenosi hierarchii, na mojej stronie wszystko przenosi typografia:

:root {
  --font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text",
                 "SF Pro Display", "Helvetica Neue", Arial, sans-serif;
  --font-size-display: 5rem;     /* 80px — hero headlines */
  --font-size-7xl:     3.875rem; /* 62px */
  --font-size-base:    1rem;     /* 16px — body text */
  --font-size-xs:      0.75rem;  /* 12px — metadata */
}

Fonty systemowe, nie własne fonty webowe. Wybór jest zarówno decyzją brutalistyczną (użycie natywnego materiału platformy), jak i decyzją wydajnościową (zero opóźnień ładowania fontów, co przyczynia się do wyników 100/100 w Lighthouse). Rozmiar display (80px) z ciasnym rozstawem liter (-0.03em) nadaje nagłówkom powagę bez dekoracji. Tekst główny o rozmiarze 16px z hojną wysokością linii (1.7) priorytetyzuje czytelność nad gęstością.12

13-stopniowa skala typograficzna od 0,75rem do 5rem zapewnia wystarczającą szczegółowość, aby wyrażać hierarchię wyłącznie poprzez rozmiar. W połączeniu z czterema warstwami przezroczystości mam 52 potencjalne kombinacje (13 rozmiarów × 4 przezroczystości) — więcej niż wystarczająco, aby wyrazić dowolną hierarchię treści bez sięgania po kolor.


Kluczowe wnioski

Dla projektantów: - Definiowanie palet kolorów w OKLCH zamiast sRGB; perceptualnie jednorodne przestrzenie kolorów dają przewidywalną hierarchię i spójne współczynniki kontrastu w całej palecie - Testowanie trzeciorzędnej warstwy tekstu względem WCAG AAA (7:1), a nie tylko AA (4,5:1); próg AAA zapewnia wystarczający zapas na rzeczywiste warunki ekranowe (niska jasność, odblaski, starzejące się wyświetlacze) - Rozważenie, czy projekt faktycznie potrzebuje koloru; moja strona dowodzi, że typografia i przezroczystość same mogą nieść kompletną hierarchię wizualną

Dla programistów: - Użycie CSS oklch() do definiowania kolorów i testowanie współczynników kontrastu zarówno w WCAG 2 (obecny standard), jak i APCA (nadchodzący standard); wsparcie przeglądarek dla oklch() jest dostępne we wszystkich nowoczesnych przeglądarkach - Implementacja trybu ciemnego przez dostosowanie jasności i nasycenia w przestrzeni OKLCH zamiast odwracania wartości hex; perceptualne dostosowanie daje lepsze wyniki niż matematyczne odwrócenie - Ścisłe egzekwowanie tokenów projektowych zapobiega cichym awariom CSS; jeśli token nie istnieje, to projekt powinien się zmienić, nie system tokenów


Źródła


  1. WebAIM, “The WebAIM Million: 2023 Accessibility Analysis,” 2023. 

  2. Author’s CSS custom properties from critical.css. 10 color tokens, all derived from white-on-black opacity relationships. 

  3. Author’s WCAG contrast calculations. Primary (21:1), secondary (13.7:1), tertiary (8.4:1), all exceeding AAA 7:1 minimum. 

  4. Author’s debugging experience. --spacing-2xs was used but never defined in :root. Documented in MEMORY.md error entries. 

  5. Hunt, R.W.G., The Reproduction of Colour, Wiley, 2004. 

  6. Poynton, Charles, Digital Video and HD, Morgan Kaufmann, 2012. Gamma correction and perceptual linearity. 

  7. Ottosson, Bjorn, “A perceptual color space for image processing,” 2020. OKLCH specification. 

  8. Author’s design decision. Single dark mode avoids visual compromise inherent in maintaining parallel light/dark systems. 

  9. W3C, “Web Content Accessibility Guidelines (WCAG) 2.1,” 2018. 

  10. Somers, Andrew, “APCA Contrast Calculator,” 2023. 

  11. W3C, “WCAG 2.1 Success Criterion 1.4.1: Use of Color,” 2018. 

  12. Author’s typography system. 13-step font scale from 0.75rem (12px) to 5rem (80px). System font stack eliminates FOIT/FOUT.