stylelint-plugin-rhythmguard 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/CONTRIBUTING.md +24 -5
- package/README.md +45 -768
- package/package.json +15 -6
- package/src/audit/args.js +449 -0
- package/src/audit/baseline.js +114 -0
- package/src/audit/config.js +217 -0
- package/src/audit/contract.js +422 -0
- package/src/audit/render-github.js +39 -0
- package/src/audit/render-html.js +78 -0
- package/src/audit/render-markdown.js +227 -0
- package/src/audit/render-text.js +168 -0
- package/src/audit/render-utils.js +41 -0
- package/src/audit/report.js +189 -0
- package/src/audit/scan.js +546 -0
- package/src/audit/shared.js +108 -0
- package/src/cli/audit.js +22 -2213
- package/src/cli/index.js +11 -5
- package/src/cli/init.js +5 -1
- package/src/cli/quickstart.js +203 -0
- package/src/configs/embed.js +24 -0
- package/src/configs/embed.mjs +4 -0
- package/src/eslint/rules/tailwind-class-use-motion-scale.js +1 -0
- package/src/eslint/rules/tailwind-class-use-scale.js +1 -0
- package/src/index.js +2 -0
- package/src/rules/no-offscale-transform/index.js +23 -1
- package/src/rules/prefer-token/index.js +29 -1
- package/src/rules/use-motion-scale/index.js +1 -1
- package/src/rules/use-scale/index.js +35 -5
- package/src/utils/length.js +16 -0
- package/src/utils/options.js +53 -3
- package/src/utils/scale-inference.js +255 -0
- package/src/utils/token-map.js +4 -2
- package/src/utils/token-sources.js +32 -2
- package/types/__checks__/consumer.ts +80 -0
- package/types/audit.d.ts +25 -4
- package/types/index.d.ts +5 -0
- package/types/shared.d.ts +22 -1
package/README.md
CHANGED
|
@@ -4,201 +4,36 @@
|
|
|
4
4
|
|
|
5
5
|
# stylelint-plugin-rhythmguard
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Spacing scale and design-token governance for CSS and Tailwind. `padding: 13px` and `p-[13px]` get reported with the nearest on-scale values, and fixed to them when you ask.
|
|
8
8
|
|
|
9
9
|
[](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/actions/workflows/ci.yml)
|
|
10
10
|
[](https://www.npmjs.com/package/stylelint-plugin-rhythmguard)
|
|
11
11
|
[](https://www.npmjs.com/package/stylelint-plugin-rhythmguard)
|
|
12
12
|
[](./LICENSE)
|
|
13
|
-
[](https://nodejs.org/)
|
|
14
13
|
|
|
15
|
-
Rhythmguard
|
|
14
|
+
Rhythmguard is scale-aware rather than a blanket ban: values on your scale pass, values off it are reported with the two nearest steps, and tokens are only ever suggested from a map you control. It works on CSS declarations through Stylelint and on Tailwind class strings through an ESLint companion, and it ships an audit CLI so you can measure drift and ratchet it down before enforcing anything.
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
What it is not: it does not check colors or hex values, the Stylelint rules do not see Tailwind class strings (that is the separate ESLint companion below), and it does not parse SCSS in the audit. Pair it with a color linter if you need one; do not expect one tool to do both.
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
- token-first workflows with autofix migration
|
|
21
|
-
- Tailwind arbitrary value governance (`p-[13px]` → `p-[12px]`)
|
|
22
|
-
- consistent layout rhythm across components and pages
|
|
23
|
-
|
|
24
|
-
## Quick Start: Next.js + Tailwind
|
|
18
|
+
## Start here
|
|
25
19
|
|
|
26
20
|
```bash
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**.stylelintrc.json:**
|
|
31
|
-
|
|
32
|
-
```json
|
|
33
|
-
{
|
|
34
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/tailwind"]
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**eslint.config.js** (for Tailwind class-string governance):
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
import rhythmguard from 'stylelint-plugin-rhythmguard/eslint';
|
|
42
|
-
|
|
43
|
-
export default [
|
|
44
|
-
{
|
|
45
|
-
plugins: { 'rhythmguard-tailwind': rhythmguard },
|
|
46
|
-
rules: {
|
|
47
|
-
'rhythmguard-tailwind/tailwind-class-use-scale': [
|
|
48
|
-
'error',
|
|
49
|
-
{ scale: [0, 4, 8, 12, 16, 24, 32] }
|
|
50
|
-
],
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
];
|
|
21
|
+
npx rhythmguard
|
|
54
22
|
```
|
|
55
23
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
## Rule Matrix
|
|
59
|
-
|
|
60
|
-
<p align="center">
|
|
61
|
-
<img src="https://raw.githubusercontent.com/petrilahdelma/stylelint-plugin-rhythmguard/main/assets/rhythmguard-rules.svg" width="100%" alt="Rhythmguard rule matrix visual" />
|
|
62
|
-
</p>
|
|
63
|
-
|
|
64
|
-
| Rule | What it does | Autofix |
|
|
65
|
-
| --- | --- | --- |
|
|
66
|
-
| `rhythmguard/use-scale` | Enforces spacing values must be on your configured scale | Yes, nearest safe value |
|
|
67
|
-
| `rhythmguard/prefer-token` | Enforces token usage over raw spacing literals | Yes, with `tokenMap` |
|
|
68
|
-
| `rhythmguard/no-offscale-transform` | Enforces scale-aligned `translate*` motion offsets | Yes, nearest safe value |
|
|
69
|
-
| `rhythmguard/use-motion-scale` | Enforces opt-in duration/delay rhythm and flags raw easing curves | Yes, for duration/delay values |
|
|
70
|
-
|
|
71
|
-
## Demo
|
|
72
|
-
|
|
73
|
-
<p align="center">
|
|
74
|
-
<a href="https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/assets/rhythmguard-campaign-60s.webm">
|
|
75
|
-
<img src="https://raw.githubusercontent.com/petrilahdelma/stylelint-plugin-rhythmguard/main/assets/rhythmguard-campaign-60s.gif" width="100%" alt="Rhythmguard 60-second demo" />
|
|
76
|
-
</a>
|
|
77
|
-
</p>
|
|
78
|
-
|
|
79
|
-
I built Rhythmguard after 20 years of watching teams ignore spacing scales and ship arbitrary pixel values everywhere.
|
|
80
|
-
|
|
81
|
-
## Try It in Your Browser
|
|
82
|
-
|
|
83
|
-
**[petrilahdelma.github.io/stylelint-plugin-rhythmguard](https://petrilahdelma.github.io/stylelint-plugin-rhythmguard/)** — paste CSS, see violations and token opportunities live. No install, no config.
|
|
84
|
-
|
|
85
|
-
## Audit Before You Enforce
|
|
86
|
-
|
|
87
|
-
Use the audit CLI to create a design-system drift report before turning rules into hard CI gates:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
npx rhythmguard audit ./src
|
|
91
|
-
npx rhythmguard audit ./src --format markdown
|
|
92
|
-
npx rhythmguard audit ./src --json
|
|
93
|
-
npx rhythmguard audit . --ignore "apps/legacy/**" --ignore "vendor/**"
|
|
94
|
-
npx rhythmguard audit ./src --write-baseline
|
|
95
|
-
npx rhythmguard audit ./src --since-baseline --fail-on-new-drift
|
|
96
|
-
npx rhythmguard audit ./src --staged --max-findings 0
|
|
97
|
-
npx rhythmguard audit ./src --token-source ./tokens.json
|
|
98
|
-
npx rhythmguard audit ./src --token-source ./theme.css --token-source-format css
|
|
99
|
-
npx rhythmguard audit ./src --include-motion
|
|
100
|
-
npx rhythmguard audit ./src --format html --output rhythmguard-report.html
|
|
101
|
-
npx rhythmguard audit --schema
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
The report covers authored CSS declarations, Tailwind arbitrary spacing values in common template/source files, and token-contract drift such as missing spacing tokens, unused spacing tokens, repeated raw values that deserve token review, raw values that match known tokens, conflicting token values, and opt-in motion rhythm drift. Scan paths are scoped to the directory argument. Use `--ignore`, `.rhythmguardignore`, or `--ignore-path` for generated or legacy subtrees, then add baselines and CI thresholds when you are ready to gate new drift. Markdown output is PR-ready for UX developers, UX designers, and design-system owners:
|
|
105
|
-
|
|
106
|
-
```md
|
|
107
|
-
# Rhythmguard Design-System Audit
|
|
108
|
-
|
|
109
|
-
| Metric | Value |
|
|
110
|
-
| --- | ---: |
|
|
111
|
-
| CSS files scanned | 47 |
|
|
112
|
-
| Template files scanned | 83 |
|
|
113
|
-
| Files with issues | 12 |
|
|
114
|
-
| Total findings | 52 |
|
|
115
|
-
| Scale cleanliness | 91% |
|
|
116
|
-
| New findings | 3 |
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### Audit config and external token sources
|
|
120
|
-
|
|
121
|
-
For large codebases, put shared audit settings in `.rhythmguardrc.json`:
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"audit": {
|
|
126
|
-
"ignore": ["legacy/**", "generated/**"],
|
|
127
|
-
"tokenSources": [
|
|
128
|
-
"./tokens.json",
|
|
129
|
-
{ "path": "./src/theme.css", "format": "css" }
|
|
130
|
-
],
|
|
131
|
-
"tokenKind": "spacing",
|
|
132
|
-
"includeMotion": false,
|
|
133
|
-
"tokenCandidateMinCount": 2,
|
|
134
|
-
"minCleanliness": 90
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
`rhythmguard audit` loads `.rhythmguardrc.json` automatically when present. Use `--config <file>` for another config, `--no-config` to skip config discovery, and `--token-source <file>` for extra canonical token files. Token source paths in config files resolve from the config file directory; CLI token source paths resolve from the current working directory. Supported source formats are CSS custom properties and Tailwind v4 `@theme`, flat JSON maps, Style Dictionary JSON, and DTCG JSON.
|
|
140
|
-
|
|
141
|
-
### Audit JSON 2.0 and API
|
|
142
|
-
|
|
143
|
-
In Rhythmguard 2.0, `--format json` emits the stable audit contract:
|
|
144
|
-
|
|
145
|
-
```json
|
|
146
|
-
{
|
|
147
|
-
"schemaVersion": "2.0",
|
|
148
|
-
"command": { "directory": "./src", "scanScope": "full" },
|
|
149
|
-
"summary": { "totalFindings": 12, "scaleCleanliness": 94 },
|
|
150
|
-
"scanned": { "cssFiles": 10, "templateFiles": 20 },
|
|
151
|
-
"contracts": {
|
|
152
|
-
"scale": {},
|
|
153
|
-
"tokens": {},
|
|
154
|
-
"motion": {}
|
|
155
|
-
},
|
|
156
|
-
"findings": {
|
|
157
|
-
"css": [],
|
|
158
|
-
"tailwind": [],
|
|
159
|
-
"motion": []
|
|
160
|
-
},
|
|
161
|
-
"baseline": null
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
Use `--format json-v1` for the pre-2.0 JSON shape during migration.
|
|
166
|
-
|
|
167
|
-
Programmatic usage:
|
|
168
|
-
|
|
169
|
-
```js
|
|
170
|
-
const {
|
|
171
|
-
createAuditReport,
|
|
172
|
-
toAuditContractReport,
|
|
173
|
-
} = require('stylelint-plugin-rhythmguard/audit');
|
|
174
|
-
|
|
175
|
-
const report = await createAuditReport({ dir: './src', noConfig: true });
|
|
176
|
-
const contract = toAuditContractReport(report);
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Installation
|
|
24
|
+
No install, no config. It detects your stack and token files, infers your spacing scale from your own tokens, audits the current directory, and prints the exact `.stylelintrc.json` (and ESLint snippet for Tailwind) to paste. Then:
|
|
180
25
|
|
|
181
26
|
```bash
|
|
182
27
|
npm install --save-dev stylelint stylelint-plugin-rhythmguard
|
|
183
28
|
```
|
|
184
29
|
|
|
185
|
-
## Drop-In for Existing Projects (Recommended)
|
|
186
|
-
|
|
187
|
-
If your project already uses Stylelint, you only need one command and one config block:
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
npm install --save-dev stylelint-plugin-rhythmguard
|
|
191
|
-
```
|
|
192
|
-
|
|
193
30
|
```json
|
|
194
31
|
{
|
|
195
32
|
"extends": ["stylelint-plugin-rhythmguard/configs/recommended"]
|
|
196
33
|
}
|
|
197
34
|
```
|
|
198
35
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
### Tailwind config
|
|
36
|
+
That enables `rhythmguard/use-scale` on spacing properties with the default 4px scale. Tailwind projects use the `tailwind` config instead, which also extracts spacing tokens from `@theme`:
|
|
202
37
|
|
|
203
38
|
```json
|
|
204
39
|
{
|
|
@@ -206,628 +41,70 @@ npm install --save-dev stylelint-plugin-rhythmguard
|
|
|
206
41
|
}
|
|
207
42
|
```
|
|
208
43
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
```json
|
|
212
|
-
{
|
|
213
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/recommended"]
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Strict config
|
|
218
|
-
|
|
219
|
-
```json
|
|
220
|
-
{
|
|
221
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/strict"]
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
`strict` intentionally delegates transform translation enforcement to `rhythmguard/no-offscale-transform` to reduce overlapping warnings from `use-scale`.
|
|
226
|
-
|
|
227
|
-
### Expanded config
|
|
228
|
-
|
|
229
|
-
```json
|
|
230
|
-
{
|
|
231
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/expanded"]
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
`expanded` enables scale enforcement for spacing + radius + typography + size property groups.
|
|
236
|
-
|
|
237
|
-
### Logical config
|
|
238
|
-
|
|
239
|
-
```json
|
|
240
|
-
{
|
|
241
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/logical"]
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
`logical` composes Rhythmguard strict mode with `stylelint-plugin-logical-css` recommended rules.
|
|
246
|
-
|
|
247
|
-
### Migration config
|
|
248
|
-
|
|
249
|
-
```json
|
|
250
|
-
{
|
|
251
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/migration"]
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
`migration` keeps on-scale numeric values temporarily while auto-building token mappings from CSS custom properties and optional Tailwind spacing config.
|
|
256
|
-
|
|
257
|
-
### React / Next.js + Tailwind config
|
|
258
|
-
|
|
259
|
-
```json
|
|
260
|
-
{
|
|
261
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/react-tailwind"]
|
|
262
|
-
}
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
`react-tailwind` extends the tailwind config with CSS Modules overrides (spacing + radius enforcement) and ignores Next.js build directories.
|
|
266
|
-
|
|
267
|
-
### Motion config
|
|
268
|
-
|
|
269
|
-
```json
|
|
270
|
-
{
|
|
271
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/motion"]
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
`motion` enables opt-in duration/delay rhythm checks with `rhythmguard/use-motion-scale`.
|
|
276
|
-
|
|
277
|
-
Stable shared config entry points:
|
|
278
|
-
|
|
279
|
-
- `stylelint-plugin-rhythmguard/configs/recommended`
|
|
280
|
-
- `stylelint-plugin-rhythmguard/configs/strict`
|
|
281
|
-
- `stylelint-plugin-rhythmguard/configs/tailwind`
|
|
282
|
-
- `stylelint-plugin-rhythmguard/configs/react-tailwind`
|
|
283
|
-
- `stylelint-plugin-rhythmguard/configs/expanded`
|
|
284
|
-
- `stylelint-plugin-rhythmguard/configs/logical`
|
|
285
|
-
- `stylelint-plugin-rhythmguard/configs/migration`
|
|
286
|
-
- `stylelint-plugin-rhythmguard/configs/motion`
|
|
287
|
-
|
|
288
|
-
Framework-specific setup for Vue, Lit, Astro, and SvelteKit: [`docs/FRAMEWORKS.md`](https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/FRAMEWORKS.md)
|
|
289
|
-
|
|
290
|
-
## Comparison and Migration Recipes
|
|
291
|
-
|
|
292
|
-
- Side-by-side tool fit guide with migration snippets: [`docs/COMPARISON.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/COMPARISON.md)
|
|
293
|
-
- Audit 2.0 validation and roadmap: [`docs/AUDIT_2_VALIDATION.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/AUDIT_2_VALIDATION.md)
|
|
294
|
-
- CI baseline rollout recipe: [`docs/CI_ADOPTION.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/CI_ADOPTION.md)
|
|
295
|
-
- Programmatic dashboard and Figma-friendly export examples: [`docs/AUDIT_API_EXAMPLES.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/AUDIT_API_EXAMPLES.md)
|
|
296
|
-
- Real-world before/after excerpts from public repos: [`docs/ADOPTION_DIFFS.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/ADOPTION_DIFFS.md)
|
|
297
|
-
- Distribution submissions to Stylelint discovery surfaces: [`docs/DISTRIBUTION.md`](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/blob/main/docs/DISTRIBUTION.md)
|
|
298
|
-
|
|
299
|
-
### Full custom setup
|
|
300
|
-
|
|
301
|
-
```json
|
|
302
|
-
{
|
|
303
|
-
"plugins": ["stylelint-plugin-rhythmguard"],
|
|
304
|
-
"rules": {
|
|
305
|
-
"rhythmguard/use-scale": [
|
|
306
|
-
true,
|
|
307
|
-
{
|
|
308
|
-
"preset": "rhythmic-4",
|
|
309
|
-
"propertyGroups": ["spacing", "radius"],
|
|
310
|
-
"propertyScales": {
|
|
311
|
-
"font-size": [12, 14, 16, 20, 24]
|
|
312
|
-
},
|
|
313
|
-
"units": ["px", "rem", "em"],
|
|
314
|
-
"unitStrategy": "convert",
|
|
315
|
-
"baseFontSize": 16,
|
|
316
|
-
"tokenPattern": "^--space-",
|
|
317
|
-
"tokenFunctions": ["var", "theme", "token"],
|
|
318
|
-
"allowNegative": true,
|
|
319
|
-
"allowPercentages": true,
|
|
320
|
-
"fixToScale": true,
|
|
321
|
-
"enforceInsideMathFunctions": true,
|
|
322
|
-
"mathFunctionArguments": {
|
|
323
|
-
"clamp": [1, 3]
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
],
|
|
327
|
-
"rhythmguard/prefer-token": [
|
|
328
|
-
true,
|
|
329
|
-
{
|
|
330
|
-
"tokenPattern": "^--space-",
|
|
331
|
-
"allowNumericScale": false,
|
|
332
|
-
"tokenMapFromCssCustomProperties": true,
|
|
333
|
-
"tokenMapFromTailwindSpacing": true,
|
|
334
|
-
"tailwindConfigPath": "./tailwind.config.mjs",
|
|
335
|
-
"tokenMap": {
|
|
336
|
-
"4px": "var(--space-1)",
|
|
337
|
-
"8px": "var(--space-2)",
|
|
338
|
-
"12px": "var(--space-3)",
|
|
339
|
-
"16px": "var(--space-4)"
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
],
|
|
343
|
-
"rhythmguard/no-offscale-transform": [
|
|
344
|
-
true,
|
|
345
|
-
{
|
|
346
|
-
"scale": [0, 4, 8, 12, 16, 24, 32]
|
|
347
|
-
}
|
|
348
|
-
]
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Presets and custom scales
|
|
354
|
-
|
|
355
|
-
Preset-based setup:
|
|
356
|
-
|
|
357
|
-
```json
|
|
358
|
-
{
|
|
359
|
-
"rules": {
|
|
360
|
-
"rhythmguard/use-scale": [true, { "preset": "fibonacci" }]
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
Custom scale setup:
|
|
366
|
-
|
|
367
|
-
```json
|
|
368
|
-
{
|
|
369
|
-
"rules": {
|
|
370
|
-
"rhythmguard/use-scale": [true, { "customScale": [0, 6, 12, 18, 24, 36, 48] }]
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Scale resolution precedence:
|
|
376
|
-
|
|
377
|
-
1. `customScale` (highest priority)
|
|
378
|
-
2. `scale`
|
|
379
|
-
3. `preset`
|
|
380
|
-
4. default `rhythmic-4` scale
|
|
381
|
-
|
|
382
|
-
## Option Validation
|
|
383
|
-
|
|
384
|
-
Rhythmguard validates `secondaryOptions` for each rule before linting declarations.
|
|
385
|
-
|
|
386
|
-
- Unknown option names fail fast with Stylelint invalid option warnings.
|
|
387
|
-
- Invalid option shapes fail fast (for example string vs array mismatches).
|
|
388
|
-
- `properties` string entries are validated against supported scale-targetable CSS property names.
|
|
389
|
-
- `propertyGroups` values are validated against built-in groups: `spacing`, `radius`, `typography`, and `size`.
|
|
390
|
-
- Math function argument maps are validated per function (`calc`, `clamp`, `min`, `max`) and positive 1-based argument indexes.
|
|
391
|
-
|
|
392
|
-
Example typo that now fails immediately:
|
|
393
|
-
|
|
394
|
-
```json
|
|
395
|
-
{
|
|
396
|
-
"rules": {
|
|
397
|
-
"rhythmguard/use-scale": [true, { "sevverity": "warning" }]
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
## Built-in Scale Presets
|
|
403
|
-
|
|
404
|
-
| Preset | Pattern | Scale |
|
|
405
|
-
| --- | --- | --- |
|
|
406
|
-
| `rhythmic-4` | 4pt rhythm | `[0,4,8,12,16,24,32,40,48,64]` |
|
|
407
|
-
| `rhythmic-8` | 8pt rhythm | `[0,8,16,24,32,40,48,64,80,96]` |
|
|
408
|
-
| `product-material-8dp` | Material 8dp baseline + 4dp increments | `[0,4,8,12,16,24,32,40,48,56,64,72,80]` |
|
|
409
|
-
| `product-atlassian-8px` | Atlassian-like product spacing progression | `[0,2,4,6,8,12,16,20,24,32,40,48,64,80]` |
|
|
410
|
-
| `product-carbon-2x` | Carbon 2x spacing progression | `[0,2,4,8,12,16,24,32,40,48,64,80]` |
|
|
411
|
-
| `editorial-baseline-4` | editorial baseline rhythm at 4-unit cadence | `[0,4,8,12,16,20,24,28,32,40,48,56,64]` |
|
|
412
|
-
| `editorial-baseline-6` | editorial baseline rhythm at 6-unit cadence | `[0,6,12,18,24,30,36,48,60,72]` |
|
|
413
|
-
| `compact` | dense UI spacing | `[0,2,4,6,8,12,16,20,24,32]` |
|
|
414
|
-
| `fibonacci` | Fibonacci progression | `[0,2,3,5,8,13,21,34,55,89]` |
|
|
415
|
-
| `powers-of-two` | geometric doubling | `[0,2,4,8,16,32,64,128]` |
|
|
416
|
-
| `golden-ratio` | ratio 1.618 | generated modular sequence |
|
|
417
|
-
| `modular-major-second` | ratio 1.125 | generated modular sequence |
|
|
418
|
-
| `modular-minor-third` | ratio 1.2 | generated modular sequence |
|
|
419
|
-
| `modular-major-third` | ratio 1.25 | generated modular sequence |
|
|
420
|
-
| `modular-augmented-fourth` | ratio 1.414 | generated modular sequence |
|
|
421
|
-
| `modular-perfect-fourth` | ratio 1.333 | generated modular sequence |
|
|
422
|
-
| `modular-perfect-fifth` | ratio 1.5 | generated modular sequence |
|
|
423
|
-
|
|
424
|
-
Aliases:
|
|
425
|
-
|
|
426
|
-
- `4pt` → `rhythmic-4`
|
|
427
|
-
- `8pt` → `rhythmic-8`
|
|
428
|
-
- `material` → `product-material-8dp`
|
|
429
|
-
- `atlassian-8` → `product-atlassian-8px`
|
|
430
|
-
- `carbon` → `product-carbon-2x`
|
|
431
|
-
- `baseline-4` → `editorial-baseline-4`
|
|
432
|
-
- `baseline-6` → `editorial-baseline-6`
|
|
433
|
-
- `golden` → `golden-ratio`
|
|
434
|
-
- `major-second` → `modular-major-second`
|
|
435
|
-
- `minor-third` → `modular-minor-third`
|
|
436
|
-
- `major-third` → `modular-major-third`
|
|
437
|
-
- `augmented-fourth` → `modular-augmented-fourth`
|
|
438
|
-
- `perfect-fourth` → `modular-perfect-fourth`
|
|
439
|
-
- `perfect-fifth` → `modular-perfect-fifth`
|
|
440
|
-
|
|
441
|
-
### Preset Rationale
|
|
442
|
-
|
|
443
|
-
- Product presets are based on widely-used design-system spacing frameworks.
|
|
444
|
-
- Editorial presets model baseline-grid cadence used in long-form typography and column layouts.
|
|
445
|
-
- Theory presets expose mathematically-derived modular scales from design theory and typographic proportion systems.
|
|
446
|
-
- Full research notes and sources are documented in [`docs/SCALE_RESEARCH.md`](https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/SCALE_RESEARCH.md).
|
|
447
|
-
|
|
448
|
-
## Community Scale Registry
|
|
449
|
-
|
|
450
|
-
Rhythmguard supports community-contributed scale presets from `scales/community/*.json`.
|
|
451
|
-
|
|
452
|
-
### Current community scales
|
|
453
|
-
|
|
454
|
-
| Preset | Base | Pattern | Contributor |
|
|
455
|
-
| --- | --- | --- | --- |
|
|
456
|
-
| `product-decimal-10` | `10` | Decimal-friendly dashboard/product cadence | [Petri Lahdelma](https://github.com/PetriLahdelma) |
|
|
457
|
-
|
|
458
|
-
### Contribute a scale
|
|
459
|
-
|
|
460
|
-
1. Scaffold a new scale file:
|
|
461
|
-
|
|
462
|
-
```bash
|
|
463
|
-
npm run scales:add -- --name my-team-scale --base 8 --steps 0,4,8,12,16,24,32
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
2. Validate:
|
|
467
|
-
|
|
468
|
-
```bash
|
|
469
|
-
npm run scales:validate
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
3. Open a PR with your scale JSON.
|
|
473
|
-
|
|
474
|
-
Full specification and policy: [`docs/COMMUNITY_SCALES.md`](https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/COMMUNITY_SCALES.md).
|
|
475
|
-
|
|
476
|
-
If your scale is private or very niche, keep it in your project config with `customScale` instead of contributing it to the shared registry.
|
|
477
|
-
|
|
478
|
-
## Rule Details
|
|
479
|
-
|
|
480
|
-
### `rhythmguard/use-scale`
|
|
481
|
-
|
|
482
|
-
Enforces spacing literals to stay on a configured numeric scale.
|
|
483
|
-
|
|
484
|
-
Checks:
|
|
485
|
-
|
|
486
|
-
- `margin*`, `padding*`
|
|
487
|
-
- `gap`, `row-gap`, `column-gap`
|
|
488
|
-
- `inset*`, `scroll-margin*`, `scroll-padding*`
|
|
489
|
-
- `translate`, `translate-x`, `translate-y`, `translate-z`
|
|
490
|
-
- `transform` translation functions (`translate`, `translateX`, `translateY`, `translateZ`, `translate3d`)
|
|
491
|
-
- optional property groups:
|
|
492
|
-
- `radius` (`border-radius*`, corner radii, `outline-offset`)
|
|
493
|
-
- `typography` (`font-size`, `line-height`, `letter-spacing`, `word-spacing`)
|
|
494
|
-
- `size` (`width`, `height`, min/max size, logical `inline-size`/`block-size`)
|
|
495
|
-
|
|
496
|
-
Example:
|
|
497
|
-
|
|
498
|
-
```css
|
|
499
|
-
/* ❌ Off-scale */
|
|
500
|
-
.card {
|
|
501
|
-
margin: 13px;
|
|
502
|
-
transform: translateY(18px);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/* ✅ On-scale */
|
|
506
|
-
.card {
|
|
507
|
-
margin: 12px;
|
|
508
|
-
transform: translateY(16px);
|
|
509
|
-
}
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
Options:
|
|
513
|
-
|
|
514
|
-
| Option | Type | Default | Description |
|
|
515
|
-
| --- | --- | --- | --- |
|
|
516
|
-
| `preset` | `string` | `rhythmic-4` | Selects a built-in spacing scale |
|
|
517
|
-
| `customScale` | `Array<number|string>` | `undefined` | Highest-priority custom scale override |
|
|
518
|
-
| `scale` | `Array<number|string>` | `[0,4,8,12,16,24,32,40,48,64]` | Allowed spacing values |
|
|
519
|
-
| `units` | `string[]` | `['px','rem','em']` | Units considered for scale enforcement |
|
|
520
|
-
| `unitStrategy` | `'convert' \| 'exact'` | `'convert'` | `convert`: compare via px conversion (`px/rem/em`). `exact`: compare against same-unit scale values (for example `vw`, `cqi`) |
|
|
521
|
-
| `baseFontSize` | `number` | `16` | Used for `rem`/`em` conversion |
|
|
522
|
-
| `tokenPattern` | `string` | `^--space-` | Regex for accepted token variable names |
|
|
523
|
-
| `tokenFunctions` | `string[]` | `['var','theme','token']` | Functions treated as tokenized values |
|
|
524
|
-
| `allowNegative` | `boolean` | `true` | Allows negative scale values |
|
|
525
|
-
| `allowPercentages` | `boolean` | `true` | Allows `%` values without scale checks |
|
|
526
|
-
| `fixToScale` | `boolean` | `true` | Enables nearest-value autofix |
|
|
527
|
-
| `enforceInsideMathFunctions` | `boolean` | `false` | Lints `calc()/clamp()/min()/max()` internals |
|
|
528
|
-
| `mathFunctionArguments` | `Record<mathFn, number[]>` | `{}` | Restricts linting to specific 1-based argument indexes per math function |
|
|
529
|
-
| `ignoreMathFunctionArguments` | `Record<mathFn, number[]>` | `{}` | Excludes specific 1-based argument indexes per math function |
|
|
530
|
-
| `propertyGroups` | `Array<'spacing' \| 'radius' \| 'typography' \| 'size' \| 'motion'>` | `['spacing']` | Selects built-in property groups when `properties` is not provided |
|
|
531
|
-
| `properties` | `Array<string|RegExp>` | built-in spacing patterns | Override targeted property set; string values may be supported property names or regex-like strings (`/pattern/flags`) |
|
|
532
|
-
| `propertyScales` | `Record<propertyOrRegex, scaleOrPreset>` | `{}` | Per-property scale overrides (supports exact names or `/regex/flags` keys; stateful `g`/`y` flags are normalized for deterministic matching) |
|
|
533
|
-
|
|
534
|
-
### `rhythmguard/prefer-token`
|
|
535
|
-
|
|
536
|
-
Enforces token usage for spacing declarations. This is ideal once your token system is stable.
|
|
537
|
-
|
|
538
|
-
Example:
|
|
539
|
-
|
|
540
|
-
```css
|
|
541
|
-
/* ❌ Raw literals */
|
|
542
|
-
.stack {
|
|
543
|
-
gap: 12px;
|
|
544
|
-
padding: 16px;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/* ✅ Tokenized */
|
|
548
|
-
.stack {
|
|
549
|
-
gap: var(--space-3);
|
|
550
|
-
padding: var(--space-4);
|
|
551
|
-
}
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
Options:
|
|
555
|
-
|
|
556
|
-
| Option | Type | Default | Description |
|
|
557
|
-
| --- | --- | --- | --- |
|
|
558
|
-
| `tokenPattern` | `string` | `^--space-` | Regex for accepted token variable names |
|
|
559
|
-
| `tokenFunctions` | `string[]` | `['var','theme','token']` | Functions treated as tokenized values |
|
|
560
|
-
| `allowNumericScale` | `boolean` | `false` | Temporary migration mode to permit on-scale literals |
|
|
561
|
-
| `preset` | `string` | `rhythmic-4` | Selects a built-in scale used in migration mode |
|
|
562
|
-
| `customScale` | `Array<number|string>` | `undefined` | Highest-priority custom scale override |
|
|
563
|
-
| `scale` | `Array<number|string>` | `[0,4,8,12,16,24,32,40,48,64]` | Used when `allowNumericScale` is enabled |
|
|
564
|
-
| `baseFontSize` | `number` | `16` | Used for scale checks with `rem`/`em` |
|
|
565
|
-
| `unitStrategy` | `'convert' \| 'exact'` | `'convert'` | Matching strategy when `allowNumericScale` is enabled |
|
|
566
|
-
| `units` | `string[]` | `['px','rem','em']` | Units considered for numeric scale checks |
|
|
567
|
-
| `enforceInsideMathFunctions` | `boolean` | `false` | Lints `calc()/clamp()/min()/max()` internals |
|
|
568
|
-
| `mathFunctionArguments` | `Record<mathFn, number[]>` | `{}` | Restricts linting to specific 1-based argument indexes per math function |
|
|
569
|
-
| `ignoreMathFunctionArguments` | `Record<mathFn, number[]>` | `{}` | Excludes specific 1-based argument indexes per math function |
|
|
570
|
-
| `tokenMap` | `Record<string,string>` | `{}` | Enables autofix from raw value to token |
|
|
571
|
-
| `tokenMapFile` | `string` | `null` | JSON file path to merge additional token mappings (supports flat, Style Dictionary, and W3C DTCG formats) |
|
|
572
|
-
| `tokenMapFromCssCustomProperties` | `boolean` | `false` | Auto-builds mappings from matching custom property declarations in the same stylesheet |
|
|
573
|
-
| `tokenMapFromTailwindSpacing` | `boolean` | `false` | Auto-builds mappings from `theme.spacing` and `theme.extend.spacing` in Tailwind config |
|
|
574
|
-
| `tailwindConfigPath` | `string` | `null` | Path to Tailwind config used by `tokenMapFromTailwindSpacing` (`.js`, `.cjs`, `.mjs`) |
|
|
575
|
-
| `ignoreValues` | `string[]` | CSS global keywords + `auto` | Skips keyword literals |
|
|
576
|
-
| `propertyGroups` | `Array<'spacing' \| 'radius' \| 'typography' \| 'size'>` | `['spacing']` | Selects built-in property groups when `properties` is not provided |
|
|
577
|
-
| `properties` | `Array<string|RegExp>` | built-in spacing patterns | Override targeted property set; string values may be supported property names or regex-like strings (`/pattern/flags`) |
|
|
578
|
-
| `propertyScales` | `Record<propertyOrRegex, scaleOrPreset>` | `{}` | Per-property scale overrides for numeric migration mode (stateful `g`/`y` flags are normalized for deterministic matching) |
|
|
579
|
-
|
|
580
|
-
### `rhythmguard/no-offscale-transform`
|
|
581
|
-
|
|
582
|
-
Specialized guardrail for motion spacing consistency in translation transforms.
|
|
583
|
-
|
|
584
|
-
Example:
|
|
585
|
-
|
|
586
|
-
```css
|
|
587
|
-
/* ❌ Off-scale motion */
|
|
588
|
-
.toast {
|
|
589
|
-
transform: translateY(18px) scale(1);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/* ✅ Motion on spacing scale */
|
|
593
|
-
.toast {
|
|
594
|
-
transform: translateY(16px) scale(1);
|
|
595
|
-
}
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
Options:
|
|
599
|
-
|
|
600
|
-
`rhythmguard/no-offscale-transform` accepts the same scale options as `rhythmguard/use-scale` (including `unitStrategy`, math argument targeting, and deterministic autofix), but only for transform translation properties. Its secondary options are also validated for unknown keys and invalid value shapes.
|
|
601
|
-
|
|
602
|
-
### `rhythmguard/use-motion-scale`
|
|
603
|
-
|
|
604
|
-
Opt-in guardrail for duration, delay, and easing rhythm.
|
|
605
|
-
|
|
606
|
-
Example:
|
|
607
|
-
|
|
608
|
-
```css
|
|
609
|
-
/* ❌ Off-scale timing + raw easing */
|
|
610
|
-
.button {
|
|
611
|
-
transition: opacity 175ms cubic-bezier(.2, 0, 0, 1);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/* ✅ Timing on motion scale */
|
|
615
|
-
.button {
|
|
616
|
-
transition: opacity 150ms var(--ease-snappy);
|
|
617
|
-
}
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
Options:
|
|
621
|
-
|
|
622
|
-
| Option | Type | Default | Description |
|
|
623
|
-
| --- | --- | --- | --- |
|
|
624
|
-
| `durationScale` | `number[]` | `[0,75,100,150,200,300,500,700,1000]` | Allowed duration and delay values in milliseconds |
|
|
625
|
-
| `durationUnits` | `Array<'ms' \| 's'>` | `['ms','s']` | Time units considered by the rule |
|
|
626
|
-
| `fixToScale` | `boolean` | `true` | Autofixes simple duration/delay values to the nearest scale value |
|
|
627
|
-
| `easingTokenMap` | `Record<string,string>` | `{}` | Optional exact replacements for raw easing functions |
|
|
628
|
-
|
|
629
|
-
Tailwind class strings can use the ESLint companion rule `rhythmguard-tailwind/tailwind-class-use-motion-scale` for `duration-[...]`, `delay-[...]`, and `ease-[...]` arbitrary values.
|
|
630
|
-
|
|
631
|
-
## Tailwind CSS Integration
|
|
632
|
-
|
|
633
|
-
Rhythmguard works well in Tailwind projects, but it enforces what Stylelint can parse: CSS declarations.
|
|
634
|
-
|
|
635
|
-
### What Rhythmguard covers in Tailwind projects
|
|
636
|
-
|
|
637
|
-
- custom CSS in `globals.css`, `components.css`, `utilities.css`
|
|
638
|
-
- CSS Modules (for example `*.module.css`)
|
|
639
|
-
- declarations inside `@layer` blocks
|
|
640
|
-
|
|
641
|
-
### Tailwind v4 @theme tokens
|
|
642
|
-
|
|
643
|
-
The `tailwind` config preset automatically extracts spacing tokens from Tailwind v4 `@theme` blocks and uses them for `prefer-token` enforcement. Raw values like `padding: 16px` are autofixed to `padding: var(--spacing-4)`.
|
|
644
|
-
|
|
645
|
-
See [`docs/TAILWIND.md`](https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/TAILWIND.md) for full setup.
|
|
646
|
-
|
|
647
|
-
### What the Stylelint layer does not cover
|
|
648
|
-
|
|
649
|
-
- Tailwind class strings in templates/JSX/TSX, for example:
|
|
650
|
-
- `class="p-4 gap-2"`
|
|
651
|
-
- `class="p-[13px] translate-y-[18px]"`
|
|
652
|
-
|
|
653
|
-
Those are not Stylelint declaration nodes, so they are outside Stylelint rule scope. Use the ESLint companion rule below for scale-aware class-string enforcement.
|
|
654
|
-
|
|
655
|
-
### Companion ESLint layer for class strings
|
|
656
|
-
|
|
657
|
-
Rhythmguard now ships an ESLint companion export for class-string governance:
|
|
44
|
+
For class strings in JSX, TSX, Vue, Svelte or Astro, add the ESLint companion:
|
|
658
45
|
|
|
659
46
|
```js
|
|
660
|
-
// eslint.config.js
|
|
47
|
+
// eslint.config.js
|
|
661
48
|
import rhythmguard from 'stylelint-plugin-rhythmguard/eslint';
|
|
662
49
|
|
|
663
50
|
export default [
|
|
664
51
|
{
|
|
665
|
-
plugins: {
|
|
666
|
-
|
|
667
|
-
},
|
|
668
|
-
rules: {
|
|
669
|
-
'rhythmguard-tailwind/tailwind-class-use-scale': ['error', { scale: [0, 4, 8, 12, 16, 24, 32] }],
|
|
670
|
-
},
|
|
52
|
+
plugins: { 'rhythmguard-tailwind': rhythmguard },
|
|
53
|
+
rules: { 'rhythmguard-tailwind/tailwind-class-use-scale': ['error', { scale: [0, 4, 8, 12, 16, 24, 32] }] },
|
|
671
54
|
},
|
|
672
55
|
];
|
|
673
56
|
```
|
|
674
57
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
#### Supported patterns
|
|
678
|
-
|
|
679
|
-
The rule checks every string literal in your code, so it works automatically with common utility functions:
|
|
680
|
-
|
|
681
|
-
- `cn("p-[13px]")` / `cn("p-[13px]", condition && "m-[7px]")`
|
|
682
|
-
- `clsx("p-[13px]", "gap-[18px]")`
|
|
683
|
-
- `twMerge("p-[13px]", otherClasses)`
|
|
684
|
-
- `cva("base", { variants: { size: { sm: "p-[5px]" } } })`
|
|
685
|
-
- `<div className={cn("p-[13px]")} />`
|
|
686
|
-
|
|
687
|
-
No extra config needed — if the string contains an arbitrary spacing value, it gets caught and autofixed.
|
|
688
|
-
|
|
689
|
-
### Recommended stack for full Tailwind enforcement
|
|
690
|
-
|
|
691
|
-
Use both layers:
|
|
692
|
-
|
|
693
|
-
1. Stylelint + Rhythmguard for CSS declaration governance.
|
|
694
|
-
2. Tailwind-aware class-string linting/formatting for template utility usage.
|
|
695
|
-
|
|
696
|
-
Suggested setup:
|
|
697
|
-
|
|
698
|
-
```json
|
|
699
|
-
{
|
|
700
|
-
"extends": ["stylelint-plugin-rhythmguard/configs/tailwind"]
|
|
701
|
-
}
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
Then pair with:
|
|
705
|
-
|
|
706
|
-
- `stylelint-plugin-rhythmguard/eslint` for arbitrary spacing class-string scale enforcement.
|
|
707
|
-
- `eslint-plugin-tailwindcss` for broader class-string linting and conventions. If your policy is to ban every arbitrary value, enable its `tailwindcss/no-arbitrary-value` rule; use Rhythmguard when you want spacing-specific scale checks and nearest-value fixes.
|
|
708
|
-
- `prettier-plugin-tailwindcss` for deterministic class ordering.
|
|
709
|
-
|
|
710
|
-
Detailed setup reference: [`docs/TAILWIND.md`](https://github.com/PetriLahdelma/stylelint-plugin-rhythmguard/blob/main/docs/TAILWIND.md).
|
|
711
|
-
|
|
712
|
-
### Tailwind token function support
|
|
713
|
-
|
|
714
|
-
By default, `tokenFunctions` includes `theme`, so values like `theme(spacing.4)` are treated as tokenized values.
|
|
715
|
-
|
|
716
|
-
This keeps CSS declaration enforcement and template class-string enforcement separated but coordinated.
|
|
717
|
-
|
|
718
|
-
## Programmatic Presets
|
|
719
|
-
|
|
720
|
-
```js
|
|
721
|
-
const rhythmguard = require('stylelint-plugin-rhythmguard');
|
|
722
|
-
|
|
723
|
-
console.log(rhythmguard.presets.listScalePresetNames());
|
|
724
|
-
console.log(rhythmguard.presets.listCommunityScalePresetNames());
|
|
725
|
-
console.log(rhythmguard.presets.getCommunityScaleMetadata('product-decimal-10'));
|
|
726
|
-
console.log(rhythmguard.presets.scales['rhythmic-4']);
|
|
727
|
-
console.log(Object.keys(rhythmguard.eslint.rules));
|
|
728
|
-
```
|
|
729
|
-
|
|
730
|
-
## Token File Formats
|
|
731
|
-
|
|
732
|
-
The `tokenMapFile` option supports multiple JSON formats:
|
|
733
|
-
|
|
734
|
-
**Flat token-to-value:**
|
|
735
|
-
|
|
736
|
-
```json
|
|
737
|
-
{ "--spacing-4": "16px", "--spacing-3": "12px" }
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
**Style Dictionary:**
|
|
741
|
-
|
|
742
|
-
```json
|
|
743
|
-
{ "--spacing-4": { "value": "16px" } }
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
**W3C DTCG (Design Token Community Group):**
|
|
747
|
-
|
|
748
|
-
```json
|
|
749
|
-
{
|
|
750
|
-
"spacing": {
|
|
751
|
-
"4": { "$value": "16px", "$type": "dimension" },
|
|
752
|
-
"2": { "$value": "8px", "$type": "dimension" }
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
Nested DTCG groups are walked recursively. The key path becomes the CSS variable name: `spacing.4` → `var(--spacing-4)`. Non-length values (colors, fonts) are ignored automatically.
|
|
758
|
-
|
|
759
|
-
## Autofix Philosophy
|
|
760
|
-
|
|
761
|
-
Rhythmguard only applies deterministic fixes:
|
|
762
|
-
|
|
763
|
-
- nearest scale value for numeric off-scale literals
|
|
764
|
-
- explicit `tokenMap` replacements for token migration
|
|
765
|
-
|
|
766
|
-
It will not guess token mappings without your map.
|
|
58
|
+
## Rules
|
|
767
59
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
-
|
|
771
|
-
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
```bash
|
|
778
|
-
npm install
|
|
779
|
-
npm run lint
|
|
780
|
-
npm test
|
|
781
|
-
npm run test:coverage
|
|
782
|
-
```
|
|
60
|
+
| Rule | What it reports | Fix |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| [`rhythmguard/use-scale`](docs/rules/use-scale.md) | Length values off the configured scale | Nearest scale value |
|
|
63
|
+
| [`rhythmguard/prefer-token`](docs/rules/prefer-token.md) | Raw literals where a design token exists | Token from your map |
|
|
64
|
+
| [`rhythmguard/no-offscale-transform`](docs/rules/no-offscale-transform.md) | Off-scale `translate*` offsets | Nearest scale value |
|
|
65
|
+
| [`rhythmguard/use-motion-scale`](docs/rules/use-motion-scale.md) | Off-scale durations and raw easing curves. Opt-in, experimental | Nearest duration |
|
|
66
|
+
| [`rhythmguard-tailwind/tailwind-class-use-scale`](docs/rules/tailwind-class-use-scale.md) | Off-scale Tailwind arbitrary spacing values (`p-[13px]`) in class strings | Nearest scale value |
|
|
67
|
+
| [`rhythmguard-tailwind/tailwind-class-use-motion-scale`](docs/rules/tailwind-class-use-motion-scale.md) | Off-scale `duration-[...]`, `delay-[...]`, raw `ease-[...]`. Opt-in | Nearest duration |
|
|
783
68
|
|
|
784
|
-
|
|
69
|
+
Every rule validates its options up front. Unknown option names and wrong shapes are reported, never ignored.
|
|
785
70
|
|
|
786
|
-
|
|
71
|
+
## Configs
|
|
787
72
|
|
|
788
|
-
|
|
789
|
-
npm run bench:perf
|
|
790
|
-
```
|
|
73
|
+
`recommended`, `strict`, `tailwind`, `react-tailwind`, `expanded`, `logical`, `migration`, `motion`, and `embed` for authors of shared configs (see [docs/FOR_CONFIG_AUTHORS.md](docs/FOR_CONFIG_AUTHORS.md)). All are `stylelint-plugin-rhythmguard/configs/<name>`. What each enables, the full custom setup, and the scale-selection precedence are in [docs/CONFIGS.md](docs/CONFIGS.md). Built-in and community scale presets are in [docs/SCALE_PRESETS.md](docs/SCALE_PRESETS.md).
|
|
791
74
|
|
|
792
|
-
|
|
75
|
+
## Audit before you enforce
|
|
793
76
|
|
|
794
77
|
```bash
|
|
795
|
-
|
|
78
|
+
npx rhythmguard audit ./src --format markdown
|
|
79
|
+
npx rhythmguard audit ./src --write-baseline
|
|
80
|
+
npx rhythmguard audit ./src --since-baseline --fail-on-new-drift
|
|
81
|
+
npx rhythmguard audit ./src --format github
|
|
796
82
|
```
|
|
797
83
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
## Article
|
|
84
|
+
The audit scans CSS declarations, Tailwind class strings and your token contract, prints a scale-cleanliness score, and supports baselines so legacy codebases can gate only new drift. Formats: text, Markdown, JSON 2.0, HTML, and GitHub Actions annotations. Full reference in [docs/AUDIT.md](docs/AUDIT.md), rollout recipe in [docs/CI_ADOPTION.md](docs/CI_ADOPTION.md).
|
|
801
85
|
|
|
802
|
-
|
|
86
|
+
`npx rhythmguard init` writes a starter config for your stack. `npx rhythmguard doctor` checks the setup.
|
|
803
87
|
|
|
804
|
-
##
|
|
88
|
+
## Guides
|
|
805
89
|
|
|
806
|
-
|
|
90
|
+
- [Tailwind integration](docs/TAILWIND.md), including v4 `@theme` tokens and what each layer covers
|
|
91
|
+
- [Framework setup](docs/FRAMEWORKS.md) for Vue, Lit, Astro and SvelteKit
|
|
92
|
+
- [Comparison with adjacent plugins](docs/COMPARISON.md) and migration recipes
|
|
93
|
+
- [Real before/after excerpts](docs/ADOPTION_DIFFS.md) from public codebases
|
|
94
|
+
- [For shared-config authors](docs/FOR_CONFIG_AUTHORS.md): the `embed` entry point and how inference works per consumer
|
|
95
|
+
- [For coding agents](docs/FOR_AGENTS.md): a paste-ready `AGENTS.md` block and the commands to run
|
|
96
|
+
- [Quiet benchmark](docs/QUIET_BENCHMARK.md): findings on public design systems, checked on every change
|
|
97
|
+
- [Product direction](docs/STRATEGY_2026-09.md)
|
|
98
|
+
- Browser playground: [petrilahdelma.github.io/stylelint-plugin-rhythmguard](https://petrilahdelma.github.io/stylelint-plugin-rhythmguard/)
|
|
807
99
|
|
|
808
|
-
|
|
809
|
-
- [PetriLahdelma/digitaltableteur](https://github.com/PetriLahdelma/digitaltableteur)
|
|
810
|
-
|
|
811
|
-
Want your team listed here?
|
|
812
|
-
|
|
813
|
-
1. Open an issue with `used-by` in the title.
|
|
814
|
-
2. Include one before/after diff and your Rhythmguard config.
|
|
815
|
-
3. Add migration notes (false positives, rules enabled, rollout phase).
|
|
816
|
-
|
|
817
|
-
## Release Workflow
|
|
100
|
+
## Compatibility
|
|
818
101
|
|
|
819
|
-
|
|
820
|
-
2. `release.yml` runs the Node/Stylelint matrix validation.
|
|
821
|
-
3. A tarball smoke test validates package exports and install behavior.
|
|
822
|
-
4. If `NPM_TOKEN` is configured in repository secrets, the package is published to npm with provenance (`npm publish --provenance`).
|
|
823
|
-
5. If `NPM_TOKEN` is not configured, publish is skipped with an explicit workflow notice.
|
|
824
|
-
6. `post-publish-smoke.yml` verifies the published npm version can be installed and run in a clean project (and skips cleanly if the version is not on npm).
|
|
102
|
+
Stylelint 16 and 17. Node 18.18 or newer for Stylelint 16, Node 20.19 or newer for Stylelint 17. CommonJS and ESM entry points, TypeScript declarations for every export. The CI matrix runs Node 18, 20 and 22 against Stylelint 16.0.0, 16.x and 17.x.
|
|
825
103
|
|
|
826
|
-
##
|
|
104
|
+
## Contributing and support
|
|
827
105
|
|
|
828
|
-
|
|
829
|
-
- Security reports and direct contact: `hello@petrilahdelma.com`
|
|
106
|
+
Development setup, semver policy, benchmarking and the release process are in [CONTRIBUTING.md](CONTRIBUTING.md). Bugs and feature requests: [GitHub issues](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/issues). Security: see [SECURITY.md](SECURITY.md).
|
|
830
107
|
|
|
831
108
|
## License
|
|
832
109
|
|
|
833
|
-
MIT. See [
|
|
110
|
+
MIT. See [LICENSE](./LICENSE).
|