packages/@n8n/stylelint-config/README.md
Stylelint configuration for n8n projects with custom CSS variable naming convention enforcement.
This package is already configured in n8n frontend packages. To use it in a new package:
{
"devDependencies": {
"@n8n/stylelint-config": "workspace:*"
}
}
Create a stylelint.config.mjs file:
import { baseConfig } from '@n8n/stylelint-config/base';
export default baseConfig;
Add scripts to your package.json:
{
"scripts": {
"lint:styles": "stylelint \"src/**/*.{scss,sass,vue}\" --cache",
"lint:styles:fix": "stylelint \"src/**/*.{scss,sass,vue}\" --fix --cache"
}
}
The @n8n/css-var-naming rule enforces a structured naming pattern for CSS custom properties (variables).
--[namespace--][component[--part]--]property--value[--variant][--state][--mode][--media]
Required groups:
property: Property name from vocabulary (color, background, spacing, etc.)value: Semantic name or scale valueOptional groups (in order):
namespace: n8n, chat, or p (for primitives)component: Component name (e.g., button, input)part: Sub-component (e.g., menu, tab, arrow)variant: Visual style (e.g., solid, outline, ghost)state: Interaction state (e.g., hover, active, focus, disabled)mode: Theme/environment (e.g., light, dark, hc)media: Breakpoint (e.g., sm, md, lg)-- between groups, single dash - within groups (kebab-case)/* Global tokens */
--color--primary: #0d6efd;
--spacing--md: 20px;
--text-color--muted: #888;
/* With namespace */
--n8n--color--primary: #0d6efd;
--chat--button--background--primary: #0d6efd;
--p--color--primary-500: #0d6efd;
--color--neutral-850: #2e3440;
/* Component tokens */
--button--background--primary: #0d6efd;
--button--text-color--on-primary: #fff;
--tabs--tab--text-color--muted: #888;
/* With states */
--button--background--primary--hover: #0b5ed7;
--input--border-color--primary--focus: blue;
/* With variants */
--button--background--primary--solid: #0d6efd;
--button--background--primary--outline: transparent;
/* With variants and states */
--button--background--primary--solid--hover: #0b5ed7;
--button--background--primary--outline--focus: rgba(13, 110, 253, 0.5);
/* With modes */
--color--primary--dark: #66a3ff;
--background--surface--dark: #000;
/* Complex patterns */
--n8n--button--background--primary--solid--hover--dark: #0a58ca;
/* ❌ Single dash between groups */
--color-primary: #0d6efd;
/* ❌ Only one group */
--primary: #0d6efd;
/* ❌ Uppercase letters */
--Color--Primary: #0d6efd;
/* ❌ Missing property from vocabulary */
--button--main--primary: #0d6efd;
/* ❌ Invalid value (too short) */
--color--xyz: #0d6efd;
/* ❌ Wrong order (state before variant) */
--button--background--primary--hover--solid: #0d6efd;
/* ❌ Missing required property */
--p--gray-740: #2e3440;
The following properties are recognized:
| Property | CSS Mapping | Description |
|---|---|---|
color | color | Text/element color |
text-color | color | Text-specific color |
background | background-color | Background color |
border-color | border-color | Border color |
border-width | border-width | Border width |
icon-color | fill/stroke | Icon color |
radius | border-radius | Border radius |
shadow | box-shadow | Box shadow |
spacing | margin/padding | Spacing scale |
font-size | font-size | Font size |
font-weight | font-weight | Font weight |
line-height | line-height | Line height |
z | z-index | Z-index |
duration | transition-duration | Animation duration |
easing | transition-timing-function | Animation easing |
outline-color | outline-color | Outline color |
outline-width | outline-width | Outline width |
Use semantic names for values when possible:
primary, secondary, success, warning, danger, info, muted, surface, on-primary, on-surfacesolid, outline, ghost, link, soft, subtlehover, active, focus, focus-visible, visited, disabled, selected, checked, invalid, opened, closed, loadinglight, dark, hc (high-contrast), rtl, printUse scale values for sizes and spacing:
none, 2xs, xs, sm, md, lg, xl, 2xl, 3xlpill, fullregular, medium, semibold, boldprimary-100, primary-500, danger-700Descriptive value names with 4+ characters are also accepted:
base, thin, thick, fast, slow, modal, tooltip, purple, tealDefine global design tokens in a central location:
:root {
--color--primary: #0d6efd;
--color--secondary: #6c757d;
--spacing--sm: 8px;
--spacing--md: 16px;
--spacing--lg: 24px;
}
Create component-specific tokens that reference globals:
:root {
--button--background--primary: var(--color--primary);
--button--text-color--on-primary: #ffffff;
--button--radius--md: var(--radius--md);
--button--padding--md: var(--spacing--md);
}
.button {
background: var(--button--background--primary);
color: var(--button--text-color--on-primary);
border-radius: var(--button--radius--md);
padding: var(--button--padding--md);
&:hover {
background: var(--button--background--primary--hover);
}
}
Override variables for different themes:
:root {
--color--primary: #0d6efd;
--color--surface: #ffffff;
--button--background--primary: var(--color--primary);
}
:root[data-theme="dark"] {
--color--primary: #66a3ff;
--color--surface: #0f1115;
/* button tokens automatically update through var() references */
}
Use namespaces for cross-app libraries:
/* In @n8n/design-system */
:root {
--n8n--color--primary: #ff6d5a;
--n8n--button--background--primary: var(--n8n--color--primary);
}
/* In @n8n/chat */
:root {
--chat--color--primary: #0d6efd;
--chat--button--background--primary: var(--chat--color--primary);
}
The stylelint rule runs automatically in:
Pre-commit hooks (via lefthook):
GitHub Actions CI:
cd packages/@n8n/stylelint-config
pnpm test
pnpm build
# Test on a specific file
pnpm stylelint "path/to/file.scss" --config stylelint.config.mjs
# Test with auto-fix
pnpm stylelint "path/to/file.scss" --fix --config stylelint.config.mjs
Issue: Rule not being applied
pnpm buildIssue: Too many violations in existing codebase
--fix to auto-fix pattern issues, or add /* stylelint-disable @n8n/css-var-naming */ temporarilyIssue: False positive for a valid pattern
To disable for a specific line:
/* stylelint-disable-next-line @n8n/css-var-naming */
--legacy-var-name: value;
To disable for a file:
/* stylelint-disable @n8n/css-var-naming */
To disable in config (not recommended):
export default {
...baseConfig,
rules: {
...baseConfig.rules,
'@n8n/css-var-naming': null,
},
};
When modifying the rule:
src/rules/css-var-naming.tssrc/rules/css-var-naming.test.tspnpm testpnpm buildAll tests must pass before submitting changes.