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
-
WebAIM, “The WebAIM Million: 2023 Accessibility Analysis,” 2023. ↩
-
Author’s CSS custom properties from
critical.css. 10 color tokens, all derived from white-on-black opacity relationships. ↩ -
Author’s WCAG contrast calculations. Primary (21:1), secondary (13.7:1), tertiary (8.4:1), all exceeding AAA 7:1 minimum. ↩
-
Author’s debugging experience.
--spacing-2xswas used but never defined in:root. Documented in MEMORY.md error entries. ↩ -
Hunt, R.W.G., The Reproduction of Colour, Wiley, 2004. ↩
-
Poynton, Charles, Digital Video and HD, Morgan Kaufmann, 2012. Gamma correction and perceptual linearity. ↩
-
Ottosson, Bjorn, “A perceptual color space for image processing,” 2020. OKLCH specification. ↩
-
Author’s design decision. Single dark mode avoids visual compromise inherent in maintaining parallel light/dark systems. ↩
-
W3C, “Web Content Accessibility Guidelines (WCAG) 2.1,” 2018. ↩
-
Somers, Andrew, “APCA Contrast Calculator,” 2023. ↩
-
W3C, “WCAG 2.1 Success Criterion 1.4.1: Use of Color,” 2018. ↩
-
Author’s typography system. 13-step font scale from 0.75rem (12px) to 5rem (80px). System font stack eliminates FOIT/FOUT. ↩