К содержанию

Навигация

Темы

Тема переключает значения цветовых ролей, не меняя разметку. Здесь — светлая и тёмная темы, выбор темы и его API, своя тема проекта и проверки.

Темы

Тема определяет, какие значения получают цветовые роли. Framework поставляет светлую и тёмную тему; одна разметка правильна в обеих, а выбор темы ядро делает само.

Как устроена тема

Темы задаются классами .theme-light и .theme-dark. Сам класс темы несёт только color-scheme:

CSS
.theme-light { color-scheme: light; }
.theme-dark  { color-scheme: dark; }

Все роли объявлены один раз, и обе их стороны стоят рядом в функции light-dark():

CSS
--sf-primary: light-dark(var(--sf-primary-40), var(--sf-primary-80));
--sf-surface-0: light-dark(var(--sf-white), var(--sf-neutral-5));

Браузер берёт первое значение при color-scheme: light и второе при dark. Роль не может получить значение только для одной темы.

От color-scheme зависят и системные элементы — полосы прокрутки, нативные поля и флажки, — поэтому тема и нативные элементы всегда совпадают.

Как выбирается тема на странице

Ядро выбирает тему до первой отрисовки и ставит класс на <html>:

  1. Если сохранён явный выбор (cookie sf-theme со значением light или dark), берётся он.
  2. Иначе тема следует системной настройке устройства (prefers-color-scheme) и меняется вместе с ней на лету.

Выбором управляет Loader. Пример показывает три вызова и подписку на событие смены темы:

JavaScript
SF.Loader.setTheme('dark');    // явный выбор, сохраняется
SF.Loader.setTheme('system');  // снова следовать системе
SF.Loader.changeTheme();       // переключить светлую и тёмную

window.addEventListener('sf-theme-change', (event) => {
  console.log(event.detail.theme, event.detail.preference);
});

Значения в detail события sf-theme-change:

Поле Значения Что означает
theme light, dark Тема, которая сейчас на странице
preference system, light, dark Выбор человека; system — явного выбора нет (нет cookie sf-theme), тема следует системе

Ядро отправляет sf-theme-change один раз при запуске, с начальными значениями, и затем при каждой смене темы или выбора. После запуска ядра текущие значения читаются из свойств Loader (только для чтения):

JavaScript
SF.Loader.theme;            // 'light' или 'dark'
SF.Loader.themePreference;  // 'system', 'light' или 'dark'

Готовый переключатель — кнопка с классом .sf-theme-button; атрибут data-theme="system|light|dark" делает из неё выбор конкретного значения. Состояние кнопок Framework ставит сам: атрибут aria-pressed и класс active, с которым кнопка выглядит нажатой:

  • кнопка с data-theme нажата, когда её значение равно текущему выбору (preference). Кнопка system нажата, пока явного выбора нет;
  • кнопка без data-theme переключает светлую и тёмную тему и нажата, когда на странице тёмная тема.

Каждая кнопка группы выбора темы — отдельный переключатель с aria-pressed, поэтому aria-pressed здесь стоит и в группе из нескольких кнопок.

Если проект управляет темой сам, автоматический выбор выключает window.SF_BOOT_CONFIG = { theme: false }. Тогда класс темы на <html> обязательно поставить в разметке: без него роли не объявлены (Где объявлены токены).

Участок другой темы

Саму страницу красит ядро: базовый стиль body в core.css задаёт фон --sf-surface-0 и цвет текста --sf-on-surface (Что в каком слое). Странице с темой на <html> свой фон не нужен.

Класс темы можно поставить и на часть страницы — например, тёмный блок на светлой странице. Класс темы область не красит: он меняет значения ролей, но фона не задаёт. Участку обязательно нужны свой фон и цвет текста:

HTML
<section class="theme-dark bg-surface-0 color-on-surface">
  Тёмный блок на светлой странице
</section>

Без фона сквозь полупрозрачные роли внутри (залитое поле, блок кода, тональная кнопка) просвечивает фон страницы, и светлый текст тёмной темы оказывается на светлом.

Для одиночного элемента, который должен выглядеть тёмным в обеих темах, рекомендуется не переключать тему, а взять инверсную пару --sf-surface-inverse / --sf-on-surface-inverse или фиксированные роли -fixed.

Своя тема проекта

Тему из одного фирменного цвета строит генератор темы: он отдаёт 226 примитивов палитр — тона и их полупрозрачные ступени — правилом CSS для слоя проекта. Тема проекта — это переопределение примитивов или ролей в слое проекта (см. Как переопределять правильно), в CSS, подключённом после core.css. Собранное ядро не правится. Пример показывает три способа:

CSS
@layer project {
  /* Своя палитра: роли получат её сами, потому что ссылаются на примитивы. */
  :root {
    --sf-primary-40: #123456;
    --sf-primary-80: #9ab0c8;
  }

  /* Своя роль сразу для двух тем. */
  .theme-light,
  .theme-dark {
    --sf-link: light-dark(#0057b8, #7fb3ff);
  }

  /* Или только для одной темы. */
  .theme-dark {
    --sf-link: #7fb3ff;
  }
}

Правила:

  • Примитивы — на :root. light-dark() выбирает сторону уже после того, как var() раскрыт, поэтому новый тон сразу работает в обеих темах.
  • Роли — на классах темы, а не на :root. Роль объявлена на элементе с классом темы; участок другой темы внутри страницы объявляет её заново, и значение с :root до него не дойдёт.
  • Высота, плашки и границы перенастраиваются так же — своими ролями (--sf-elevation-N--surface, --sf-elevation-N--shadow, --sf-shadow--rim, --sf-outline), а не правкой компонентов.

Дополнительные темы

Framework поставляет две темы — светлую и тёмную. Проект, которому нужна ещё одна тема (например, приглушённая, высококонтрастная или цветная), строит её тем же способом: свой класс, задающий color-scheme, и переопределение примитивов или ролей под этим классом.

Смотрите также

Как это связано с компонентами и утилитами

Токены, цвет и темы — общий язык всех уровней Framework. Эта глава показывает, как утилиты, компоненты и Smart-компоненты используют роли и почему смена темы не требует правки ни одного из них.

Уровни Framework и роли

Утилиты (см. Цвета) — тонкая обёртка над ролями. Класс color-primary пишет color: var(--sf-primary), bg-surface-container — фон-плашку, border-outline — границу контрола, hover:bg-primary — состояние. Утилита не хранит цвета: сменилась тема — сменился цвет класса. Полный список цветовых утилит — в справочнике утилит.

Компоненты (см. Из чего сделан компонент и Настройки: общий язык компонентов) красят себя ролями через собственные токены. Настройка scheme выбирает семейство ролей, status — статусное семейство, appearance — часть семейства (насыщенную заливку, контейнер, контур). Поэтому любой вид существует в любой схеме, а смена темы перекрашивает компонент без правки в нём.

Smart-компоненты (Smart-компоненты) рисуют те же классы компонентов и берут те же токены; собственного оформления у них нет.

Каскадные слои (Каскадные слои) определяют, кто кого перекрывает: компонент сильнее утилиты, состояние сильнее перенастройки, проект без слоя сильнее всего.

Общая схема

Code
палитра (примитивы, :root)
   └─ роли (классы темы, light-dark)
        ├─ токены компонентов → компоненты и Smart-компоненты
        └─ утилиты цвета, фона, границы, высоты

Что менять:

  • бренд — палитру;
  • правило — роль;
  • глубокий вид одного Smart-компонента — его шаблон (Шаблоны).

Ни в одном случае не нужно править сам компонент и задавать его внутренние переменные (см. Как менять дизайн).

Смотрите также

Проверки

Цветовую систему проверяют контракты исходников Framework. Для проекта они служат перечнем правил: то, что проверяет контракт, считается обязательным.

Контракт Что проверяет
token-derivation Числа есть только у примитивов цвета и размерной лестницы
declared-tokens Каждый токен, который читают собранные стили, где-то объявлен
core-layer-order Порядок каскадных слоёв объявлен первым и один раз
alpha-scale Прозрачность — только ступени шкалы; каждый примитив используется; имя совпадает с непрозрачностью
control-boundary, border-roles Граница контрола — --sf-outline с 3 : 1; граница отключённого слабее активной
disable-role Значения ролей отключённого состояния
muted-role Слабая роль не красит то, что читают
icon-colour-roles Иконка берёт одну из трёх ролей; правило цвета иконки в слое
elevation-shadow, elevation-text Высота — пара; приглушённый текст проходит на каждой ступени
theme-owner Выбор темы, сохранение и событие sf-theme-change

Смотрите также

Как менять дизайн

Эта глава показывает, как проект меняет внешний вид Framework, не правя компоненты. Изменение идёт на одном из трёх уровней, и у каждого уровня своя граница.

Три уровня

Уровень Что меняется Чем Где описано
Общие токены Вид всех компонентов сразу Небольшой набор общих токенов: радиусы, цветовые роли и примитивы палитры, лестница размеров, типографика Цветовые примитивы и палитры, Цветовые роли, Своя тема проекта, Одна лестница размеров, Типографика, Скругления
Публичные переменные компонента Вид одного компонента: цвета, рамка, скругление, отступы Переменные с одним дефисом после блока (--sf-button-radius); имена стабильны Переменные компонента, раздел «Переменные → Настройка» на странице компонента
Шаблон Smart-компонента Глубокое изменение вида одного Smart-компонента Внешний шаблон проекта; поведение компонента не меняется Шаблоны
Внутренние переменные компонента Проектом не меняются Проводка компонента (--sf-button--…), а не поверхность настройки Переменные компонента

Общие токены

Все компоненты читают одни и те же общие токены. Поэтому изменённое значение меняет весь интерфейс согласованно. Пример ниже делает углы острее и меняет шрифт во всём интерфейсе; значения объявлены в слое проекта (см. Как переопределять правильно):

CSS
@layer project {
  :root {
    --sf-radius--ui: var(--sf-radius-1\/3);
    --sf-radius--surface: var(--sf-radius-1\/2);
    --sf-text--family: "Golos Text", system-ui, sans-serif;
    --sf-heading--family: "Golos Text", system-ui, sans-serif;
    --sf-display--family: "Golos Text", system-ui, sans-serif;
  }
}

Обратите внимание: значение радиуса берётся со ступени шкалы, а не задаётся числом. Фирменный цвет меняется так же — тонами палитры (Замена палитры) или ролями на классах темы (Своя тема проекта).

Шаблон Smart-компонента

Если общих токенов недостаточно и Smart-компонент должен выглядеть иначе по устройству, проект подключает для него внешний шаблон. Компонент сохраняет поведение, события и состояние; меняется только разметка и её стили:

HTML
<script>window.SF_SMART_TEMPLATE_PATH = '/assets/project/smart-templates';</script>
<sf-button template="compact"></sf-button>

Шаблон лежит в /assets/project/smart-templates/button/compact/index.js, стили — рядом, в index.css. Контракт модуля — в Контракт внешнего модуля.

Правила

  • Обязательно меняйте дизайн общими токенами; значения берите со ступеней шкал и из палитры.
  • Глубокое изменение вида Smart-компонента делайте внешним шаблоном (Шаблоны), не меняя сам компонент.
  • Вид одного компонента меняйте его публичными переменными (--sf-button-radius, --sf-badge-color), если компонент их предлагает.
  • Нельзя задавать в проекте внутренние переменные компонентов (--sf-button--…, --sf-input-field--… и подобные, с двумя дефисами после блока): это проводка, её имена и устройство не входят в контракт и могут измениться. Токены темы из списка Все переменные к ним не относятся, даже с двумя дефисами (--sf-focus--width, --sf-radius--ui): их проект задаёт.
  • Нельзя править собранные файлы поставки и перебивать правила компонентов специфичностью.
  • Если нужного варианта нет и он нужен не только вашему проекту, это задача для Framework (см. Компонент проекта или утилиты).

Смотрите также

Поиск по документации

Введите минимум 2 символа

    Настройки

    Тема
    Размер шрифта
    Ширина страницы