@klodd/ds 4.0.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/css/utilities.css CHANGED
@@ -140,6 +140,14 @@
140
140
  color: var(--text-disabled);
141
141
  }
142
142
 
143
+ /* SVG-ikoner i text-flow behover subtil baseline-shift sa de inte
144
+ hangr ovanfor x-height. Sprint 50 (CSP A+) raderade inline
145
+ style="vertical-align: -2px"; denna utility replikerar effekten
146
+ utan style-attribute. Lyft fran Ekonom v4.1.0 (Sprint 4 cleanup). */
147
+ .icon-baseline {
148
+ vertical-align: -2px;
149
+ }
150
+
143
151
  /* Pre-formatted code/text. Sunken bakgrund, monospace, pre-wrap
144
152
  for line-break-preservering med word-wrap pa lang text. */
145
153
  .code-block {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klodd/ds",
3
- "version": "4.0.0",
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];