Przełączniki trybu ciemnego

Przełącznik trybu ciemnego umożliwia użytkownikom zastąpienie schematu kolorów systemu w jednej witrynie. Zastąpienie jest stosowane przez zaktualizowanie <meta name="color-scheme">: light dark oznacza „zgodnie z preferencjami systemowymi”, light lub dark przypina konkretny schemat. Kolory (light-dark()), kolory systemowe i natywny interfejs użytkownika są powiązane z właściwością CSS color-scheme, więc przełącznik działa, utrzymując tę właściwość w synchronizacji z <meta> (patrz Odzwierciedlanie zastąpienia w CSS).

Czy powinien być widoczny stały przełącznik, ustawienia czy może w ogóle nie powinno być przełącznika?

Większość witryn nie potrzebuje stałego przełącznika schematu kolorów w interfejsie (nagłówek, nawigacja, stopka itp.). Domyślnie postępuj zgodnie z preferencjami systemowymi, a jeśli konieczne jest ręczne zastąpienie, umieść element sterujący na osobnej powierzchni ustawień: na stronie ustawień, w panelu lub w nakładce.

Przypięcie schematu do systemu operacyjnego jest uzasadnione, ale rzadko stosowane. Takie funkcje powinny być dostępne w ramach stopniowego ujawniania, a nie w widocznym przez cały czas elemencie sterującym.

Trwałe przełączniki można znaleźć głównie na stronach dla deweloperów lub stronach powiązanych z deweloperami (np. dla projektantów), gdzie przełączanie schematów jest znacznie częstszym zadaniem niż w przypadku przeciętnego użytkownika internetu. NIE dodawaj go tylko dlatego, że jest to powszechny wzorzec na stronach dla deweloperów – zainteresowanie dewelopera tym elementem sterującym nie jest reprezentatywne dla typowych użytkowników.

Implementacja

Najpierw wykonaj wszystkie czynności opisane w sekcji Tryb ciemny, aby obsługiwać oba schematy, domyślnie korzystając z preferencji systemowych.

Model danych

Model danych przełącznika trybu ciemnego zwykle obejmuje 3 stany:

  1. System (brak zapisanej wartości): interfejs jest zgodny ze schematem kolorów systemu. Najlepiej, aby było to ustawienie domyślne.
  2. Jasny (zapisany jako "light"): interfejs używa jasnego motywu niezależnie od schematu kolorów systemu.
  3. Ciemny (zapisany jako "dark"): interfejs używa ciemnego motywu niezależnie od schematu kolorów systemu.

Ten stan jest zwykle przechowywany w localStorage i odzwierciedlany w elemencie <meta name="color-scheme"> w nagłówku dokumentu.

Odzwierciedlenie zastąpienia w arkuszu CSS

Atrybut <meta> to tylko wskazówka dotycząca prezentacji: każda deklaracja color-scheme w kodzie CSS autora zastępuje go. Ponieważ tryb ciemny ustawia color-scheme: light dark na :root, aktualizacja samej właściwości <meta> nie przyniesie żadnego efektu – CSS ma pierwszeństwo. Zmapuj <meta> z powrotem na właściwość, aby kaskada ją uwzględniała:

:root {
  color-scheme: light dark;

  &:has(> head > meta[name="color-scheme"][content="dark"]) {
    color-scheme: dark;
  }
  &:has(> head > meta[name="color-scheme"][content="light"]) {
    color-scheme: light;
  }
}

NIE koduj na stałe color-scheme: light ani color-scheme: dark jako domyślnego elementu głównego. Deklaracja podstawowa MUSI pozostać light dark. CSS NIE MOŻE zależeć od JS: jeśli JS nigdy nie zostanie uruchomiony, <meta> pozostanie light dark, a witryna będzie działać zgodnie z preferencjami systemowymi – nic się nie zepsuje.

Trwałość i zapobieganie FOUC

Utrzymaj zastąpienie w localStorage; usuń wpis po powrocie do domyślnego ustawienia systemowego.

Aby uniknąć wyświetlenia niewłaściwego schematu, zastosuj zapisaną wartość w skrypcie wbudowanym (NIE type=module, NIE defer) umieszczonym bezpośrednio po elemencie <meta>:

<meta name="color-scheme" content="light dark">
<script>
document.querySelector('meta[name="color-scheme"]').content = localStorage.getItem("color-scheme") ?? "light dark";
</script>
  • Ten skrypt wbudowany istnieje tylko po to, aby zapobiegać FOUC. Powinien być jak najmniejszy. Dodatkowy kod JS do obsługi przełączania schematu kolorów może zostać wczytany później.
  • W rzadkich przypadkach dostęp do localStorage może spowodować błąd (np. zablokowanie danych witryny), ale ponieważ ten skrypt nie robi nic innego, jedynym efektem ubocznym będzie błąd konsoli. Jeśli to ważne, umieść w elemencie try .. catch. Upewnij się, że motyw jest nadal stosowany na stronie, nawet jeśli nie można go zapisać.
  • Zastąpienie może się zmienić na innej karcie: obsłuż zdarzenie storage elementu window, aby zachować synchronizację.
  • NIE używaj matchMedia() do usuwania przechowywanej wartości, gdy preferencje systemowe zmienią się, aby ją dopasować. System operacyjny wielu użytkowników automatycznie przełącza schematy w zależności od pory dnia. Usunięcie zapisanej wartości, gdy te 2 schematy się pokrywają, uniemożliwiłoby przypięcie schematu.

Rozgałęzianie dla wartości HTML i wartości innych niż kolory

Kolory nie wymagają dodatkowych działań: po zmapowaniu <meta> na color-scheme (powyżej) light-dark() będzie używać zastąpienia. W tej sekcji znajdziesz informacje o dostosowywaniu innych wartości (np. font-weight, źródeł multimediów itp.).

Gdy strona po prostu dostosowuje się do preferencji systemowych, można użyć (prefers-color-scheme: dark) do rozgałęziania, w tym w źródłach <picture> lub <video>. Zastąpienie schematu kolorów powoduje dodatkowe komplikacje: preferencje systemowe nie są już źródłem informacji, a w przypadku interfejsów API, które akceptują tylko zapytania o media, nie ma bezpośredniej alternatywy.

NIE polegaj wyłącznie na klasach stosowanych przez JS, takich jak .dark, do rozgałęziania, ponieważ będą one nieprawidłowe, jeśli JS się nie załaduje, a domyślny tryb systemu operacyjnego jest ciemny.

Możesz połączyć zapytanie o media z selektorem :root:has(> head > meta[name="color-scheme"][content="dark"]) (lub light), aby utworzyć rozgałęzienie. Szczegółowe informacje o tym, jak to zrobić, znajdziesz w artykule Kombinacje selektorów i reguł @.

Opcje elementu sterującego również nie są gałęziami kolorów: uwzględnij oba stany w znacznikach (ikona i ukryty tekst działania, np. „Przełącz na ciemny motyw”) i ukryj nieaktywny stan za pomocą tych selektorów. display: none usuwa też zbędną opcję z technologii wspomagającej, dzięki czemu element sterujący nie wymaga kodu JS, aby zachować aktualność po zmianie preferencji systemowych.

W przypadku multimediów w kodzie HTML jedynym sposobem jest obecnie uwzględnienie obu wersji jako osobnych elementów i odpowiednie przełączanie widoczności. Ukrywanie elementów <source> za pomocą display: none nie działa.

Interfejs

2 czy 3 stany?

We wszystkich przypadkach interfejs użytkownika MUSI obsługiwać przywracanie lub ustawianie schematu kolorów na systemowy.

W przypadku przełączników o ograniczonej przestrzeni, takich jak przełączniki trwałe w interfejsie witryny (nagłówek, nawigacja, stopka itp.), gdzie przestrzeń jest ograniczona, a dostosowywanie ustawień jest zwykle niezależne od celu użytkownika, zalecamy wyświetlanie 2 stanów.

Sterowanie schematem kolorów na osobnym ekranie ustawień (stronie, panelu lub nakładce) to inny scenariusz, w którym MOŻNA wyraźnie wyświetlić wszystkie 3 stany („Jasny”, „Ciemny”, „Systemowy”). W takim przypadku użytkownik podejmuje już świadome decyzje dotyczące przyszłego zachowania i jest miejsce na wyjaśnienie opcji.

Trzy stany jawne są również uzasadnione, jeśli witryna implementuje schematy zależne od kontekstu (np. tryb przyciemnionego światła, gdy system operacyjny jest ciemny), ponieważ „Jasny” i „System (obecnie jasny)” naprawdę się różnią.

Przełączniki dwustanowe

Gdy używany jest taki element sterujący, zwykle służy on do tymczasowego dostosowania komfortu („teraz jest zbyt jasno”), a nie do zapewnienia długotrwałego zachowania bieżącego stanu. Dlatego w przypadku przełączników dwustanowych wystarczy wyświetlić 2 z 3 ustawień: systemowe (aby można było przywrócić ustawienie) i obecne przeciwieństwo ustawienia systemowego, przechowywane jako jego bieżący literał.

Powinny być tylko 2 stany:

  1. Ustawienie domyślne systemu – brak zapisanej wartości, treść <meta> light dark. Wyświetlana jako bieżąca wartość (np. ikona słońca, gdy jest jasno).
  2. Zastąp – przechowywane dosłownie jako light lub dark.

Zasadniczo jest to kontrolka z 3 stanami (light dark, light, dark), w której stan jawny odpowiadający bieżącym preferencjom systemowym jest nieosiągalny.

Gdy użytkownik przełącza:

  1. Schemat docelowy = przeciwieństwo obecnie renderowanego schematu (zapisana wartość, jeśli istnieje, lub preferencje systemowe). Intencja użytkownika to „wybierz przeciwieństwo tego, co widzę w tej chwili”, a NIE „wybierz odwrotność ustawienia domyślnego systemu”.
  2. Jeśli cel różni się od bieżących preferencji systemowych, zapisz go dosłownie.
  3. Jeśli cel jest zgodny z bieżącymi preferencjami systemowymi, użytkownik cofa dostosowanie: usuń zapisaną wartość. NIE przechowuj go – spowodowałoby to niewidoczne przypięcie schematu, co uniemożliwiłoby wprowadzanie zmian w systemie w przyszłości.
  4. Ustaw zawartość <meta> na zapisaną wartość lub light dark, jeśli nie ma żadnej.

WAŻNE: rozbieżność należy sprawdzać tylko w momencie przechowywania, nigdy wstecznie: przechowywana wartość, którą preferencje systemowe później zmieniają, aby ją dopasować, MUSI zostać zachowana. System operacyjny wielu użytkowników automatycznie przełącza schematy w zależności od pory dnia. Usunięcie zapisanej wartości, gdy te 2 schematy się pokrywają, uniemożliwiłoby przypięcie schematu.

Przykładowy scenariusz, zaczynając od systemu operacyjnego ustawionego na tryb jasny:

  1. Użytkownik przełącza. Tryb ciemny różni się od preferencji systemowych, więc wartość dark jest zapisywana, a witryna przechodzi w tryb ciemny.
  2. Ustawienie systemu operacyjnego zmieni się na ciemne. Witryna pozostaje ciemna (zapisana wartość jest teraz zgodna z preferencjami systemowymi, ale jest zachowywana).
  3. Ustawienie systemu operacyjnego zmieni się z powrotem na jasne. Strona pozostanie ciemna.
  4. Użytkownik przełącza. Wartość docelowa (jasny) odpowiada preferencjom systemowym, więc zapisana wartość jest usuwana, a witryna ponownie dostosowuje się do systemu.

Przełączniki trójstanowe

Gdy jest więcej miejsca, a użytkownicy są już nastawieni na ustawianie długoterminowych preferencji, przełącznik trójstanowy może zapewnić większą przejrzystość. W tym przypadku model danych jest przejrzyście mapowany na 3 opcje interfejsu: system, jasny i ciemny.

Aby zmniejszyć obciążenie poznawcze, opcja „system” powinna też wyświetlać bieżące ustawienie systemu, na przykład:

  • Ikona, np. ikona słońca w prawym dolnym rogu ikony systemowej
  • Tekst, np. „System (obecnie jasny)”