Суть Читайте реальный CSS, DTCG JSON и theme-override; предсказывайте, как каскад разрешает каждый токен, и выбирайте фикс, оставляющий компоненты независимыми от темы.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Баги токенов живут в CSS и JSON, а не в дизайн-доке. Прочитайте каждый сниппет, проследите, как каскад или сборка его разрешают, и выберите фикс, который senior сделает первым.
Цель
Потренируйтесь читать код токенов так, как его дебажат: проследите ссылку через тиры, предскажите, что пере-разрешает theme-override, и заметьте ту единственную строку, что запирает компонент вне тематизации.
Позже dark-тема переопределяет --color-interactive (не primitive). Какой элемент после этого следует за dark mode и в чём фикс для второго?
Heads-up То, что сегодня они разрешаются в одно значение, не значит, что оба следуют за override. Тема переопределяет semantic-имя; .button читает primitive, поэтому изменения не видит.
Heads-up Custom properties каскадируют вживую: переопределение --color-interactive пере-разрешает каждую ссылку var(--color-interactive). .link перекрашивается за одну перерисовку; .button — нет, потому что пропустил semantic-тир.
Heads-up Всё наоборот. Именование primitive прибивает .button к этому значению; тема переопределяет semantic-токен, который читает только .link.
Style Dictionary собирает этот DTCG-источник для web и Android. Что верно о том, как color.interactive и space.4 выходят под каждую платформу?
Heads-up Сборка разрешает алиасы до форматирования; ни одна платформа не получает синтаксис {ref}. Android получает разрешённое, трансформированное значение, а не строку алиаса.
Heads-up $type говорит transform, как конвертировать значение, а не оставить его фиксированным. CSS обычно трансформирует px в rem (1rem), а Android использует свой формат размеров.
Heads-up JSON — источник, никогда не рантайм-артефакт. Transforms адаптируют каждое значение (px в rem, hex в ARGB), а formatters выдают реальные файлы CSS и XML.
Переключение data-theme в dark перекрашивает большую часть .card, но оставляет один видимый дефект. В чём он и каков фикс?
Heads-up Светло-серая граница на почти чёрной поверхности — видимый двухцветный дефект. Граница — это тематизируемое решение поверхности и нуждается в semantic-токене, как и всё остальное.
Heads-up [data-theme=dark] переопределяет те же имена custom properties; каскад пере-разрешает каждый var(), который их читает. Не следует только hardcoded-граница, которая не var().
Heads-up Это снова вводит JS для того, что каскад делает бесплатно. Определите semantic-токен границы под обоими селекторами, и граница тематизируется за одну перерисовку.
Сниппет 4 — fallback
.badge { /* --brand-accent задаётся некоторыми host-страницами, отсутствует на других */ color: var(--brand-accent); padding: var(--space-2, 8px);}
Викторина
Completed
На host-странице, которая никогда не задаёт --brand-accent, как ведёт себя каждое свойство и чему учит var()-fallback?
Heads-up Одна невалидная ссылка на custom property не обнуляет правило. color становится invalid-at-computed-value (берёт inherited/initial), а padding нормально разрешается через свой fallback.
Heads-up var() без fallback даёт guaranteed-invalid значение, запуская обработку invalid-at-computed-value — inherited для color, а не hardcoded чёрный. Надёжный дефолт — явный fallback.
Heads-up var(--name, fallback) — это ровно поддерживаемый синтаксис; второй аргумент используется, когда свойство не задано. Поэтому padding остаётся 8px.
Итог
Баги токенов читаются в CSS и JSON: компонент, называющий primitive, выпадает из тематизации, пока его сосед на semantic-ссылке перекрашивается; DTCG-сборка разрешает алиасы, затем трансформирует значения, так что один источник корректен под платформу; единственный hardcoded literal переживает переключение темы как видимый двухцветный дефект; а var() с fallback — это ремень безопасности для токенов, которые потребитель может не определить. Проследите ссылку через тиры — и фикс почти всегда «ссылайся на semantic-токен, с fallback».