Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
358 lines
8.7 KiB
Markdown
358 lines
8.7 KiB
Markdown
# @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](../../../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:
|
|
|
|
| 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 |
|
|
|
|
## 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
|
|
|
|
- [Stylelint Documentation](https://stylelint.io/)
|
|
- [n8n Design System](../frontend/@n8n/design-system/)
|
|
|
|
## 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.
|