Back to N8n

@n8n/stylelint-config

packages/@n8n/stylelint-config/README.md

1.37.28.7 KB
Original Source

@n8n/stylelint-config

Stylelint configuration for n8n projects with custom CSS variable naming convention enforcement.

Features

  • Pre-configured stylelint rules for SCSS/Sass and Vue files
  • Custom CSS variable naming convention based on the proposal.md
  • Automatic enforcement via pre-commit hooks
  • CI/CD integration

Installation

This package is already configured in n8n frontend packages. To use it in a new package:

json
{
  "devDependencies": {
    "@n8n/stylelint-config": "workspace:*"
  }
}

Usage

Create a stylelint.config.mjs file:

javascript
import { baseConfig } from '@n8n/stylelint-config/base';

export default baseConfig;

Add scripts to your package.json:

json
{
  "scripts": {
    "lint:styles": "stylelint \"src/**/*.{scss,sass,vue}\" --cache",
    "lint:styles:fix": "stylelint \"src/**/*.{scss,sass,vue}\" --fix --cache"
  }
}

CSS Variable Naming Convention

The @n8n/css-var-naming rule enforces a structured naming pattern for CSS custom properties (variables).

Pattern Structure

--[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 value

Optional 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)

Rules

  1. Pattern: Double dash -- between groups, single dash - within groups (kebab-case)
  2. Case: Lowercase alphanumerics only
  3. Groups: Minimum 2, maximum 8 groups
  4. Order: Groups must follow the canonical order shown above

Valid Examples

css
/* 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;

Invalid Examples

css
/* ❌ 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;

Property Vocabulary

The following properties are recognized:

PropertyCSS MappingDescription
colorcolorText/element color
text-colorcolorText-specific color
backgroundbackground-colorBackground color
border-colorborder-colorBorder color
border-widthborder-widthBorder width
icon-colorfill/strokeIcon color
radiusborder-radiusBorder radius
shadowbox-shadowBox shadow
spacingmargin/paddingSpacing scale
font-sizefont-sizeFont size
font-weightfont-weightFont weight
line-heightline-heightLine height
zz-indexZ-index
durationtransition-durationAnimation duration
easingtransition-timing-functionAnimation easing
outline-coloroutline-colorOutline color
outline-widthoutline-widthOutline width

Value Types

Semantic Values

Use semantic names for values when possible:

  • Colors: primary, secondary, success, warning, danger, info, muted, surface, on-primary, on-surface
  • Variants: solid, outline, ghost, link, soft, subtle
  • States: hover, active, focus, focus-visible, visited, disabled, selected, checked, invalid, opened, closed, loading
  • Modes: light, dark, hc (high-contrast), rtl, print

Scale Values

Use scale values for sizes and spacing:

  • Size scales: none, 2xs, xs, sm, md, lg, xl, 2xl, 3xl
  • Special: pill, full
  • Font weights: regular, medium, semibold, bold
  • Numeric scales: primary-100, primary-500, danger-700

Custom Values

Descriptive value names with 4+ characters are also accepted:

  • base, thin, thick, fast, slow, modal, tooltip, purple, teal

Usage in CSS/SCSS/Vue

Global Tokens

Define global design tokens in a central location:

scss
:root {
  --color--primary: #0d6efd;
  --color--secondary: #6c757d;
  --spacing--sm: 8px;
  --spacing--md: 16px;
  --spacing--lg: 24px;
}

Component Tokens

Create component-specific tokens that reference globals:

scss
: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);
}

Usage in Components

scss
.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);
  }
}

Theming

Override variables for different themes:

scss
: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 */
}

Namespaced Tokens

Use namespaces for cross-app libraries:

scss
/* 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);
}

CI/CD Integration

The stylelint rule runs automatically in:

  1. Pre-commit hooks (via lefthook):

    • Validates CSS variables before commit
    • Auto-fixes issues when possible
  2. GitHub Actions CI:

    • Runs on all pull requests
    • Blocks merging if violations are found

Development

Running Tests

bash
cd packages/@n8n/stylelint-config
pnpm test

Building

bash
pnpm build

Testing the Rule

bash
# 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

Troubleshooting

Common Issues

Issue: Rule not being applied

  • Solution: Make sure package is built: pnpm build

Issue: Too many violations in existing codebase

  • Solution: Use --fix to auto-fix pattern issues, or add /* stylelint-disable @n8n/css-var-naming */ temporarily

Issue: False positive for a valid pattern

  • Solution: Check the pattern follows the canonical structure. If it's a legitimate case, please file an issue.

Disabling the Rule

To disable for a specific line:

css
/* stylelint-disable-next-line @n8n/css-var-naming */
--legacy-var-name: value;

To disable for a file:

css
/* stylelint-disable @n8n/css-var-naming */

To disable in config (not recommended):

javascript
export default {
  ...baseConfig,
  rules: {
    ...baseConfig.rules,
    '@n8n/css-var-naming': null,
  },
};

References

Contributing

When modifying the rule:

  1. Update the rule in src/rules/css-var-naming.ts
  2. Add/update tests in src/rules/css-var-naming.test.ts
  3. Run tests: pnpm test
  4. Build: pnpm build
  5. Update this README if needed

All tests must pass before submitting changes.