Angular IDPStrong Junior
Модулі / Розмітка й стилі / CSS-змінні й теми
🔥 0

Модуль 1 · урок 10 з 14

CSS-змінні й теми

Це прямий критерій приймання: «робочий перемикач теми на чистих CSS-змінних». Ключове слово — чистих: не два файли стилів, не клас на кожному елементі, а один набір токенів, який змінює значення.

Чим CSS-змінна відрізняється від SCSS-змінної

SCSS $varCSS --var
Коли існуєТільки під час компіляціїУ браузері, у рантаймі
Чи можна змінити на льотуНіТак — і все перемалюється
Каскад і наслідуванняНемаєЄ: значення успадковується вниз по дереву
Для чогоБрейкпоінти, службові числа, логіка міксинівТокени теми: кольори, відступи, радіуси

Тема можлива саме тому, що CSS-змінні живуть у рантаймі й каскадують. Змінив значення на :root — воно змінилось скрізь, де його використали.

:ROOT — СВІТЛА ТЕМА --bg: #fff --ink: #101722 [DATA-THEME="DARK"] --bg: #0D121A --ink: #E7ECF4 перевизначає КОМПОНЕНТИ .card { background: var(--bg); } .card { color: var(--ink); } жоден компонент не знає, яка зараз тема Перемикання теми = зміна одного атрибута на <html>. Жодних класів на компонентах.
Компоненти посилаються на токени, а тема лише підмінює значення токенів. Тому додати третю тему коштує рівно один блок оголошень.

Робочий шаблон теми

/* 1. Світла тема — значення за замовчуванням на голому :root */
:root {
  --bg: #F4F6FA;
  --surface: #FFFFFF;
  --ink: #101722;
  --accent: #A85A08;
  --rule: #D6DEEA;
}

/* 2. Системна темна — але тільки якщо користувач не вибрав світлу вручну */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0D121A;
    --surface: #141B25;
    --ink: #E7ECF4;
    --accent: #E8A34E;
    --rule: #26313F;
  }
}

/* 3. Явний вибір користувача — перебиває системний */
:root[data-theme="dark"] {
  --bg: #0D121A;
  --surface: #141B25;
  --ink: #E7ECF4;
  --accent: #E8A34E;
  --rule: #26313F;
}

body { background: var(--bg); color: var(--ink); }

Логіка трьох блоків важлива, і саме її зазвичай ламають. Стани теми три, а не два: світла, темна і як у системи. Якщо написати лише медіазапит — користувач не зможе вибрати вручну. Якщо лише [data-theme] — не спрацює системна тема на першому візиті.

// перемикач: рівно чотири рядки логіки
const root = document.documentElement;
const saved = localStorage.getItem('theme');          // 'light' | 'dark' | null
if (saved) root.setAttribute('data-theme', saved);

toggle.addEventListener('click', () => {
  const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
  root.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
});
Правила, щоб тема не «попливла»

Ніколи не давай кольору єдине оголошення всередині медіазапиту чи [data-theme] — тоді в іншій темі його просто не буде. Повний набір токенів завжди лежить на голому :root, а решта блоків лише перевизначає.

І другий момент: color-scheme: light dark на :root — щоб скролбари, поля вводу й системні елементи теж перемкнулись.

Токени: як їх називати

Погана назва — --blue. Гарна — --accent. Різниця в тому, що перша описує колір, а друга — роль. У темній темі акцент буде не синім, і назва --blue: orange виглядатиме як знущання.

/* ролі, а не кольори */
--bg, --surface, --surface-2      /* фони: сторінка, картка, вкладений блок */
--ink, --ink-2, --ink-3           /* текст: основний, вторинний, приглушений */
--rule, --rule-soft               /* лінії */
--accent, --ok, --warn            /* акцент і семантика */

≈ 45 хв · +25 XP за урок