ionbase-ui 0.17.0 → 0.18.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ionbase-ui",
3
- "version": "0.17.0",
4
- "description": "IonBase Design System \u2014 accessible React components, styles and design tokens in one package",
3
+ "version": "0.18.1",
4
+ "description": "IonBase Design System — accessible React components, styles and design tokens in one package",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -21,7 +21,9 @@
21
21
  "./tokens-js": {
22
22
  "types": "./dist/tokens-js.d.ts",
23
23
  "default": "./dist/tokens-js.js"
24
- }
24
+ },
25
+ "./stylelint-config": "./stylelint-config.js",
26
+ "./eslint-plugin": "./eslint-plugin/index.js"
25
27
  },
26
28
  "keywords": [
27
29
  "design-system",
@@ -34,7 +36,9 @@
34
36
  "ionbase"
35
37
  ],
36
38
  "files": [
37
- "dist"
39
+ "dist",
40
+ "eslint-plugin",
41
+ "stylelint-config.js"
38
42
  ],
39
43
  "sideEffects": [
40
44
  "**/*.css"
@@ -53,17 +57,22 @@
53
57
  },
54
58
  "scripts": {
55
59
  "dev": "tsc --watch",
56
- "build": "node scripts/sync-tokens.mjs && tsc --build --force && node scripts/copy-css.mjs && node scripts/verify-client-boundaries.mjs && node scripts/build-meta.mjs && node scripts/verify-meta.mjs",
60
+ "build": "node scripts/sync-tokens.mjs && tsc --build --force && node scripts/copy-css.mjs && node scripts/verify-client-boundaries.mjs && node scripts/verify-contrast.mjs && node scripts/build-meta.mjs && node scripts/verify-meta.mjs",
57
61
  "typecheck": "tsc --noEmit",
58
62
  "lint": "eslint . && stylelint \"src/styles/*.css\"",
59
63
  "format": "prettier --check .",
60
64
  "icons:verify": "node scripts/verify-icons.mjs",
61
65
  "meta:build": "node scripts/build-meta.mjs",
62
- "meta:verify": "node scripts/build-meta.mjs && node scripts/verify-meta.mjs"
66
+ "meta:verify": "node scripts/build-meta.mjs && node scripts/verify-meta.mjs",
67
+ "contrast": "node scripts/verify-contrast.mjs",
68
+ "contrast:list": "node scripts/verify-contrast.mjs --list"
63
69
  },
64
70
  "peerDependencies": {
65
71
  "react": "^18.0.0 || ^19.0.0",
66
- "react-dom": "^18.0.0 || ^19.0.0"
72
+ "react-dom": "^18.0.0 || ^19.0.0",
73
+ "stylelint": "^16.0.0 || ^17.0.0",
74
+ "stylelint-declaration-strict-value": "^1.10.0",
75
+ "eslint": "^8.57.0 || ^9.0.0"
67
76
  },
68
77
  "dependencies": {
69
78
  "@react-stately/collections": "^3.13.1",
@@ -71,9 +80,20 @@
71
80
  "react-aria": "^3.34.0",
72
81
  "react-stately": "^3.48.0"
73
82
  },
74
- "//devDependencies": "lucide-react is a devDependency, not a dependency: nothing in src/ imports it. It is here only so scripts/verify-icons.mjs can compare the installed icon set against the Figma page. Consumers pick their own icon library. There is deliberately NO dependency on @ionbase-ui/tokens \u2014 it is private and would publish as a version that does not exist on npm. Build ordering is expressed in turbo.json instead.",
83
+ "//devDependencies": "lucide-react is a devDependency, not a dependency: nothing in src/ imports it. It is here only so scripts/verify-icons.mjs can compare the installed icon set against the Figma page. Consumers pick their own icon library. There is deliberately NO dependency on @ionbase-ui/tokens — it is private and would publish as a version that does not exist on npm. Build ordering is expressed in turbo.json instead.",
75
84
  "devDependencies": {
76
85
  "lucide-react": "^1.27.0",
77
86
  "typescript": "^5.5.2"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "stylelint": {
90
+ "optional": true
91
+ },
92
+ "stylelint-declaration-strict-value": {
93
+ "optional": true
94
+ },
95
+ "eslint": {
96
+ "optional": true
97
+ }
78
98
  }
79
99
  }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * IonBase stylelint config — the token rules, shipped.
3
+ *
4
+ * // stylelint.config.js
5
+ * export default { extends: ['ionbase-ui/stylelint-config'] };
6
+ *
7
+ * This exists because the premise of IonBase is that the code consuming it may
8
+ * be written by an agent with no developer reviewing the result. A rule that
9
+ * lives only in this repo protects only this repo; the app is where the drift
10
+ * actually happens, and a lint error naming the fix is the one form of guidance
11
+ * an agent reliably acts on.
12
+ *
13
+ * It is deliberately NOT a superset of stylelint-config-standard. That config
14
+ * is about CSS hygiene and is the consumer's choice; this one is about design
15
+ * tokens, and pairs with whatever else they already run.
16
+ *
17
+ * Requires `stylelint-declaration-strict-value` alongside `stylelint`. Both are
18
+ * optional peer dependencies — installed only by projects that opt in.
19
+ *
20
+ * REPO NOTE: the root stylelint.config.js extends this file directly rather
21
+ * than re-declaring the rules. One source, many pointers — the same reason
22
+ * CLAUDE.md is a pointer to AGENTS.md. If you edit rules, edit them here.
23
+ */
24
+
25
+ /** Properties where a raw value is a theming bug rather than a style choice. */
26
+ export const TOKENISED_PROPERTIES = [
27
+ 'color',
28
+ 'background-color',
29
+ 'border-color',
30
+ 'border-top-color',
31
+ 'border-bottom-color',
32
+ 'border-left-color',
33
+ 'border-right-color',
34
+ 'outline-color',
35
+ 'font-size',
36
+ 'font-family',
37
+ 'font-weight',
38
+ 'line-height',
39
+ 'margin',
40
+ 'margin-top',
41
+ 'margin-bottom',
42
+ 'margin-left',
43
+ 'margin-right',
44
+ 'padding',
45
+ 'padding-top',
46
+ 'padding-bottom',
47
+ 'padding-left',
48
+ 'padding-right',
49
+ 'gap',
50
+ 'border-radius',
51
+ 'border-top-left-radius',
52
+ 'border-top-right-radius',
53
+ 'border-bottom-left-radius',
54
+ 'border-bottom-right-radius',
55
+ 'border-width',
56
+ 'border-top-width',
57
+ 'border-bottom-width',
58
+ 'border-left-width',
59
+ 'border-right-width',
60
+ ];
61
+
62
+ /**
63
+ * Values that are not tokens and never should be.
64
+ *
65
+ * The forced-colors block is the important half: under Windows High Contrast
66
+ * every authored colour is remapped, and these system keywords are the only
67
+ * values that survive. A token there would be silently discarded.
68
+ */
69
+ export const ALLOWED_RAW_VALUES = [
70
+ 'inherit',
71
+ 'transparent',
72
+ 'currentColor',
73
+ 'currentcolor',
74
+ '0',
75
+ 'none',
76
+ 'auto',
77
+ 'normal',
78
+ '100%',
79
+ 'max-content',
80
+ 'min-content',
81
+ 'Canvas',
82
+ 'CanvasText',
83
+ 'Highlight',
84
+ 'HighlightText',
85
+ 'GrayText',
86
+ 'ButtonText',
87
+ 'ButtonFace',
88
+ 'LinkText',
89
+ 'Field',
90
+ 'FieldText',
91
+ 'SelectedItem',
92
+ 'SelectedItemText',
93
+ ];
94
+
95
+ /** @type {import('stylelint').Config} */
96
+ const config = {
97
+ plugins: ['stylelint-declaration-strict-value'],
98
+ rules: {
99
+ 'color-no-hex': true,
100
+
101
+ // BEM. `.ion-button--primary-brand`, `.ion-tabs__item`.
102
+ 'selector-class-pattern': null,
103
+
104
+ 'scale-unlimited/declaration-strict-value': [
105
+ TOKENISED_PROPERTIES,
106
+ {
107
+ ignoreValues: ALLOWED_RAW_VALUES,
108
+ message:
109
+ 'Raw CSS values are forbidden here. Use a design token — see `ionbase-ui/tokens` for the custom properties, or `ionbase-ui/meta/<Component>.json` for the tokens a given component consumes.',
110
+ },
111
+ ],
112
+
113
+ /*
114
+ * No raw colour inside a shadow.
115
+ *
116
+ * Shadows are the one property where geometry and colour share a value.
117
+ * The offsets (`0 0 0 2px`) are inert and never theme, so requiring them to
118
+ * be tokens would buy nothing — but a raw colour is a place dark mode has to
119
+ * be fixed by hand, once per occurrence. This bans the colour half only,
120
+ * which is why it is a disallowed-list rather than declaration-strict-value.
121
+ *
122
+ * Correct: box-shadow: 0 0 0 2px var(--border-primary-strong);
123
+ * box-shadow: var(--ion-shadow-button-raised);
124
+ * Rejected: box-shadow: 0 1px 2px rgb(0 0 0 / 40%);
125
+ */
126
+ 'declaration-property-value-disallowed-list': {
127
+ 'box-shadow': [/rgba?\(/i, /hsla?\(/i, /#[0-9a-f]{3,8}/i],
128
+ },
129
+ },
130
+
131
+ overrides: [
132
+ {
133
+ // Wherever a project keeps its own elevation ramp, that file is where raw
134
+ // shadow values are SUPPOSED to live — it is the one place dark mode has
135
+ // to touch. Everywhere else must reference the token.
136
+ files: ['**/elevation.css'],
137
+ rules: { 'declaration-property-value-disallowed-list': null },
138
+ },
139
+ ],
140
+
141
+ ignoreFiles: ['**/dist/**', '**/node_modules/**'],
142
+ };
143
+
144
+ export default config;