Единый источник правды о том, КАК устроены токены интерфейса. Не список цветов — список правил: какие ярусы существуют, кто из какого яруса имеет право брать значения, что делать, когда нужной роли нет, и чем это проверяется.

Значения живут в Assets/_Project/UI/Theme/tokens.primitives.uss (палитра) и tokens.semantic.uss (роли). Кегли — Reference - UI Typography. Боевой свет — Reference - VFX Color.


Три яруса, и что каждый значит

ЯрусГде живётЧто этоКто имеет право брать
1. Палитра (primitives)tokens.primitives.ussсырые ступени рамп: --gm-brass-500, --gm-ink-700. Отвечают только на вопрос «какой это цвет»только ярус 2
2. Роли (semantic)tokens.semantic.ussсмысл: --gm-color-text, --gm-color-border-frame, --gm-color-text-action. Отвечают на вопрос «что это значит на экране»ярус 3
3. Компонентыcomponents.uss + контролы в Scripts/UI/Componentsкнопка, строка опции, панель, вуаль — вид и поведение повторяющегося элементаэкраны

HARD: компонент берёт цвет ТОЛЬКО из роли. Ступень рампы в components.uss или в экранном USS — дефект, а не сокращение. Причина не в чистоте: пока часть потребителей висит на ступени, а не на роли, палитру нельзя сменить целиком — половина интерфейса не поедет. Это же условие делает возможными несколько визуальных стилей поверх одних и тех же ролей.

Палитра может быть сколь угодно большой. Ступеней хоть сотни, хоть по набору на каждый стиль — это сырьё. Ограничение лежит не на палитре, а на ролях: их список конечен, и каждая роль обязана применяться. Роль, которую никто не спрашивает, — мусор, а не запас (так умерла рампа ember).

Роли нет — заводится роль. Отсутствие подходящей роли никогда не разрешает взять ступень: это сигнал, что словарь смыслов неполон. Заводится ступень (если нужна) → роль в семантике с объяснением, что она значит → потребитель ссылается на роль.


Что такое «компонент» у нас, и почему нет component-токенов

Термины расходятся, и путать их дорого.

Компоненты у нас есть и должны быть — это повторяющиеся элементы интерфейса: PlateButton, ToggleRow, SliderRow, SelectRow, PanelFrame, EdgeVeil плюс их классы в components.uss. Всё, что встречается на двух экранах, обязано быть компонентом, а не копией разметки. Дыра здесь есть, и она известна: компонентов РАСКЛАДКИ у нас нет — экран до сих пор собирается из голых VisualElement, и автор обязан помнить про flex-shrink, min-height: 0 и прокрутку. Это долг, а не замысел (план переделки, фаза 3).

Component-токены — другое. Это отдельные переменные вида --gm-button-primary-bg, заводимые под каждый компонент поверх ролей. Их у нас нет намеренно: они окупаются, когда у компонента есть переопределения, которые нельзя выразить ролью, а у нас каждый такой случай — признак того, что роли не хватает. Заводить их «на будущее» значит получить третий словарь, который надо синхронизировать с двумя предыдущими.

Короче: компоненты — да, обязательно. Токены на каждый компонент — нет.


Шкалы против палитры

Правило «только через роль» относится к цвету. Шкалы — отступы (--gm-space-*), кегли (--gm-font-*), радиусы (--gm-radius-*) — компонент берёт напрямую.

Разница не в удобстве: --gm-space-2 осмыслен сам по себе («вторая ступень шага»), а --gm-brass-500 не значит ничего, пока ему не назначили роль. Шкала уже является словарём смыслов, палитра — нет.

Исключение внутри исключения: если у отступа есть имя роли («внутреннее поле панели», «зазор между пунктами списка»), он заводится ролью и берётся как роль. Такие места единичны и перечислены в семантике.


Вынужденные литералы

Три случая, где сырое значение законно, и только они:

  1. rgba(0, 0, 0, 0) — это «нет заливки», а не цвет.
  2. Альфа поверх роли--gm-color-scrim, поверхность dev-консоли, --gm-color-menu-shade. USS не умеет rgba(var(--токен), 0.42), а прозрачность там часть роли. Живут в семантике, не в компонентах, и каждый несёт комментарий с обоснованием.
  3. HDR-яркости боевых вспышек — это не оттенок, а сила свечения; живут в CombatFeelConfig и CombatColorPalette, в палитру не едут.

Мир читает снимок, а не USS

Карта акта, боевые VFX и перекрасчик спрайтов рисуются мимо UI Toolkit и USS читать не умеют. Между ними стоит ScriptableObjects/Configs/GuildmasterPalette.assetснимок, который собирается из USS автоматически при импорте токенов (PaletteSnapshotAutoRebuild). Направление одностороннее: USS источник, ассет — производная. Правка снимка руками отменяется первой же пересборкой.


Чем это проверяется

ИнвариантГейтСтатус
снимок не разошёлся с USSPaletteSnapshotTestsесть
элементы экрана не налезают друг на другаScreenLayoutOverlapTestsесть
компонент не берёт ступень палитрытест ярусафаза 1 плана
роль без потребителей не копитсяскрипт-переписьфаза 4 плана

Контраст автоматически НЕ проверяется — и это решение, а не упущение (Макс, 04.08.2026: «лучше оставь это на мои глазочки… Без оверинженеринга»). Числа контраста в комментариях USS — ориентир на момент подбора, а не поддерживаемая правда: при смене палитры они устаревают молча. Опираться на них как на факт нельзя; приёмку держит глаз владельца.