Темы
Тема переключает значения цветовых ролей, не меняя разметку. Здесь — светлая и тёмная темы, выбор темы и его API, своя тема проекта и проверки.
Темы
Тема определяет, какие значения получают цветовые роли. Framework поставляет светлую и тёмную тему; одна разметка правильна в обеих, а выбор темы ядро делает само.
Как устроена тема
Темы задаются классами .theme-light и .theme-dark. Сам класс темы несёт
только color-scheme:
.theme-light { color-scheme: light; }
.theme-dark { color-scheme: dark; }
Все роли объявлены один раз, и обе их стороны стоят рядом в функции
light-dark():
--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>:
- Если сохранён явный выбор (cookie
sf-themeсо значениемlightилиdark), берётся он. - Иначе тема следует системной настройке устройства
(
prefers-color-scheme) и меняется вместе с ней на лету.
Выбором управляет Loader. Пример показывает три вызова и подписку на событие смены темы:
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 (только для чтения):
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> свой фон не нужен.
Класс темы можно поставить и на часть страницы — например, тёмный блок на светлой странице. Класс темы область не красит: он меняет значения ролей, но фона не задаёт. Участку обязательно нужны свой фон и цвет текста:
<section class="theme-dark bg-surface-0 color-on-surface">
Тёмный блок на светлой странице
</section>
Без фона сквозь полупрозрачные роли внутри (залитое поле, блок кода, тональная кнопка) просвечивает фон страницы, и светлый текст тёмной темы оказывается на светлом.
Для одиночного элемента, который должен выглядеть тёмным в обеих темах,
рекомендуется не переключать тему, а взять инверсную пару
--sf-surface-inverse / --sf-on-surface-inverse или фиксированные роли
-fixed.
Своя тема проекта
Тему из одного фирменного цвета строит генератор темы:
он отдаёт 226 примитивов палитр — тона и их полупрозрачные ступени — правилом
CSS для слоя проекта. Тема проекта —
это переопределение примитивов или ролей в слое проекта
(см. Как переопределять правильно), в CSS, подключённом после core.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, и переопределение
примитивов или ролей под этим классом.
Смотрите также
- Где объявлены токены — где объявлены роли;
- Как переопределять правильно — почему CSS проекта сильнее Framework;
- Замена палитры — новые тона фирменного цвета;
- Проверки — контракт
theme-owner; - Как менять дизайн — что менять, чтобы изменить вид компонентов;
- Переключатель темы — кнопка
.sf-theme-buttonв каталоге.
Как это связано с компонентами и утилитами
Токены, цвет и темы — общий язык всех уровней Framework. Эта глава показывает, как утилиты, компоненты и Smart-компоненты используют роли и почему смена темы не требует правки ни одного из них.
Уровни Framework и роли
Утилиты (см. Цвета) — тонкая обёртка над ролями. Класс color-primary
пишет color: var(--sf-primary), bg-surface-container — фон-плашку,
border-outline — границу контрола, hover:bg-primary — состояние. Утилита
не хранит цвета: сменилась тема — сменился цвет класса. Полный список цветовых
утилит — в справочнике утилит.
Компоненты (см. Из чего сделан компонент и Настройки: общий язык компонентов) красят себя ролями через собственные токены.
Настройка scheme выбирает семейство ролей, status — статусное семейство,
appearance — часть семейства (насыщенную заливку, контейнер, контур).
Поэтому любой вид существует в любой схеме, а смена темы перекрашивает
компонент без правки в нём.
Smart-компоненты (Smart-компоненты) рисуют те же классы компонентов и берут те же токены; собственного оформления у них нет.
Каскадные слои (Каскадные слои) определяют, кто кого перекрывает: компонент сильнее утилиты, состояние сильнее перенастройки, проект без слоя сильнее всего.
Общая схема
палитра (примитивы, :root)
└─ роли (классы темы, light-dark)
├─ токены компонентов → компоненты и Smart-компоненты
└─ утилиты цвета, фона, границы, высоты
Что менять:
- бренд — палитру;
- правило — роль;
- глубокий вид одного Smart-компонента — его шаблон (Шаблоны).
Ни в одном случае не нужно править сам компонент и задавать его внутренние переменные (см. Как менять дизайн).
Смотрите также
- Цвета — цветовые утилиты;
- Из чего сделан компонент и Настройки: общий язык компонентов — компоненты и их настройки;
- 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 и Как Framework проверяет себя — проверки и стандарты;
- Контраст — пороги контраста;
- Одна шкала — шкала прозрачности.
Как менять дизайн
Эта глава показывает, как проект меняет внешний вид Framework, не правя компоненты. Изменение идёт на одном из трёх уровней, и у каждого уровня своя граница.
Три уровня
| Уровень | Что меняется | Чем | Где описано |
|---|---|---|---|
| Общие токены | Вид всех компонентов сразу | Небольшой набор общих токенов: радиусы, цветовые роли и примитивы палитры, лестница размеров, типографика | Цветовые примитивы и палитры, Цветовые роли, Своя тема проекта, Одна лестница размеров, Типографика, Скругления |
| Публичные переменные компонента | Вид одного компонента: цвета, рамка, скругление, отступы | Переменные с одним дефисом после блока (--sf-button-radius); имена стабильны |
Переменные компонента, раздел «Переменные → Настройка» на странице компонента |
| Шаблон Smart-компонента | Глубокое изменение вида одного Smart-компонента | Внешний шаблон проекта; поведение компонента не меняется | Шаблоны |
| Внутренние переменные компонента | Проектом не меняются | Проводка компонента (--sf-button--…), а не поверхность настройки |
Переменные компонента |
Общие токены
Все компоненты читают одни и те же общие токены. Поэтому изменённое значение меняет весь интерфейс согласованно. Пример ниже делает углы острее и меняет шрифт во всём интерфейсе; значения объявлены в слое проекта (см. Как переопределять правильно):
@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-компонент должен выглядеть иначе по устройству, проект подключает для него внешний шаблон. Компонент сохраняет поведение, события и состояние; меняется только разметка и её стили:
<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 (см. Компонент проекта или утилиты).
Смотрите также
- Как переопределять правильно — слой проекта;
- Замена палитры и Своя тема проекта — палитра и тема проекта;
- Одна лестница размеров, Типографика и Скругления — лестница размеров, типографика, скругления;
- Шаблоны — шаблоны Smart-компонентов.