textmachine/frontend/stylelint.config.js

75 lines
4.5 KiB
JavaScript
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.

// Гейт «одно место для цвета и размера», половина в CSS (STACK_DECISIONS §3).
// TSX-половина — в eslint.config.js.
//
// Двухслойно: запрет литеральных цветов ловит их в ЛЮБОМ свойстве, включая сокращённые
// записи вроде `border: 1px solid #fff`, а allowed-list добавляет свойства, где литерал
// не цвет (`font-size`, `z-index`). reportDisables делает ошибкой и попытку отключить
// правило комментарием.
const gate = { reportDisables: true };
const token = '/^var\\(--/';
const colorValues = [token, 'inherit', 'currentColor', 'currentcolor', 'transparent'];
// Ф-4: шкала отступов обкатана оболочкой S2, гейт включён. Значение — последовательность токенов;
// 0 и auto легальны, потому что это не размеры, а отсутствие размера и «посчитай сам».
// calc() намеренно не разрешён: составное выражение из литералов проверить нечем, и если размер
// понадобился новый, его место — в tokens.css, а не в скобках calc.
const spacingValues = ['/^(?:(?:var\\(--[a-z0-9-]+\\)|0|auto)\\s*)+$/', 'inherit'];
// Системные цветовые ключевые слова — не «именованные цвета», их не видит color-named, и в
// сокращённых записях (outline, box-shadow, border) их не ловит вообще ничто (дыра из Ф-9).
const systemColors =
'/\\b(AccentColor|AccentColorText|ActiveText|ButtonBorder|ButtonFace|ButtonText|Canvas|CanvasText|Field|FieldText|GrayText|Highlight|HighlightText|LinkText|Mark|MarkText|SelectedItem|SelectedItemText|VisitedText)\\b/i';
export default {
extends: ['stylelint-config-standard'],
// reportDisables на правиле ловит только ИМЕНОВАННОЕ отключение
// (`/* stylelint-disable color-no-hex */`). Голое `/* stylelint-disable */` в шапке файла
// снимало гейт целиком и молча — проверено. Эта опция требует, чтобы отключение всегда
// называло правило, и тем самым закрывает обход.
reportUnscopedDisables: true,
rules: {
'color-no-hex': [true, gate],
'color-named': ['never', gate],
// color() и light-dark() тоже задают цвет литералом, а в сокращённой записи
// (`border: 1px solid color(...)`) их не ловит ничто другое. color-mix() оставлен:
// смешивать ему нечего, кроме токенов.
'function-disallowed-list': [
['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'lab', 'lch', 'oklab', 'oklch', 'color', 'light-dark'],
gate,
],
'declaration-property-value-allowed-list': [
{
color: colorValues,
'background-color': colorValues,
'border-color': colorValues,
background: [...colorValues, 'none'],
fill: [...colorValues, 'none'],
stroke: [...colorValues, 'none'],
'font-size': [token, 'inherit'],
'z-index': [token],
'/^(padding|margin)(-|$)/': spacingValues,
'/^(gap|row-gap|column-gap)$/': spacingValues,
'/^border-.*radius$/': spacingValues,
},
{ ...gate, message: 'Значение только из токена: var(--...) из src/tokens/tokens.css' },
],
'declaration-property-value-disallowed-list': [
{ '/.*/': [systemColors] },
{ ...gate, message: 'Системный цвет ОС — не наш цвет: тон берётся из src/tokens/tokens.css' },
],
// CSS Modules читаются из TS как styles.panelHeader, поэтому классы camelCase.
'selector-class-pattern': ['^[a-z][a-zA-Z0-9]*$', { message: 'Имя класса — camelCase' }],
},
overrides: [
{
// Единственное место, где литеральный цвет легален: сами токены.
files: ['src/tokens/tokens.css'],
rules: {
'color-no-hex': null,
'color-named': null,
'function-disallowed-list': null,
'declaration-property-value-allowed-list': null,
},
},
],
};