Перейти к содержимому

Слияние тёмного режима

Тёмный файл ("$dark": true) объявляет только те ключи, которые отличаются от светлой темы; непереопределённые ключи наследуют значения светлой:

color-dark.json
{
"$namespace": "color",
"$dark": true,
"$description": "Объявляются только отличающиеся от светлой темы ключи; непереопределённые наследуются из color.json",
"primary": { "default": { "value": "#3D649E" } }
}

color.primary.hover, .active и другие ключи, не перечисленные здесь, в тёмной теме сохраняют значения светлой. Принцип обрезки — посравнивание ключей: ключи, значения которых полностью совпадают со светлой темой, удаляются (например, onPrimary в обеих темах — #FFFFFF, а disabled.* в обеих темах — один и тот же набор ref, поэтому они не попадают в тёмный файл).

Тёмный файл обязан объявлять то же $namespace, что и светлый (color-dark.json явно пишет "$namespace": "color"), чтобы ключи переопределения легли на тот же путь.

ref в тёмном файле разрешается функцией resolveDarkRef по правилам, отличающимся от светлой:

  1. Цель находится в тёмном пространстве имён и является ref → рекурсия по цепочке внутри тёмного (тёмное ссылается на тёмное — только тогда берётся значение той же темы)
  2. Цель находится в тёмном пространстве имён и является литералом → возвращается тёмное значение
  3. Цель не находится в тёмном пространстве имён → откат к светлому пространству имён за значением
// Тёмное ссылается на тёмное: берётся тёмное primary.active
"selectedDark": { "ref": "color.primary.active" }

Для тёмных ref также выполняется обнаружение циклов (Обнаружен цикл ссылок тёмных токенов), а цель обязана существовать в светлой или тёмной теме, иначе выбрасывается Ссылка тёмного токена X не существует.

В тёмной теме направления web широко используется color-mix(), которой нет у нативных платформ во время выполнения, поэтому на этапе сборки значения предвычисляются в плоские цвета, а основание вычисления записывается в description:

"selected": {
"value": "#B1C1D8",
"description": "Предвычислено из color-mix(primary 40%, #fff); контрастность на тёмной панели 5.2:1"
},
"primary": {
"bg": {
"value": "rgba(61, 100, 158, 0.25)",
"description": "Предвычислено из color-mix(primary 25%, transparent)"
}
}

resolveTokens возвращает TokenSet.dark: плоское Map<path, TokenValue>, содержащее только переопределённые ключи. Target’ы платформ сами решают, как их испустить:

Платформа Механизм тёмной темы
Android Отдельный object LhDarkTokens
iOS Отдельный public enum LhDarkTokens
HarmonyOS Отдельный export class LhDarkTokens
C Переопределение одноимённых макросов — тёмный заголовочный файл подключается после светлого и просто перекрывает одноимённые #define

Два вида аномалий фиксируются в orphanDarkPaths (не прерывают codegen, сообщаются командой pnpm verify:tokens):

  • Тёмный ключ без типа (нет ни собственного type, ни одноимённого ключа в светлой теме, у которого можно его унаследовать) → ключ пропускается и не записывается в таблицу переопределений
  • Тёмный ключ, отсутствующий в светлой теме (сирота) → всё же записывается в таблицу переопределений, но попадает в отчёт

Сейчас тёмная тема покрывает только пространство имён color.* (точное количество ключей меняется от версии к версии; см. столбец тёмной темы на странице Справочник по токенам). Нецветовые пространства имён (отступы, скругления, размеры шрифта и т. д.) в тёмной теме наследуют значения светлой — именно поэтому в dist/docs/tokens.md у нецветовых строк в столбце тёмной темы стоит —.

На странице Справочник по токенам для каждого токена одновременно видны светлое и тёмное значения.

Copyright © 2024-2026 FengMo WenXue