dowel-ui 0.7.0 → 0.9.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/README.md CHANGED
@@ -11,7 +11,7 @@ A dowel is the hidden peg that joins two boards so the seam does not show. That
11
11
  **[Documentation](https://lacodda.github.io/dowel/)** — what everything is and why it is that way.
12
12
  **[The stand](https://lacodda.github.io/dowel/stand/)** — every component, live, in either theme and in the accent of any product of the line.
13
13
 
14
- **Status:** v0.7.0 - the theme, the scales, an accent per product, ten primitives, and the gates each one passes: axe, the keyboard, a dependency budget and a picture in both themes. The first product of the line lives on it. See the [roadmap](#roadmap).
14
+ **Status:** v0.9.0 - the theme, the scales, an accent per product, twenty components including the overlays and the menus, and the gates each one passes: axe, the keyboard, a dependency budget and a picture in both themes. The first product of the line lives on it. See the [roadmap](#roadmap).
15
15
 
16
16
  ## The theme
17
17
 
@@ -83,8 +83,23 @@ your code:
83
83
  npx shadcn@latest add https://lacodda.github.io/dowel/r/button.json
84
84
  ```
85
85
 
86
- Ten of them so far: Button, Input, Textarea, Panel, Badge, Chip, Kbd, Spinner,
87
- Truncate and Copyable.
86
+ Twenty of them so far. The everyday ones - Button, Input, Textarea, Panel,
87
+ Badge, Chip, Kbd, Spinner, Truncate and Copyable; the six that float above the
88
+ page - Dialog, ConfirmDialog, Drawer, Popover, PreviewCard and Tooltip; and
89
+ four for choosing something: Menu, ContextMenu, Select and Combobox.
90
+
91
+ `Select` is the one the line's oldest rule is about. It renders
92
+ `<button role="combobox">` and no native `<select>` at all - the browser draws
93
+ that popup in the operating system's own chrome, where no stylesheet reaches
94
+ it, and on a screen of the product's own controls it reads as a foreign object.
95
+ `multiple` is a prop on Select and on Combobox rather than a component of its
96
+ own.
97
+
98
+ The overlays are built on [Base UI](https://base-ui.com), which supplies the
99
+ part that is genuinely hard and invisible when it works: the focus trap, the
100
+ return of focus to whatever opened the thing, `Escape`, the scroll lock, and
101
+ the `aria-labelledby` that names a popup by its own title. Choosing between
102
+ them is the harder question, and [a guide](https://lacodda.github.io/dowel/guides/overlays/) covers it.
88
103
 
89
104
  Each is written in the vocabulary - no raw colours, no `dark:` utilities - so
90
105
  the same component is correct in both themes and in every product's accent.
@@ -99,8 +114,10 @@ import dowel from 'dowel-ui/eslint'
99
114
  export default [...dowel.configs.recommended]
100
115
  ```
101
116
 
102
- It reports a hex, an `rgb()`, a stock Tailwind colour, `bg-white`, and any
103
- `dark:` utility, in the file that wrote one - see [the guide](https://lacodda.github.io/dowel/guides/linting/).
117
+ Two rules come with it. One reports a hex, an `rgb()`, a stock Tailwind colour,
118
+ `bg-white` and any `dark:` utility, in the file that wrote one. The other
119
+ forbids the native `<select>`, whose popup the browser draws in the operating
120
+ system's own chrome where no CSS reaches it - see [the guide](https://lacodda.github.io/dowel/guides/linting/).
104
121
 
105
122
  Every primitive also runs a gate of its own before it ships: axe over the
106
123
  rendered DOM in every variant, the keyboard over every interactive one, a
@@ -127,7 +144,7 @@ Development goes in versions; each one is a single coherent theme, and ends in a
127
144
  ## Documentation
128
145
 
129
146
  - [The stand](https://lacodda.github.io/dowel/stand/) - every component, live
130
- - [Getting started](https://lacodda.github.io/dowel/getting-started/) and [the lint rule](https://lacodda.github.io/dowel/guides/linting/)
147
+ - [Getting started](https://lacodda.github.io/dowel/getting-started/), [the lint rules](https://lacodda.github.io/dowel/guides/linting/) and [what a component has to pass](https://lacodda.github.io/dowel/guides/gates/)
131
148
  - [Components](https://lacodda.github.io/dowel/components/button/)
132
149
  - [Tokens](https://lacodda.github.io/dowel/reference/tokens/), [scales](https://lacodda.github.io/dowel/reference/scales/) and [accents](https://lacodda.github.io/dowel/reference/accents/)
133
150
 
@@ -9,4 +9,5 @@ declare const dowel: ESLint.Plugin & {
9
9
  };
10
10
  export default dowel;
11
11
  export { noRawColor, findRawColor } from './no-raw-color.js';
12
+ export { noNativeSelect } from './no-native-select.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAoC5C;;+EAE+E;AAC/E,QAAA,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG;IAAE,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;KAAE,CAAA;CAEtE,CAAA;AAEF,eAAe,KAAK,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAyC5C;;+EAE+E;AAC/E,QAAA,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG;IAAE,OAAO,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;KAAE,CAAA;CAEtE,CAAA;AAEF,eAAe,KAAK,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA"}
@@ -1,9 +1,11 @@
1
+ import { noNativeSelect } from './no-native-select.js';
1
2
  import { noRawColor } from './no-raw-color.js';
2
3
  /*
3
4
  * The dowel ESLint plugin.
4
5
  *
5
- * One rule, and it enforces the convention the whole system rests on: a
6
- * component names colours from the vocabulary and never writes one down.
6
+ * Two rules, and both enforce a convention the system rests on: a component
7
+ * names colours from the vocabulary and never writes one down, and no screen
8
+ * uses a native `<select>`, which the browser draws in its own chrome.
7
9
  *
8
10
  * It ships from the package rather than the registry because it is not a
9
11
  * component - it is not copied into a product and edited there, it is a check
@@ -18,15 +20,18 @@ import { noRawColor } from './no-raw-color.js';
18
20
  */
19
21
  const plugin = {
20
22
  meta: { name: 'dowel' },
21
- rules: { 'no-raw-color': noRawColor },
23
+ rules: { 'no-raw-color': noRawColor, 'no-native-select': noNativeSelect },
22
24
  };
23
- /** The rule, applied where components live. Scoped to TypeScript sources: the
24
- * theme is CSS and the build tools are Node, and neither is a component. */
25
+ /** The rules, applied where components live. Scoped to TypeScript sources:
26
+ * the theme is CSS and the build tools are Node, and neither is a component. */
25
27
  const recommended = [
26
28
  {
27
29
  files: ['**/*.{ts,tsx}'],
28
30
  plugins: { dowel: plugin },
29
- rules: { 'dowel/no-raw-color': 'error' },
31
+ rules: {
32
+ 'dowel/no-raw-color': 'error',
33
+ 'dowel/no-native-select': 'error',
34
+ },
30
35
  },
31
36
  ];
32
37
  /** The plugin, with its config bundled on - the shape flat config expects
@@ -37,4 +42,5 @@ const dowel = Object.assign(plugin, {
37
42
  });
38
43
  export default dowel;
39
44
  export { noRawColor, findRawColor } from './no-raw-color.js';
45
+ export { noNativeSelect } from './no-native-select.js';
40
46
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;IACvB,KAAK,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE;CACd,CAAA;AAEzB;4EAC4E;AAC5E,MAAM,WAAW,GAAoB;IACnC;QACE,KAAK,EAAE,CAAC,eAAe,CAAC;QACxB,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QAC1B,KAAK,EAAE,EAAE,oBAAoB,EAAE,OAAO,EAAE;KACzC;CACF,CAAA;AAED;;+EAE+E;AAC/E,MAAM,KAAK,GAAkE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;IACjG,OAAO,EAAE,EAAE,WAAW,EAAE;CACzB,CAAC,CAAA;AAEF,eAAe,KAAK,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/eslint/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;IACvB,KAAK,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,kBAAkB,EAAE,cAAc,EAAE;CAClD,CAAA;AAEzB;gFACgF;AAChF,MAAM,WAAW,GAAoB;IACnC;QACE,KAAK,EAAE,CAAC,eAAe,CAAC;QACxB,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QAC1B,KAAK,EAAE;YACL,oBAAoB,EAAE,OAAO;YAC7B,wBAAwB,EAAE,OAAO;SAClC;KACF;CACF,CAAA;AAED;;+EAE+E;AAC/E,MAAM,KAAK,GAAkE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;IACjG,OAAO,EAAE,EAAE,WAAW,EAAE;CACzB,CAAC,CAAA;AAEF,eAAe,KAAK,CAAA;AACpB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const noNativeSelect: Rule.RuleModule;
3
+ //# sourceMappingURL=no-native-select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-native-select.d.ts","sourceRoot":"","sources":["../../src/eslint/no-native-select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AA4BlC,eAAO,MAAM,cAAc,EAAE,IAAI,CAAC,UA2BjC,CAAA"}
@@ -0,0 +1,51 @@
1
+ /*
2
+ * `no-native-select` - the oldest convention in the line, from atlas.
3
+ *
4
+ * A `<select>` cannot be made to look like the rest of an application. The
5
+ * browser draws its popup itself, in the operating system's chrome, with the
6
+ * operating system's fonts and spacing - and no amount of CSS reaches inside
7
+ * it. On a screen where every other control is the product's own, the one
8
+ * native dropdown reads as a foreign object, and on Windows it reads as a
9
+ * foreign object from 1998.
10
+ *
11
+ * So the rule is about the element, not about a name: `Select` from this set
12
+ * renders `<button role="combobox">` and no `<select>` at all, which is what
13
+ * the convention was always asking for.
14
+ *
15
+ * `<option>` and `<optgroup>` are reported too. They exist only inside a
16
+ * `<select>`, so one appearing on its own is either a `<select>` being
17
+ * assembled somewhere else or a misunderstanding - both worth saying out loud.
18
+ */
19
+ const NATIVE = new Set(['select', 'option', 'optgroup']);
20
+ const MESSAGE = 'A native `<{{name}}>` cannot be styled to match the rest of the application - ' +
21
+ 'the browser draws its popup in the operating system chrome. Use the Select or ' +
22
+ 'Combobox component instead.';
23
+ export const noNativeSelect = {
24
+ meta: {
25
+ type: 'problem',
26
+ docs: {
27
+ description: 'Forbid the native `<select>` element, which cannot be styled to match the product.',
28
+ url: 'https://lacodda.github.io/dowel/guides/linting/',
29
+ },
30
+ schema: [],
31
+ messages: { native: MESSAGE },
32
+ },
33
+ create(context) {
34
+ return {
35
+ // `JSXOpeningElement` is not in ESLint's own AST types - the JSX nodes
36
+ // come from the TypeScript parser - so the visitor is declared by name
37
+ // and the node is read structurally.
38
+ JSXOpeningElement(node) {
39
+ const name = node.name;
40
+ // A lower-case identifier is an HTML tag; `<Select>` is a component,
41
+ // and a member expression like `<Base.Select>` is somebody's namespace.
42
+ if (name?.type !== 'JSXIdentifier' || typeof name.name !== 'string')
43
+ return;
44
+ if (!NATIVE.has(name.name))
45
+ return;
46
+ context.report({ node, messageId: 'native', data: { name: name.name } });
47
+ },
48
+ };
49
+ },
50
+ };
51
+ //# sourceMappingURL=no-native-select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-native-select.js","sourceRoot":"","sources":["../../src/eslint/no-native-select.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAA;AAExD,MAAM,OAAO,GACX,gFAAgF;IAChF,gFAAgF;IAChF,6BAA6B,CAAA;AAE/B,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,oFAAoF;YACtF,GAAG,EAAE,iDAAiD;SACvD;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;KAC9B;IACD,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,uEAAuE;YACvE,uEAAuE;YACvE,qCAAqC;YACrC,iBAAiB,CAAC,IAAe;gBAC/B,MAAM,IAAI,GAAI,IAA+D,CAAC,IAAI,CAAA;gBAClF,qEAAqE;gBACrE,wEAAwE;gBACxE,IAAI,IAAI,EAAE,IAAI,KAAK,eAAe,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAM;gBAC3E,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,OAAM;gBAElC,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YAC1E,CAAC;SACmB,CAAA;IACxB,CAAC;CACF,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dowel-ui",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "The lacodda line design system: theme tokens and React primitives, distributed as a shadcn-compatible registry.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -53,7 +53,6 @@
53
53
  "eslint": "^9.0.0 || ^10.0.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@radix-ui/react-slot": "^1.3.3",
57
56
  "@types/node": "^26.4.0",
58
57
  "@types/react": "^19.2.18",
59
58
  "@types/react-dom": "^19.2.5",