@klodd/ds 4.1.0 → 4.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/package.json +2 -1
- package/stylelint-plugin/index.js +192 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klodd/ds",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Klodd shared design system - tokens, components, JS",
|
|
5
5
|
"main": "css/index.css",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"css/",
|
|
11
11
|
"js/",
|
|
12
12
|
"bin/",
|
|
13
|
+
"stylelint-plugin/",
|
|
13
14
|
"SKILL.md",
|
|
14
15
|
"references/"
|
|
15
16
|
],
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/* @klodd/ds stylelint-plugin
|
|
2
|
+
*
|
|
3
|
+
* Custom design-rule-validation som stylelint-config-standard inte
|
|
4
|
+
* tacker. Tre regler, alla "klodd/"-prefixade:
|
|
5
|
+
*
|
|
6
|
+
* - klodd/no-forbidden-radius:
|
|
7
|
+
* border-radius maste vara en av {var(--radius-4), var(--radius-14),
|
|
8
|
+
* 50%, var(--radius-full), 9999px} per ADR 0013 + DESIGN-LANGUAGE
|
|
9
|
+
* sektion 1 (BR-skala 4 varden).
|
|
10
|
+
*
|
|
11
|
+
* - klodd/no-forbidden-shadow:
|
|
12
|
+
* box-shadow maste vara `none`, `inset ...` (focus-rings),
|
|
13
|
+
* `0 0 0 Npx var(--accent-aN)` (focus-rings via spread), eller
|
|
14
|
+
* `var(--shadow-card)` per regel 10 + DESIGN-LANGUAGE sektion 2.
|
|
15
|
+
*
|
|
16
|
+
* - klodd/no-forbidden-padding:
|
|
17
|
+
* padding-varden maste anvanda var(--space-N) dar N matchar en
|
|
18
|
+
* existerande token i 00-primitives.css. Hardkodade px/em/%-
|
|
19
|
+
* varden (utom 0/auto) flaggas. Per-komponent-typ-disciplin
|
|
20
|
+
* (list-row 10/0, banner 14/16 etc) ar fortfarande mansklig
|
|
21
|
+
* review mot ADR 0017-tabellen - plugin validerar bara token-
|
|
22
|
+
* existens, inte semantisk val per komponent.
|
|
23
|
+
*
|
|
24
|
+
* Konfiguration i app-repos:
|
|
25
|
+
* .stylelintrc.json:
|
|
26
|
+
* {
|
|
27
|
+
* "plugins": ["@klodd/ds/stylelint-plugin"],
|
|
28
|
+
* "rules": {
|
|
29
|
+
* "klodd/no-forbidden-radius": true,
|
|
30
|
+
* "klodd/no-forbidden-shadow": true,
|
|
31
|
+
* "klodd/no-forbidden-padding": true
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const stylelint = require('stylelint');
|
|
37
|
+
|
|
38
|
+
const ALLOWED_RADIUS = new Set([
|
|
39
|
+
'0',
|
|
40
|
+
'50%',
|
|
41
|
+
'9999px',
|
|
42
|
+
'var(--radius-4)',
|
|
43
|
+
'var(--radius-14)',
|
|
44
|
+
'var(--radius-full)',
|
|
45
|
+
'inherit',
|
|
46
|
+
'initial',
|
|
47
|
+
'unset',
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
// Tillatna --space-N tokens enligt 00-primitives.css.
|
|
51
|
+
// Komponent-padding bor halla sig inom 4-24-skalan per ADR 0017 men
|
|
52
|
+
// mini-elements (badge, pill-nav) far anvanda --space-2 och
|
|
53
|
+
// layout-section-padding far anvanda --space-28/-32/-40+. Pluginet
|
|
54
|
+
// validerar bara att tokenen EXISTERAR - per-komponent-typ-disciplin
|
|
55
|
+
// ar fortfarande mansklig review mot ADR 0017-tabellen.
|
|
56
|
+
const ALLOWED_PADDING_TOKENS = new Set([
|
|
57
|
+
'2', '4', '6', '8', '10', '12', '14', '16', '18', '20',
|
|
58
|
+
'22', '24', '28', '32', '40', '48', '56', '64', '80',
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const SHADOW_NONE = /^none$/i;
|
|
62
|
+
const SHADOW_INSET = /\binset\b/i;
|
|
63
|
+
const SHADOW_FOCUS_RING = /^0\s+0\s+0\s+\d+px\s+(var\(--accent-a\d+\)|color-mix\(.+\))/i;
|
|
64
|
+
const SHADOW_CARD_TOKEN = /^var\(--shadow-card\)$/i;
|
|
65
|
+
|
|
66
|
+
const RADIUS_RULE = 'klodd/no-forbidden-radius';
|
|
67
|
+
const SHADOW_RULE = 'klodd/no-forbidden-shadow';
|
|
68
|
+
const PADDING_RULE = 'klodd/no-forbidden-padding';
|
|
69
|
+
|
|
70
|
+
const messages = {
|
|
71
|
+
radius: stylelint.utils.ruleMessages(RADIUS_RULE, {
|
|
72
|
+
forbidden: (value) =>
|
|
73
|
+
`Forbjudet border-radius "${value}" - anvand en av: var(--radius-4) | var(--radius-14) | 50% | var(--radius-full) (ADR 0013 + DESIGN-LANGUAGE 1)`,
|
|
74
|
+
}),
|
|
75
|
+
shadow: stylelint.utils.ruleMessages(SHADOW_RULE, {
|
|
76
|
+
forbidden: (value) =>
|
|
77
|
+
`Forbjudet box-shadow "${value}" - tillatet: none | inset focus-rings | var(--shadow-card) (DESIGN-LANGUAGE 2 + regel 10)`,
|
|
78
|
+
}),
|
|
79
|
+
padding: stylelint.utils.ruleMessages(PADDING_RULE, {
|
|
80
|
+
forbidden: (value) =>
|
|
81
|
+
`Forbjudet padding-varde "${value}" - anvand var(--space-N) (existerande token i 00-primitives.css). Hardkodade px-varden inte tillatna.`,
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function checkRadius (root, result, primary) {
|
|
86
|
+
if (!primary) return;
|
|
87
|
+
root.walkDecls(/^border(-(top|right|bottom|left))?-?radius$/, (decl) => {
|
|
88
|
+
const values = decl.value.split(/\s+/);
|
|
89
|
+
values.forEach((v) => {
|
|
90
|
+
const normalized = v.trim();
|
|
91
|
+
if (!normalized) return;
|
|
92
|
+
if (ALLOWED_RADIUS.has(normalized)) return;
|
|
93
|
+
stylelint.utils.report({
|
|
94
|
+
ruleName: RADIUS_RULE,
|
|
95
|
+
result,
|
|
96
|
+
node: decl,
|
|
97
|
+
message: messages.radius.forbidden(normalized),
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function checkShadow (root, result, primary) {
|
|
104
|
+
if (!primary) return;
|
|
105
|
+
root.walkDecls(/^box-shadow$/, (decl) => {
|
|
106
|
+
const value = decl.value.trim();
|
|
107
|
+
if (SHADOW_NONE.test(value)) return;
|
|
108
|
+
if (SHADOW_INSET.test(value)) return;
|
|
109
|
+
if (SHADOW_FOCUS_RING.test(value)) return;
|
|
110
|
+
if (SHADOW_CARD_TOKEN.test(value)) return;
|
|
111
|
+
stylelint.utils.report({
|
|
112
|
+
ruleName: SHADOW_RULE,
|
|
113
|
+
result,
|
|
114
|
+
node: decl,
|
|
115
|
+
message: messages.shadow.forbidden(value),
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function checkPadding (root, result, primary) {
|
|
121
|
+
if (!primary) return;
|
|
122
|
+
root.walkDecls(/^padding(-(top|right|bottom|left))?$/, (decl) => {
|
|
123
|
+
const values = decl.value.split(/\s+/);
|
|
124
|
+
values.forEach((v) => {
|
|
125
|
+
const normalized = v.trim();
|
|
126
|
+
if (!normalized) return;
|
|
127
|
+
if (normalized === '0' || normalized === 'auto' || normalized === 'inherit') return;
|
|
128
|
+
// var(--space-N)?
|
|
129
|
+
const tokenMatch = normalized.match(/^var\(--space-(\d+)\)$/);
|
|
130
|
+
if (tokenMatch) {
|
|
131
|
+
if (ALLOWED_PADDING_TOKENS.has(tokenMatch[1])) return;
|
|
132
|
+
stylelint.utils.report({
|
|
133
|
+
ruleName: PADDING_RULE,
|
|
134
|
+
result,
|
|
135
|
+
node: decl,
|
|
136
|
+
message: messages.padding.forbidden(normalized),
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Hardkodad px / em / % - flagga
|
|
141
|
+
if (/^[\d.]+(px|em|rem|%)$/.test(normalized)) {
|
|
142
|
+
stylelint.utils.report({
|
|
143
|
+
ruleName: PADDING_RULE,
|
|
144
|
+
result,
|
|
145
|
+
node: decl,
|
|
146
|
+
message: messages.padding.forbidden(normalized),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const radiusPlugin = stylelint.createPlugin(RADIUS_RULE, (primary) => {
|
|
154
|
+
return (root, result) => {
|
|
155
|
+
const validOptions = stylelint.utils.validateOptions(result, RADIUS_RULE, {
|
|
156
|
+
actual: primary,
|
|
157
|
+
possible: [true, false],
|
|
158
|
+
});
|
|
159
|
+
if (!validOptions) return;
|
|
160
|
+
checkRadius(root, result, primary);
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
radiusPlugin.ruleName = RADIUS_RULE;
|
|
164
|
+
radiusPlugin.messages = messages.radius;
|
|
165
|
+
|
|
166
|
+
const shadowPlugin = stylelint.createPlugin(SHADOW_RULE, (primary) => {
|
|
167
|
+
return (root, result) => {
|
|
168
|
+
const validOptions = stylelint.utils.validateOptions(result, SHADOW_RULE, {
|
|
169
|
+
actual: primary,
|
|
170
|
+
possible: [true, false],
|
|
171
|
+
});
|
|
172
|
+
if (!validOptions) return;
|
|
173
|
+
checkShadow(root, result, primary);
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
shadowPlugin.ruleName = SHADOW_RULE;
|
|
177
|
+
shadowPlugin.messages = messages.shadow;
|
|
178
|
+
|
|
179
|
+
const paddingPlugin = stylelint.createPlugin(PADDING_RULE, (primary) => {
|
|
180
|
+
return (root, result) => {
|
|
181
|
+
const validOptions = stylelint.utils.validateOptions(result, PADDING_RULE, {
|
|
182
|
+
actual: primary,
|
|
183
|
+
possible: [true, false],
|
|
184
|
+
});
|
|
185
|
+
if (!validOptions) return;
|
|
186
|
+
checkPadding(root, result, primary);
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
paddingPlugin.ruleName = PADDING_RULE;
|
|
190
|
+
paddingPlugin.messages = messages.padding;
|
|
191
|
+
|
|
192
|
+
module.exports = [radiusPlugin, shadowPlugin, paddingPlugin];
|