Opinionated Stylelint plugin enforcing Kaliber's CSS conventions. Bundles custom rules that promote component-based CSS architecture where components are black boxes with clear boundaries between internal styling and layout positioning.
pnpm add @kaliber/stylelint-pluginRequires Node.js ≥ 20.19.0
Create a .stylelintrc file in your project root:
{
"ignoreFiles": ["node_modules/**/*", "**/*.js", "**/*.svg", "**/*.md"],
"extends": "@kaliber/stylelint-plugin/.stylelintrc"
}| Rule | Description |
|---|---|
color-schemes |
Only color-related properties are allowed in color scheme files |
css-global |
Restrict :root to the cssGlobal directory |
layout-related-properties |
Layout properties (margin, position, z-index, etc.) belong in nested selectors, not root rules |
naming-policy |
Enforce naming conventions for selectors and properties |
selector-policy |
Only direct child selectors, no double nesting, no tag selectors outside reset/index |
parent-child-policy |
Child properties require matching parent context (flex children need display: flex, etc.) |
root-policy |
Root-level z-index must create a valid stacking context with position: relative |
at-rule-restrictions |
Restrict @import to entry files |
index |
Only tag selectors allowed in index.css — no class selectors |
reset |
Only tag selectors allowed in reset.css — no class selectors |
For a complete list of all active rules (custom, third-party, and core Stylelint), see rules-overview.md.
Each rule has a colocated readme.md explaining what it enforces, why, and showing valid/invalid examples. The docsUrl helper resolves the documentation path from the rule's directory:
rules/color-schemes/index.js → rules/color-schemes/readme.md
rules/naming-policy/index.js → rules/naming-policy/readme.md
Each rule exposes two pieces of metadata via meta.docs:
description— a one-liner explaining what the rule enforces. This is the primary context for both editor tooltips and AI coding assistants.url— afile://URL pointing to the full documentation on disk. It resolves locally regardless of where the package is installed (source checkout ornode_modules), so it works without network access or GitHub authentication.
The defineRule helper validates that every rule has the correct shape (ruleName, meta, messages, create). Type definitions are in defineRule.d.ts.
Rules lint against the source CSS as written — there is no PostCSS preprocessing step. Values that can only be resolved at runtime (var(), calc(), env()) are treated as potentially valid rather than flagged:
width: var(--size) !important→ treated as potentially intrinsic ✅display: var(--layout)with a flex child → parent treated as potentially flex ✅padding-top: calc(9 / 16 * 100%)→ treated as potentially percentage ✅
This is handled by the shared containsUnresolvable helper.
- Create
rules/{rule-name}/readme.md - In the rule's
index.js, usedefineRule:import defineRule from '../../machinery/defineRule.js' import docsUrl from '../../machinery/docsUrl.js' export default defineRule({ ruleName: 'rule-name', meta: { description: 'One-line description of the rule', url: docsUrl(import.meta.dirname), }, messages, create(config) { return ({ originalRoot, report }) => { // ... } } })
Stylelint has been updated to v17. If you only use extends without overriding individual rules, no changes are needed.
If you override formatting rules, they've moved to the @stylistic/ namespace. Two rules were also renamed:
{
"extends": "@kaliber/stylelint-plugin/.stylelintrc",
"rules": {
- "indentation": 4,
+ "@stylistic/indentation": 4,
- "at-rule-blacklist": ["apply"],
+ "at-rule-disallowed-list": ["apply"],
- "unit-whitelist": ["px", "rem"],
+ "unit-allowed-list": ["px", "rem"]
}
}See the full list of moved rules.
Caution
When saving a 'bad' .css file, use META + K + S (save without formatting);
some files (e.g. bad/file.css) are deliberately wrongly formatted, which your onSave will try to 'fix' (which it should not).
# Run all tests (integration + unit)
pnpm test
# Integration tests only
pnpm test-integration
# Unit tests only
pnpm test-rules>> pnpm publish
>> git push
>> git push --tags