Jasne i ciemne motywy dla poszczególnych komponentów

Funkcje internetowe: color-scheme light-dark()

Właściwość color-scheme jest częściej ustawiana w elemencie głównym, ale można ją ustawić w poszczególnych elementach, aby wymusić inny schemat kolorów niż w pozostałej części strony. Może to być przydatne w przypadku komponentów, które muszą być zawsze wyświetlane w określonym schemacie kolorów (np. zawsze w trybie ciemnym lub jasnym).

Przykładowe przypadki użycia:

  • elementy, które ze względów estetycznych często są wyświetlane w trybie ciemnym nawet na stronach w trybie jasnym, np. bloki kodu, odtwarzacze multimedialne, galerie zdjęć;
  • Obszary zawierające multimedia przeznaczone do wyświetlania na jasnym tle (np. obrazy, filmy, ilustracje, podglądy wydruku) mogą być ustawione na tryb jasny, nawet jeśli reszta strony jest w trybie ciemnym.
  • elementy, których schemat kolorów jest kontrolowany przez ustawienie na poziomie użytkownika, takie jak podglądy komponentów;
  • Elementy umieszczone, które nie obsługują trybu jasnego i ciemnego
  • Narzędzia do projektowania, mapy, wizualizacje, gry itp.

Kiedy zmieniać kolory, a kiedy wymuszać color-scheme?

Nie każdy element, który w trybie jasnym używa jaśniejszego tekstu na ciemniejszym tle lub w trybie ciemnym ciemniejszego tekstu na jaśniejszym tle, wymaga innego elementu color-scheme. Na przykład przycisk główny może być renderowany jako niebieski z białym tekstem w trybie jasnym, ale nie gwarantuje to, że będzie on miał kolor color-scheme: dark.

Zasadniczo elementy o innym color-scheme to zwykle złożone powierzchnie, które tworzą własny kontekst wizualny, a nie proste, płytkie kontenery.

Zanim zdecydujesz się na użycie innego elementu color-scheme, zadaj sobie te pytania:

  • Czy wbudowany interfejs przeglądarki, który nie jest dostosowany w żaden inny sposób (np.elementy sterujące formularza, paski przewijania itp.), powinien używać tego schematu kolorów czy dostosowywać się do schematu kolorów strony? -> jeśli to pierwsze, nie używaj color-scheme.
  • Czy kolory light-dark() powinny być takie same jak na pozostałej części strony czy powinny być oparte na zastąpieniu? -> if the former, don't use color-scheme.
  • Czy potomkowie powinni być w tym color-scheme? Jeśli nie, nie używaj color-scheme.

Implementacja podstawowa

Zastąpienia dotyczące konkretnych komponentów są zwykle (choć nie zawsze) używane na stronach, które obsługują też wiele schematów kolorów za pomocą globalnego elementu color-scheme. Więcej informacji o prawidłowym wdrażaniu trybu ciemnego na całej stronie znajdziesz w artykule Tryb ciemny.

Gdy na stronie jest już ustawiony atrybut color-scheme i używasz tokenów kolorów, które są na niego wrażliwe (np. za pomocą light-dark()), możesz po prostu ustawić atrybut color-scheme w przypadku konkretnych komponentów, aby zastąpić tryb kolorów dla tego poddrzewa:

pre, code, .dark {
  color-scheme: dark;
}

Pamiętaj, że niektóre przeglądarki i tak automatycznie dostosowują komponenty do innego schematu kolorów. Aby wymusić określony schemat kolorów we wszystkich przypadkach, użyj only, czyli color-scheme: only dark; zamiast color-scheme: dark;.

Dostosowywanie wartości innych niż kolor

Funkcja light-dark() działa obecnie tylko w przypadku kolorów.

Sprawdzone metody

  • OBOWIĄZKOWE: nie ustawiaj color-scheme na elementach bez tła, ponieważ może to spowodować wymieszanie par kolorów tła i tekstu z różnych schematów kolorów, co może sprawić, że tekst będzie nieczytelny.
  • OPCJONALNIE: łatwiej jest używać tych samych par kolorów co w trybie ciemnym na całej stronie, ale możemy zdefiniować różne pary kolorów dla tych komponentów. Na przykład możemy chcieć, aby komponent trybu ciemnego używany na stronie w trybie jasnym był nieco mniej ciemny niż ten sam komponent trybu ciemnego używany na stronie, która jest ogólnie w trybie ciemnym.

Znane problemy, o których warto wiedzieć

Ważna uwaga: dziedziczenie kolorów light-dark()

light-dark() jest rozwiązywany w czasie obliczania wartości. Oznacza to, że wszystkie odziedziczone właściwości <color> ustawione na kolor light-dark() będą przekazywać elementom podrzędnym tylko jeden z dwóch kolorów, a nie samo wyrażenie light-dark().

Obejmuje to m.in.:

  • Wbudowane właściwości kolorów, które są dziedziczone, takie jak color, accent-color, fill, stroke, text-shadow, caret-color
  • Wszystkie zarejestrowane dziedziczone właściwości niestandardowe z syntax: <color> i inherits: true
  • Dowolna inna właściwość <color> ustawiona na inherit

Oznacza to, że trzeba przestrzegać pewnych zaleceń.

  • NIE rejestruj właściwości niestandardowych, które mają zawierać tokeny projektowe (np. --surface-color) jako <color>. Tokeny muszą zachować wyrażenie light-dark(), aby elementy podrzędne mogły je ponownie rozwiązać w innym elemencie color-scheme.
  • Podczas ustawiania color-scheme w elemencie ponownie określ wszystkie odziedziczone właściwości <color>, które mogły zostać ustawione na wartości light-dark() (bezpośrednio lub za pomocą tokenów projektowych), nawet jeśli jest to ten sam token projektowy.
  • NIE używaj właściwości inherit w przypadku właściwości <color> w elementach z zastąpieniem color-scheme (można ich używać w elementach podrzędnych).
  • UŻYWAJ zarejestrowanych właściwości <color> w odwrotnym przypadku: gdy chcesz celowo zapisać stan koloru elementu nadrzędnego i zapobiec jego ponownemu określeniu w elemencie podrzędnym color-scheme. Możesz na przykład przechwycić tło strony, aby użyć go w innym miejscu.
  • Jeśli chcesz animować kolor, użyj osobnej właściwości <color> zarejestrowanej w @property w animowanym elemencie (rejestracja jest wymagana do interpolacji kolorów) – nie jest to token projektowy, ale cel animacji dla poszczególnych elementów, więc nie jest sprzeczny z powyższą regułą.

Przykład:

:root {
    --accent-color: light-dark(blue, skyblue);
    --surface-color: light-dark(white, #222);
    --text-color: light-dark(#111, white);

    color-scheme: light dark;
    accent-color: var(--accent-color);
    color: var(--text-color);
}

body {
    /* --surface-color dynamically switches despite being inherited because --surface-color is not registered */
    background: var(--surface-color);
}

pre, code {
    color-scheme: dark;
    background: var(--surface-color);

    /* Without this, accent-color would be blue, not skyblue! */
    accent-color: var(--accent-color);

    /* Without this, text-color would be #111, not white! */
    color: var(--text-color);
}

Problemy, o których warto pamiętać podczas korzystania ze schematu kolorów

  • Chrome i Firefox respektują color-scheme w przypadku elementów iframe: renderują umieszczone strony w odpowiednim schemacie kolorów i dostosowują zapytanie o media prefers-color-scheme umieszczonej strony, aby odzwierciedlić color-scheme kontekstu umieszczenia. Safari nie obsługuje tej funkcji i rozpoznaje prefers-color-scheme jako ustawienie systemowe nawet w elementach iframe.
    • Jeśli kontrolujesz zarówno element nadrzędny, jak i element iframe: przekaż schemat kolorów elementu nadrzędnego do elementu iframe – za pomocą parametru adresu URL (?theme=dark) podczas tworzenia elementu iframe lub za pomocą postMessage() (co pozwala też reagować na zmiany w czasie działania). W elemencie iframe ustaw klasę w <html> (lub color-scheme w :root) na podstawie tego sygnału, zamiast polegać na prefers-color-scheme.
    • Jeśli masz kontrolę tylko nad umieszczoną stroną: nie ma niezawodnego sposobu na wykrycie color-scheme kontekstu umieszczenia z poziomu elementu iframe w Safari. Udostępnij jawny parametr motywu w interfejsie API do umieszczania (np. ciąg zapytania lub protokół postMessage) i udokumentuj go dla osób umieszczających treści.

Strategie zastępcze

Kreacje zastępcze i obsługa schematu kolorów w przeglądarce

Właściwość color-scheme to progresywne ulepszanie. Przeglądarki, które nie obsługują tej właściwości, zignorują ją i będą używać domyślnego interfejsu w trybie jasnym.

Aby dostosować się do preferencji użytkownika w starszych przeglądarkach, użyj zapytań o multimedia prefers-color-scheme, aby wyświetlać różne kolory, gdy preferowany jest tryb ciemny.

  • Używaj zapytania o media, aby przełączać właściwości niestandardowe w :root lub html.
  • Unikaj używania zapytania o media w przypadku poszczególnych komponentów, chyba że komponent wymaga bardzo konkretnego typu dostosowania trybu ciemnego, który nie ogranicza się do kolorów.
:root {
  /* Define brand colors for each mode */
  --color-brand-light: #0056b3;
  --color-brand-dark: #00e5ff;
  --color-brand: var(--color-brand-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --color-brand: var(--color-brand-dark);
  }

  /* Ignored in older browsers */
  color-scheme: light dark;
}

button.primary {
    background-color: var(--color-brand);
}

Zastępcze i obsługa funkcji light-dark() w przeglądarkach

W przypadku przeglądarek, które obsługują color-scheme, ale nie obsługują jeszcze light-dark(), jasne i ciemne wersje kolorów należy najpierw zdefiniować jako właściwości niestandardowe, a zapytanie o media prefers-color-scheme powinno służyć do ustawiania kolorów dla odpowiedniego trybu, jak w przykładzie poniżej:

:root {
  /* Define browser UI accent color for each mode */
  --brand-accent-light: #0056b3;
  --brand-accent-dark: #00e5ff;
  --accent-color: var(--brand-accent-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --accent-color: var(--brand-accent-dark);
  }

  /* OPTIONAL: use light-dark() for more control of built-in UI colors */
  @supports (color: light-dark(white, black)) {
    --accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
  }

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;

  /* Example inherited color property */
  accent-color: var(--accent-color);
}

pre, code {
  color-scheme: dark;

  /* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
  accent-color: var(--accent-color);
}