Lekcja 37

Tryby kolorów, zmienne motywu i dark mode

Dark mode nie polega na odwróceniu kolorów. To system tokenów, który pozwala zdefiniować jasny i ciemny motyw, zachować kontrast, dopasować natywne kontrolki przeglądarki i respektować preferencje użytkownika.

Cele lekcji

Po ukończeniu tej lekcji będziesz umieć:

  • wyjaśnić rolę właściwości color-scheme,
  • użyć @media (prefers-color-scheme: dark),
  • zbudować jasny i ciemny motyw przez semantyczne custom properties,
  • rozróżnić preferencję systemową od ręcznego wyboru użytkownika,
  • projektować komponenty oparte o lokalne tokeny motywu,
  • sprawdzić kontrast, fokus i stany interakcji w obu motywach.

Kontekst praktyczny

Motyw kolorystyczny dotyka całej aplikacji: tła, tekstu, obramowań, cieni, formularzy, linków, przycisków, alertów i stanów focus. Jeśli kolory są wpisane bezpośrednio w dziesiątkach komponentów, dark mode szybko staje się kosztowny i niespójny.

W praktyce: zamiast tokenów typu --blue-600 używaj tokenów semantycznych: --color-bg, --color-surface, --color-text, --color-border, --color-accent. Komponent nie musi wtedy wiedzieć, czy działa w jasnym czy ciemnym motywie.

color-scheme

color-scheme informuje przeglądarkę, jakie tryby kolorów strona obsługuje. Dzięki temu elementy kontrolowane przez przeglądarkę, na przykład formularze, scrollbary i część natywnych kontrolek, mogą dopasować się do wybranego schematu.

:root {
  color-scheme: light dark;
}

/* Opcjonalnie w HTML:
<meta name="color-scheme" content="light dark">
*/

Ta deklaracja nie tworzy sama ciemnego motywu. Mówi tylko, że strona umie działać w obu schematach. Kolory interfejsu nadal definiujesz w CSS.

prefers-color-scheme

Media feature prefers-color-scheme pozwala reagować na preferencję użytkownika ustawioną w systemie lub przeglądarce. To dobry punkt startowy dla automatycznego dark mode.

:root {
  --color-bg: #f8fafc;
  --color-text: #0f172a;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0f172a;
    --color-text: #e5e7eb;
  }
}

To rozwiązanie respektuje system użytkownika, ale nie daje jeszcze ręcznego przełącznika. Jeśli produkt ma przełącznik motywu, potrzebujesz dodatkowej warstwy sterowania.

Tokeny semantyczne

Najważniejsza decyzja architektoniczna to oddzielenie tokenów bazowych od semantycznych. Komponent powinien używać tokenów mówiących o roli koloru, a nie o konkretnej wartości.

:root {
  --color-bg: #f8fafc;
  --color-surface: #ffffff;
  --color-text: #0f172a;
  --color-muted: #64748b;
  --color-border: #dbe3ef;
  --color-accent: #2563eb;
}

[data-theme="dark"] {
  --color-bg: #10151f;
  --color-surface: #172033;
  --color-text: #f8fafc;
  --color-muted: #a8b3c7;
  --color-border: #2d3a50;
  --color-accent: #7dd3fc;
}

Dzięki temu karta, formularz i przycisk nie muszą mieć osobnych zestawów kolorów dla każdego motywu. Wystarczy, że używają tych samych nazw tokenów.

Ręczne przełączanie

Preferencja systemowa jest dobra jako domyślna, ale wielu użytkowników oczekuje ręcznego wyboru. Najprostszy model to atrybut data-theme na elemencie html albo body.

<html lang="pl" data-theme="dark">

[data-theme="light"] {
  color-scheme: light;
}

[data-theme="dark"] {
  color-scheme: dark;
}

JavaScript może zapisać wybór w localStorage, ale CSS powinien być tak zaprojektowany, aby zmiana atrybutu wystarczyła do przełączenia całej palety.

Komponenty i lokalne tokeny

Komponent może mieć własne lokalne tokeny, które mapują globalny motyw na konkretną rolę komponentu. To szczególnie przydatne dla alertów, kart premium i stanów statusu.

.callout {
  --callout-bg: var(--color-surface);
  --callout-text: var(--color-text);
  --callout-border: var(--color-border);

  background: var(--callout-bg);
  color: var(--callout-text);
  border: 1px solid var(--callout-border);
}

Lokalne tokeny pozwalają zrobić wariant komponentu bez kopiowania całego zestawu reguł. Wariant zmienia tylko mapowanie wartości.

Kontrast i dostępność

Dark mode musi być czytelny. Zbyt jasny tekst na bardzo ciemnym tle może męczyć wzrok, a zbyt niskie kontrasty niszczą dostępność. Testuj tekst, linki, obramowania, fokus, disabled state i komunikaty błędów w obu motywach.

.button:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 3px;
}

[data-theme="dark"] {
  --color-focus: #fbbf24;
}

Nie zakładaj, że kolor fokusu z jasnego motywu będzie widoczny na ciemnym tle. Fokus i stany interakcji powinny mieć własne tokeny.

Demo dark mode

Demo pokazuje ten sam komponent dashboardu w jasnym i ciemnym motywie. Struktura HTML pozostaje taka sama, a zmieniają się tylko tokeny przypisane do powierzchni, tekstu, obramowania i akcentu.

Light

Panel kursu

Jasny motyw używa spokojnego tła, ciemnego tekstu i niebieskiego akcentu.

37/42 lekcje gotowe
Zobacz zadanie
Dark

Panel kursu

Ciemny motyw zachowuje strukturę, ale używa innych tokenów dla czytelności i kontrastu.

37/42 lekcje gotowe
Zobacz zadanie

Najczęstsze błędy początkujących

  • Tworzenie dark mode przez proste odwrócenie kolorów zamiast zaprojektowania palety.
  • Używanie nazw tokenów typu --white i --black w komponentach.
  • Brak color-scheme, przez co natywne kontrolki nie pasują do motywu.
  • Ignorowanie ręcznego wyboru użytkownika i poleganie wyłącznie na preferencji systemowej.
  • Testowanie tylko tła i tekstu, bez stanów hover, focus, disabled i error.
  • Używanie cieni z jasnego motywu w ciemnym motywie bez korekty przezroczystości.

Mini zadanie

Zbuduj mały system motywu dla karty informacyjnej.

  • Zdefiniuj w :root tokeny --color-bg, --color-surface, --color-text, --color-muted, --color-border i --color-accent.
  • Dodaj wariant [data-theme="dark"] z ciemną paletą.
  • Ustaw color-scheme: light dark dla bazowego dokumentu.
  • Zbuduj komponent .info-card, który korzysta tylko z tokenów semantycznych.
  • Dodaj osobny token dla fokusu i sprawdź go w obu motywach.
  • Przetestuj kartę w wariantach data-theme="light" i data-theme="dark" bez zmieniania klas komponentu.

Checklista

  • Rozumiem, że color-scheme nie zastępuje własnych tokenów kolorów.
  • Potrafię użyć prefers-color-scheme jako domyślnej reakcji na preferencję systemową.
  • Projektuję komponenty na tokenach semantycznych, nie na stałych kolorach.
  • Wiem, jak ręcznie nadpisać motyw przez data-theme.
  • Sprawdzam kontrast tekstu, linków, fokusu i stanów interakcji w obu motywach.
  • Nie zakładam, że cienie, obramowania i błędy z jasnego motywu zadziałają w dark mode.

Podsumowanie lekcji

Profesjonalny dark mode zaczyna się od semantycznych tokenów. prefers-color-scheme pomaga respektować preferencje systemu, data-theme pozwala dodać ręczny wybór, a color-scheme synchronizuje natywne elementy przeglądarki z motywem strony.

Kolejny krok roadmapy to pierwszy praktyczny projekt końcowy: responsywna karta profilu.

Przejdź do Lekcji 38