Единый источник правды о том, КАК устроены токены интерфейса. Не список цветов — список правил: какие ярусы существуют, кто из какого яруса имеет право брать значения, что делать, когда нужной роли нет, и чем это проверяется.
Значения живут в
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 не значит ничего, пока ему не назначили роль. Шкала уже является словарём смыслов,
палитра — нет.
Исключение внутри исключения: если у отступа есть имя роли («внутреннее поле панели», «зазор между пунктами списка»), он заводится ролью и берётся как роль. Такие места единичны и перечислены в семантике.
Вынужденные литералы
Три случая, где сырое значение законно, и только они:
rgba(0, 0, 0, 0)— это «нет заливки», а не цвет.- Альфа поверх роли —
--gm-color-scrim, поверхность dev-консоли,--gm-color-menu-shade. USS не умеетrgba(var(--токен), 0.42), а прозрачность там часть роли. Живут в семантике, не в компонентах, и каждый несёт комментарий с обоснованием. - HDR-яркости боевых вспышек — это не оттенок, а сила свечения; живут в
CombatFeelConfigиCombatColorPalette, в палитру не едут.
Мир читает снимок, а не USS
Карта акта, боевые VFX и перекрасчик спрайтов рисуются мимо UI Toolkit и USS читать не умеют.
Между ними стоит ScriptableObjects/Configs/GuildmasterPalette.asset — снимок, который
собирается из USS автоматически при импорте токенов (PaletteSnapshotAutoRebuild). Направление
одностороннее: USS источник, ассет — производная. Правка снимка руками отменяется первой же
пересборкой.
Чем это проверяется
| Инвариант | Гейт | Статус |
|---|---|---|
| снимок не разошёлся с USS | PaletteSnapshotTests | есть |
| элементы экрана не налезают друг на друга | ScreenLayoutOverlapTests | есть |
| компонент не берёт ступень палитры | тест яруса | фаза 1 плана |
| роль без потребителей не копится | скрипт-перепись | фаза 4 плана |
Контраст автоматически НЕ проверяется — и это решение, а не упущение (Макс, 04.08.2026: «лучше оставь это на мои глазочки… Без оверинженеринга»). Числа контраста в комментариях USS — ориентир на момент подбора, а не поддерживаемая правда: при смене палитры они устаревают молча. Опираться на них как на факт нельзя; приёмку держит глаз владельца.