Files
progcode/editorial/agent-rewrites/202.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
17 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 202,
"slug": "editorial-2022-05-field-design-system",
"title": "Как менять токен дизайн-системы и не сломать соседний экран",
"excerpt": "Практический разбор правки токена: как зафиксировать симптом, оценить радиус изменения, проверить состояния кнопки и оставить точный путь к откату.",
"contentHtml": "<p>Цвет primary-кнопки меняют в одной строке, а ошибка появляется на другом экране. На форме оплаты пропадает focus ring. В диалоге отмены кнопка получает цвет действия подтверждения. В третьем месте новый токен не применяется, потому что компонент ждёт другое имя. Визуально это похоже на одну проблему. Технически это разные сбои контракта.</p>\n<p>Цена ошибки растёт вместе с радиусом общего токена. Локальная правка исправляет один usage. Глобальная правка меняет все usages, включая те, которые не попали в поиск. Если команда не знает список мест и состояний, она не может объяснить diff, выбрать безопасный откат или отличить новый вариант от случайного исключения.</p>\n<p><strong>Тезис.</strong> Токен нельзя менять по одному скриншоту. Сначала свяжите симптом с ролью компонента, состоянием, именем токена и известными usages. Затем проверьте допустимость значения. Только после этого меняйте один слой и сохраняйте прежнее значение. Такой порядок делает изменение ограниченным, наблюдаемым и обратимым.</p>\n<h2>Механизм: токен имеет владельца и радиус</h2>\n<p>Дизайн-токен — не просто цветовая константа. Он выражает роль: например, фон primary-кнопки в обычном состоянии. Роль связывает значение с компонентом и состоянием. Если код использует <code>#2457D6</code> напрямую, он обходит эту связь. Если код ссылается на неизвестное имя, система получает второй, неописанный способ задать ту же роль.</p>\n<p>У одного значения есть четыре границы. Первая — имя роли, например <code>button.primary.background</code>. Вторая — состояние: <code>default</code>, <code>hover</code>, <code>focus-visible</code>, <code>disabled</code> или <code>loading</code>. Третья — компонент, который действительно может использовать эту роль. Четвёртая — набор известных мест в коде. Пропуск любой границы превращает косметическую правку в догадку.</p>\n<p>Состояния нельзя восстановить из одного цвета. Кнопка может сохранить фон и потерять outline при переходе на клавиатуру. Она может выглядеть одинаково в спокойном состоянии, но стать неразличимой при отключении. Поэтому inventory должен хранить не только имя токена, но и ожидаемые состояния. Для ссылки, переключателя и destructive-действия нужен отдельный контракт, а не необязательный флаг в универсальной кнопке.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><caption>Минимальная диагностика разрыва контракта</caption><thead><tr><th>Симптом</th><th>Причина</th><th>Проверка</th><th>Действие</th></tr></thead><tbody><tr><td>На одном экране другой синий</td><td>Literal обошёл именованный токен</td><td>Найти значение и сравнить с ролью в inventory</td><td>Заменить подтверждённый usage</td></tr><tr><td>После refactor исчез focus ring</td><td>Состояние не входит в матрицу</td><td>Пройти кнопку клавиатурой</td><td>Вернуть <code>focus-visible</code> в контракт</td></tr><tr><td>Новое имя не работает</td><td>Имя отсутствует в словаре роли</td><td>Проверить декларацию и чтение</td><td>Остановить правку или добавить роль</td></tr><tr><td>Review назван visual-проверкой без снимка</td><td>Список входов перепутали с результатом</td><td>Проверить браузер, viewport и diff</td><td>Не выдавать зелёный статус без артефакта</td></tr><tr><td>Платёжный экран изменился вместе с профилем</td><td>Общий токен исправляли без радиуса</td><td>Сопоставить usages и роли</td><td>Вернуть прежнее значение и отделить variant</td></tr></tbody></table>\n<h2>Инвентарь до правки</h2>\n<p>Начните с короткой таблицы известных usages. Для каждого места запишите компонент, роль, состояние, имя и способ задания значения. Например, <code>profile-save</code> использует <code>button.primary.background</code> в состояниях <code>default</code>, <code>hover</code>, <code>focus-visible</code>, <code>disabled</code> и <code>loading</code>. <code>dialog-cancel</code> может быть secondary-кнопкой. Похожая разметка не делает эти usages одной ролью.</p>\n<p>Поиск по имени и hex-значению даёт кандидатов, но не доказывает полноту inventory. Стиль может прийти из темы, CSS-переменной, inline-значения или обёртки. Автоматический поиск не определяет, является ли ссылка кнопкой по смыслу. Поэтому результат поиска нужно проверить на уровне entry point и фактического состояния. Учебный пример ниже ограничен тремя usages и не изображает полный обход настоящего репозитория.</p>\n<h2>Проверка входа раньше изменения</h2>\n<p>Неверная конфигурация должна остановиться до записи. В примере есть словарь разрешённых ролей и простая проверка формата: цвет передаётся как <code>#RRGGBB</code>. Это учебное правило, а не универсальный валидатор CSS. Его задача — не дать срочной правке молча создать новый ключ или принять случайную строку.</p>\n<pre><code>const tokens = {'button.primary.background':'#2457D6'};\nfunction correctToken(name, nextValue) {\n if (!(name in tokens)) return {ok:false, reason:'unknown-token'};\n if (!/^#[0-9A-F]{6}$/i.test(nextValue)) return {ok:false, reason:'invalid-color'};\n return {ok:true, name, previousValue:tokens[name], nextValue};\n}\nfunction rollbackToken(change) {\n return {ok:change.ok, name:change.name, nextValue:change.previousValue};\n}\nconst change = correctToken('button.primary.background','#1D4ED8');\nconst restored = rollbackToken(change);</code></pre>\n<p>Функция не меняет объект сама. Она возвращает решение с именем, прежним и новым значением. Отдельный слой применяет решение после проверки inventory. Валидатор не знает, подходит ли новый синий для оплаты, профиля или тёмной темы. Он проверяет только форму и существование роли.</p>\n<p>В учебном коде <code>change.nextValue</code> равен <code>#1D4ED8</code>, а <code>restored.nextValue</code> равен <code>#2457D6</code>. Пример не запускает CSS, не открывает браузер и не измеряет контраст. Он показывает только две операции: неизвестное имя отклоняется, а принятая правка хранит точное прежнее значение. В настоящем проекте формат, запись и права должны соответствовать его контракту.</p>\n<figure><img src='/assets/editorial/2022/design-system-diagnosis-rollback-2022.svg' alt='Схема изменения токена: inventory ведёт к проверке роли и состояния, неверная конфигурация останавливается, допустимая правка сохраняет прежнее значение для отката' /><figcaption>Диагностический маршрут токена. Схема показывает порядок проверки и возврата значения; она не является результатом visual-регрессионного запуска.</figcaption></figure>\n<h2>Почему список входов не равен проверке интерфейса</h2>\n<p>До реального запуска можно составить список входов: viewport 375 и 1280, состояния <code>default</code>, <code>hover</code>, <code>focus-visible</code>, <code>disabled</code>, <code>loading</code>, светлая и тёмная темы. Список полезен как граница проверки: он заставляет назвать объём работы и не забыть состояние.</p>\n<p>Но список не видит пиксели, cascade, media query, шрифт или порядок фокуса. Заполненный JSON не доказывает, что браузер применил нужную переменную. Для visual-проверки нужны страница, браузер, viewport, baseline, новый снимок, правило допустимого diff и сохранённый результат. Для доступности нужны keyboard route и выбранная комбинация браузера и вспомогательной технологии. Если артефакта нет, проверку нельзя называть успешной.</p>\n<h2>Порядок исправления</h2>\n<ol><li><strong>Зафиксируйте симптом.</strong> Укажите экран, компонент, состояние, ожидаемое и фактическое значение.</li><li><strong>Определите роль.</strong> Проверьте, что control действительно primary-кнопка. Для другого смысла нужен отдельный контракт.</li><li><strong>Соберите inventory.</strong> Найдите usages по имени, роли и literal-значению. Отметьте подтверждённые места.</li><li><strong>Проверьте состояния.</strong> Пройдите клавиатурой focus-visible, затем проверьте disabled и loading. Default не заменяет остальные состояния.</li><li><strong>Проверьте вход.</strong> Отклоните неизвестный token и неверный формат. Не добавляйте новый ключ как временное исключение.</li><li><strong>Сделайте одну правку.</strong> Сохраните previous value, измените выбранную роль и запишите причину.</li><li><strong>Проверьте отрицательный путь.</strong> Передайте неизвестное имя, неверное значение и отмену принятой правки. Ни один путь не должен менять baseline молча.</li><li><strong>Проверьте интерфейс.</strong> Соберите CSS, откройте нужные viewport, пройдите клавиатурой и сохраните снимок или иной артефакт.</li></ol>\n<h2>Ограничения</h2>\n<p>Этот подход не вычисляет контраст и не заменяет ручную проверку. Он не решает каскад тем, локализацию, media queries, пользовательские настройки, разные браузеры и конкурирующие изменения. Он также не определяет, является ли новый цвет хорошим дизайном. Он отвечает на более узкий вопрос: существует ли роль, что именно она затрагивает и можно ли вернуть прежнее значение.</p>\n<p>Один общий токен не всегда лучше двух. Если профиль и оплата имеют разные требования к смыслу, состояниям или риску ошибки, их нужно разделить после подтверждения usages. Нельзя объявлять variant только потому, что один экран случайно выглядит иначе. Сначала исключите literal, неправильное состояние и неверное имя.</p>\n<p>Rollback не безопасен во всех слоях. Возврат значения токена не отменяет опубликованный CSS, не откатывает сборку и не компенсирует действие пользователя. Для этих уровней нужны собственные процедуры и артефакты. Здесь rollback ограничен одной парой <code>name → previousValue</code>.</p>\n<h2>Критерий готовности</h2>\n<p>Правка готова, если команда может показать inventory затронутых usages, матрицу состояний и запись с прежним значением. Неизвестный token и неверный формат останавливаются без изменения. Допустимая правка меняет только выбранную роль. Отдельный запуск подтверждает нужные viewport и состояния реальным артефактом. Повторный просмотр возвращает прежнее значение по сохранённому <code>previousValue</code>, а не по памяти или догадке.</p>\n<p>Если visual или accessibility запуск ещё не выполнен, критерий должен прямо это показывать. Нельзя превращать объявленный объём проверки в результат. Для учебного примера достаточно проверить решения функции и отрицательные входы. Для своего интерфейса добавьте браузерную проверку, историю baseline и ссылку на сохранённый diff.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href='https://www.w3.org/TR/WCAG22/' target='_blank' rel='noopener noreferrer'>W3C: Web Content Accessibility Guidelines (WCAG) 2.2</a> — требования к видимости фокуса, контрасту и состояниям интерфейса.</li><li><a href='https://www.w3.org/TR/css-variables-1/' target='_blank' rel='noopener noreferrer'>W3C: CSS Custom Properties for Cascading Variables Module Level 1</a> — область применения и вычисление пользовательских CSS-свойств.</li><li><a href='https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element' target='_blank' rel='noopener noreferrer'>WHATWG HTML Standard: The button element</a> — семантика нативной кнопки и её состояния в HTML.</li></ul>"
}