FISYCO

Публичная бета
РазрывСтоимостьКак работаетПродуктБезопасность
Войти

Как найти зашитые цвета в кодовой базе React

4 мин чтения

Инженерия
A code card with highlighted colour literals and a magnifier over one orange swatch next to a ranked list of files

Зашитый цвет: значение, записанное прямо в код, #2563eb вместо токена. Такой элемент тихо выпадает из дизайн-системы. Он не последует за ребрендингом и не перекрасится в тёмной теме. Здесь собраны команды, которые за минуты находят зашитые цвета в React-кодовой базе, случаи ложных срабатываний и способ остановить новые на ревью.

Коротко
- Ищите пять видов цвета: hex, цветовые функции, именованные цвета, токены палитры и лазейки фреймворков вроде bg-[#…] в Tailwind.
- Прежде чем считать, исключите сгенерированный код и комментарии, иначе цифры соврут.
- Превратите поиск в правило линтера, чтобы счётчик только снижался.
- Зашитый цвет не всегда ошибка. Разрешайте исключения, но с записанной причиной.

Что считается зашитым цветом

CSS принимает больше синтаксисов цвета, чем обычно помнят. CSS Color 4 перечисляет hex из 3, 4, 6 или 8 цифр, rgb() и rgba(), hsl() и hsla(), hwb(), lab(), lch(), oklab(), oklch() и color() (CSS Color Module Level 4). Сверху ещё около 150 именованных цветов (MDN: named-color).

В React-кодовой базе они прячутся в пяти местах:

Последние два легко пропустить. Примитивный токен палитры вроде gray.100 выглядит как токен, но ведёт себя как зашитое значение: в любой теме он один и тот же. Квадратные скобки Tailwind «на лету генерируют класс с любым произвольным значением» (Tailwind: arbitrary values). Это удобно, но тему такой класс обходит целиком.

Команды поиска для каждого вида цвета

ripgrep быстро ищет по всему репозиторию и учитывает .gitignore, поэтому результаты сборки по умолчанию не попадают в выдачу. Его стандартный движок регулярных выражений не умеет lookbehind, поэтому шаблон для hex построен на классе символов. Начните с двух шаблонов, которые ловят большинство случаев:

# Hex colours, not preceded by a letter, & or ( (skips – and issue refs like (#619))
rg -n -e '(^|[^&\w(])#[0-9a-fA-F]{3,8}\b' src

# Colour functions (color( is left out: it matches too many JavaScript calls)
rg -n -e '\b(rgba?|hsla?|hwb|lab|lch|oklab|oklch)\(' src

Затем шаблоны, специфичные для React:

# Raw palette tokens in style props (adjust the palette names to your theme)
rg -n -e '["'"'"'`](gray|red|blue|green)\.\d{2,3}["'"'"'`]' src

# Tailwind arbitrary colours
rg -n -o -e '\b(bg|text|border|fill|stroke)-\[#[0-9a-fA-F]{3,8}\]' src

# Named colours in JS style props (a bare word search drowns in noise; plain CSS needs a Stylelint rule)
rg -n -e '\b(color|background|backgroundColor|bg|borderColor|fill|stroke)\s*[:=]\s*\{?\s*["'"'"'](white|black|red|blue|green|gray|grey)["'"'"']' src

Мы проверили их на небольшом тестовом проекте, где было по одному случаю каждого вида. Пять команд нашли все зашитые значения и ещё три ложных срабатывания. Их убирает следующий раздел.

Уберите шум

Сырой счёт почти всегда завышен. Его раздувают три источника:

  1. Комментарии. Комментарий вроде // Fixes the bug from #619 совпадает с шаблоном для hex. Мы наткнулись ровно на это в собственной проверке CSS: (#619) внутри комментария засчитался как цвет. Отбросьте строки комментариев до подсчёта.
  2. Сгенерированный код. Компоненты иконок, экспортированные из Figma, несут исходные цвета. Когда мы впервые измеряли демо-репозиторий, иконки, выгруженные нашей же синхронизацией, дали около 590 зашитых цветов. С кодом всё было в порядке, ошибалась метрика. Исключите сгенерированные папки glob-шаблоном.
  3. Сама тема. Файл, который связывает смысловые токены с палитрой, обязан упоминать палитру. Исключите его по имени.

Всё вместе, со счётом по файлам:

rg -n --no-heading -g '!src/icons/**' -g '!src/theme.ts' \
  -e '(^|[^&\w(])#[0-9a-fA-F]{3,8}\b' \
  -e '\b(rgba?|hsla?|hwb|lab|lch|oklab|oklch)\(' src \
  | rg -v '^[^:]+:\d+:\s*(//|/\*|\*)' \
  | cut -d: -f1 | sort | uniq -c | sort -nr

На выходе получается ранжированный список файлов: начинайте сверху. Фильтр отбрасывает только строки, целиком занятые комментарием, а комментарии в конце строки по-прежнему попадают в счёт. Поэтому просмотрите первые результаты, прежде чем доверять итогу.

Решите, что разрешено

Не каждый зашитый цвет ошибка. Точный цвет логотипа бренда, белый фон шаблона письма, чёрный фон под видеоплеером: это значения, а не роли. Проблема в исключении, которого никто не видит.

Наше правило простое. Исключение получает комментарий с причиной на той же строке или строкой выше, и проверка его пропускает.

// design-tokens-allow: many email clients don't support CSS variables
const EMAIL_CANVAS = "#ffffff";

Ревьюер прочитает все исключения одним поиском, а на вопрос «почему здесь зашитый цвет?» ответ есть прямо в коде.

Остановите новые на ревью

Поиск показывает, где вы сейчас. Правило линтера не даёт откатиться назад.

  • Stylelint. Встроенное правило color-no-hex делает то, что обещает его название. Плагин stylelint-declaration-strict-value идёт дальше. Для перечисленных свойств, например color, fill и stroke, он требует переменную, функцию или ключевое слово.
  • ESLint. Правило Atlassian ensure-design-token-usage отмечает зашитые цвета и сырые отступы в JavaScript и требует функцию token(). Оно привязано к токенам Atlassian, но показывает, каким может быть правило для ваших собственных токенов.
  • Скрипт в CI. Если стили живут в пропсах, короткий скрипт на шаблонах выше часто оказывается самым быстрым путём. Наш запускается как pnpm design:check в CI на каждом pull request и перед каждым коммитом.

Включите правило в режиме предупреждений и исправьте верхние файлы из своего списка. Затем переключите его в режим ошибки. Дальше счётчик должен только снижаться.

Место зашитых цветов в проверке здоровья

Зашитые цвета относятся к обходу системы: это код, который идёт мимо системы, даже когда нужный токен существует. Их подсчёт входит в четыре сигнала из нашего руководства по измерению здоровья дизайн-системы. Эту часть аудита можно провести сегодня же вечером. Аудит дизайн-системы за один вечер ставит её рядом с остальными проверками.

Посмотрите на каждый обход в своей React-кодовой базе рядом с дизайном, которому он должен следовать.
#Расхождения#Дизайн-токены
KK

Автор статьи

Kirill Karpov

Основатель FISYCO

Кирилл Карпов — основатель FISYCO, инструмента контроля дизайн-системы, который держит Figma и код продакшена в согласии.

Новые статьи на почту

Дизайн-системы, токены и доставка дизайна в код. Без спама, отписка в один клик.

Подписываясь, вы соглашаетесь получать письма блога FISYCO. Подробности — в политике конфиденциальности.

Держите дизайн-систему и код в согласии

FISYCO связывает библиотеки Figma с кодовой базой, находит расхождения и присылает правку пулреквестом.

Начать бесплатно

Похожие статьи

5 мин чтения

Инженерия

SVG-спрайт из Figma и когда он лучше компонентов

Один файл символов вместо компонента на каждую иконку: когда SVG-спрайт выгоднее, как подключить его в React и на чём спотыкаются чаще всего.

6 мин чтения

Инженерия

Иконки из Figma в React: весь путь

От компонента-иконки в Figma до влитого pull request: каждый шаг, что он даёт на выходе, и три формата доставки на настоящем сгенерированном коде.

6 мин чтения

Инженерия

Style Dictionary на практике: токены в CSS, SCSS и TypeScript

Рабочая настройка Style Dictionary 5 для токенов DTCG, проверенная настоящей сборкой: ссылки, режимы, единицы и эксперимент, в котором переименование тихо ломает CSS.

Как найти зашитые цвета в кодовой базе React | FISYCO