dowel-ui 0.5.0 → 0.6.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/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.5.0 - the theme, the scales, an accent per product, and ten primitives: enough for a screen without overlays. See the [roadmap](#roadmap).
14
+ **Status:** v0.6.1 - the theme, the scales, an accent per product, ten primitives and the lint rule that keeps them honest. The first product of the line lives on it. See the [roadmap](#roadmap).
15
15
 
16
16
  ## The theme
17
17
 
@@ -90,6 +90,18 @@ Each is written in the vocabulary - no raw colours, no `dark:` utilities - so
90
90
  the same component is correct in both themes and in every product's accent.
91
91
  Every one of them is on [the stand](https://lacodda.github.io/dowel/stand/), live - change the theme and the accent and watch what follows.
92
92
 
93
+ That convention is a lint rule, and it ships with the package:
94
+
95
+ ```js
96
+ // eslint.config.js
97
+ import dowel from 'dowel-ui/eslint'
98
+
99
+ export default [...dowel.configs.recommended]
100
+ ```
101
+
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/).
104
+
93
105
  Full vocabulary, shown rather than tabulated: **[colours](https://lacodda.github.io/dowel/reference/tokens/)** in both themes, **[the scales](https://lacodda.github.io/dowel/reference/scales/)** - radius, type, motion, elevation and stacking order - and **[the accents](https://lacodda.github.io/dowel/reference/accents/)**, where the same screen is drawn in every colour of the line.
94
106
 
95
107
  ## Roadmap
@@ -109,7 +121,7 @@ Development goes in versions; each one is a single coherent theme, and ends in a
109
121
  ## Documentation
110
122
 
111
123
  - [The stand](https://lacodda.github.io/dowel/stand/) - every component, live
112
- - [Getting started](https://lacodda.github.io/dowel/getting-started/)
124
+ - [Getting started](https://lacodda.github.io/dowel/getting-started/) and [the lint rule](https://lacodda.github.io/dowel/guides/linting/)
113
125
  - [Components](https://lacodda.github.io/dowel/components/button/)
114
126
  - [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/)
115
127
 
@@ -0,0 +1,12 @@
1
+ import type { ESLint, Linter } from 'eslint';
2
+ /** The plugin, with its config bundled on - the shape flat config expects
3
+ * from a default export. Annotated rather than inferred: the inferred type
4
+ * reaches into ESLint's internals and cannot be written down by a consumer. */
5
+ declare const dowel: ESLint.Plugin & {
6
+ configs: {
7
+ recommended: Linter.Config[];
8
+ };
9
+ };
10
+ export default dowel;
11
+ export { noRawColor, findRawColor } from './no-raw-color.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,40 @@
1
+ import { noRawColor } from './no-raw-color.js';
2
+ /*
3
+ * The dowel ESLint plugin.
4
+ *
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.
7
+ *
8
+ * It ships from the package rather than the registry because it is not a
9
+ * component - it is not copied into a product and edited there, it is a check
10
+ * every product runs the same way, and it should improve for all of them at
11
+ * once.
12
+ *
13
+ * import dowel from 'dowel-ui/eslint'
14
+ *
15
+ * export default [
16
+ * ...dowel.configs.recommended,
17
+ * ]
18
+ */
19
+ const plugin = {
20
+ meta: { name: 'dowel' },
21
+ rules: { 'no-raw-color': noRawColor },
22
+ };
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
+ const recommended = [
26
+ {
27
+ files: ['**/*.{ts,tsx}'],
28
+ plugins: { dowel: plugin },
29
+ rules: { 'dowel/no-raw-color': 'error' },
30
+ },
31
+ ];
32
+ /** The plugin, with its config bundled on - the shape flat config expects
33
+ * from a default export. Annotated rather than inferred: the inferred type
34
+ * reaches into ESLint's internals and cannot be written down by a consumer. */
35
+ const dowel = Object.assign(plugin, {
36
+ configs: { recommended },
37
+ });
38
+ export default dowel;
39
+ export { noRawColor, findRawColor } from './no-raw-color.js';
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,11 @@
1
+ import type { Rule } from 'eslint';
2
+ interface Finding {
3
+ message: string;
4
+ }
5
+ /** What is wrong with this string, if anything. Exported for the test: the
6
+ * matching is the whole rule, and testing it directly is what lets a mutation
7
+ * to any single pattern show up as a failure. */
8
+ export declare function findRawColor(text: string): Finding | undefined;
9
+ export declare const noRawColor: Rule.RuleModule;
10
+ export {};
11
+ //# sourceMappingURL=no-raw-color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-raw-color.d.ts","sourceRoot":"","sources":["../../src/eslint/no-raw-color.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AA2DlC,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;iDAEiD;AACjD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAgC9D;AAED,eAAO,MAAM,UAAU,EAAE,IAAI,CAAC,UA2B7B,CAAA"}
@@ -0,0 +1,109 @@
1
+ /*
2
+ * `no-raw-color` - the rule behind the line's oldest convention.
3
+ *
4
+ * Two things are forbidden in a component, and they are the same mistake seen
5
+ * from either end:
6
+ *
7
+ * - a raw colour (`#d9569e`, `rgb(...)`, `bg-zinc-800`), because a colour
8
+ * without a name in the vocabulary cannot follow the product's accent and
9
+ * will not swap when the theme does;
10
+ * - a `dark:` utility, because it says the component knows which theme it is
11
+ * in. It does not, and must not: the token underneath changes instead.
12
+ *
13
+ * The check is deliberately textual. What matters is what ends up in a
14
+ * `className`, and that string is assembled from literals in `cva` variant
15
+ * maps, ternaries and template pieces - places a type-aware rule would have to
16
+ * follow anyway. So every string literal in the file is read, and the ones
17
+ * that look like a colour are reported.
18
+ *
19
+ * It runs on components, not on the theme: the theme is where raw colour is
20
+ * supposed to live, and it is CSS, which this never sees.
21
+ */
22
+ /** A hex colour: `#abc`, `#aabbcc`, `#aabbccdd`. Word-bounded at the end so a
23
+ * hash-prefixed word (`#section-title`) is not mistaken for one. */
24
+ const HEX = /#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{1,5})?\b/;
25
+ /** A CSS colour function with numeric arguments. `color-mix` and `oklch(from
26
+ * var(--accent-base) ...)` are how the theme derives colour and are allowed -
27
+ * what is forbidden is naming a colour outright. */
28
+ const COLOR_FUNCTION = /\b(?:rgba?|hsla?|hwb|lab|lch|oklab)\s*\(/;
29
+ /** Tailwind's stock palette, as it appears inside a utility: `bg-zinc-800`,
30
+ * `hover:text-red-500/50`, `border-slate-200`. The line drops the stock
31
+ * palette from the theme, so these do not compile - but they are written by
32
+ * habit and by every code generator, and the error they produce points at CSS
33
+ * rather than at the file that asked. */
34
+ const STOCK_PALETTE = /\b(?:slate|gray|grey|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950)\b/;
35
+ /** A `dark:` variant, anywhere in a class string. */
36
+ const DARK_VARIANT = /(?:^|[\s"'`])dark:/;
37
+ /*
38
+ * `white` and `black` as Tailwind utilities, at full opacity: `bg-white`,
39
+ * `text-black`. They are colours with no name in the vocabulary and no way to
40
+ * follow a theme - white chrome stays white when the ground turns white.
41
+ *
42
+ * With an opacity they are something else, and allowed: `bg-black/50` is a
43
+ * modal scrim, `text-white/70` is a label over a picture. Neither is a theme
44
+ * colour that failed to be named - they are what a translucent veil and ink
45
+ * on an arbitrary image are made of, which is why the theme keeps
46
+ * `--color-white` and `--color-black` deliberately. The line's first consumer
47
+ * had eleven of them and every one was right.
48
+ */
49
+ const ABSOLUTE = /\b(?:bg|text|border|fill|stroke|ring|outline|divide|shadow|from|via|to)-(?:white|black)\b(?!\/)/;
50
+ /** What is wrong with this string, if anything. Exported for the test: the
51
+ * matching is the whole rule, and testing it directly is what lets a mutation
52
+ * to any single pattern show up as a failure. */
53
+ export function findRawColor(text) {
54
+ if (DARK_VARIANT.test(text)) {
55
+ return {
56
+ message: 'A `dark:` utility makes the component know which theme it is in. Use a token - the theme swaps it underneath.',
57
+ };
58
+ }
59
+ if (HEX.test(text)) {
60
+ return {
61
+ message: 'A raw colour cannot follow the product accent or the theme. Use a token from the dowel vocabulary.',
62
+ };
63
+ }
64
+ if (COLOR_FUNCTION.test(text)) {
65
+ return {
66
+ message: 'A raw colour cannot follow the product accent or the theme. Use a token from the dowel vocabulary.',
67
+ };
68
+ }
69
+ if (STOCK_PALETTE.test(text)) {
70
+ return {
71
+ message: "Tailwind's stock palette is dropped from the theme, so this compiles to nothing. Use a token from the dowel vocabulary.",
72
+ };
73
+ }
74
+ if (ABSOLUTE.test(text)) {
75
+ return {
76
+ message: '`white` and `black` do not change with the theme. Use `text-text`, `bg-bg` or another token.',
77
+ };
78
+ }
79
+ return undefined;
80
+ }
81
+ export const noRawColor = {
82
+ meta: {
83
+ type: 'problem',
84
+ docs: {
85
+ description: 'Forbid raw colours and `dark:` utilities in components; every colour goes through a dowel token.',
86
+ url: 'https://lacodda.github.io/dowel/guides/tokens/',
87
+ },
88
+ schema: [],
89
+ },
90
+ create(context) {
91
+ const report = (node, text) => {
92
+ const finding = findRawColor(text);
93
+ if (finding)
94
+ context.report({ node, message: finding.message });
95
+ };
96
+ return {
97
+ Literal(node) {
98
+ if (typeof node.value === 'string')
99
+ report(node, node.value);
100
+ },
101
+ TemplateElement(node) {
102
+ // A template element carries no cooked value when the template is
103
+ // tagged with an invalid escape; the raw text is always there.
104
+ report(node, node.value.cooked ?? node.value.raw);
105
+ },
106
+ };
107
+ },
108
+ };
109
+ //# sourceMappingURL=no-raw-color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-raw-color.js","sourceRoot":"","sources":["../../src/eslint/no-raw-color.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;oEACoE;AACpE,MAAM,GAAG,GAAG,wCAAwC,CAAA;AAEpD;;oDAEoD;AACpD,MAAM,cAAc,GAAG,0CAA0C,CAAA;AAEjE;;;;yCAIyC;AACzC,MAAM,aAAa,GACjB,gMAAgM,CAAA;AAElM,qDAAqD;AACrD,MAAM,YAAY,GAAG,oBAAoB,CAAA;AAEzC;;;;;;;;;;;GAWG;AACH,MAAM,QAAQ,GACZ,iGAAiG,CAAA;AAMnG;;iDAEiD;AACjD,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,OAAO,EACL,+GAA+G;SAClH,CAAA;IACH,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,OAAO;YACL,OAAO,EACL,oGAAoG;SACvG,CAAA;IACH,CAAC;IACD,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,OAAO,EACL,oGAAoG;SACvG,CAAA;IACH,CAAC;IACD,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EACL,yHAAyH;SAC5H,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EACL,8FAA8F;SACjG,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAoB;IACzC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,kGAAkG;YACpG,GAAG,EAAE,gDAAgD;SACtD;QACD,MAAM,EAAE,EAAE;KACX;IACD,MAAM,CAAC,OAAO;QACZ,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,IAAY,EAAE,EAAE;YAC/C,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;YAClC,IAAI,OAAO;gBAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;QACjE,CAAC,CAAA;QAED,OAAO;YACL,OAAO,CAAC,IAAI;gBACV,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;oBAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YAC9D,CAAC;YACD,eAAe,CAAC,IAAI;gBAClB,kEAAkE;gBAClE,+DAA+D;gBAC/D,MAAM,CAAC,IAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAChE,CAAC;SACF,CAAA;IACH,CAAC;CACF,CAAA"}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { lineProducts, lineProduct, type LineProduct } from './line.js';
3
3
  export { defaultStorageKey, initTheme, nextTheme, resolvedTheme, useTheme, useThemeSwitch, type Theme, } from './theme.js';
4
4
  /** Tokens that carry a colour. Ordered as they read on a screen: grounds,
5
5
  * then hairlines, then ink, then the accent, then status. */
6
- export declare const colorTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft"];
6
+ export declare const colorTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "on-good", "on-warn", "on-bad", "on-info", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft"];
7
7
  /** Tokens a product overrides to make the theme its own: the accent from the
8
8
  * brand-line registry, and how much of it bleeds into the greys. */
9
9
  export declare const themeParameters: readonly ["accent-base", "neutral-base", "neutral-tint", "neutral-tint-strong", "ground", "ink"];
@@ -28,7 +28,7 @@ export declare const layerTokens: readonly ["z-popup", "z-sticky", "z-menu", "z-
28
28
  * vocabulary - a docs page, an inspector, the JSON export, the test that keeps
29
29
  * this file honest against the stylesheet - reads this, so a new category
30
30
  * cannot be added and quietly missed by half of them. */
31
- export declare const allTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft", "accent-base", "neutral-base", "neutral-tint", "neutral-tint-strong", "ground", "ink", "shadow-lift", "shadow-raise", "shadow-float", "radius-xs", "radius-sm", "radius-md", "radius-lg", "radius-xl", "radius-2xl", "radius-inner", "font-sans", "font-mono", "text-2xs", "text-xs", "text-sm", "text-base", "text-lg", "text-xl", "text-2xl", "font-weight-normal", "font-weight-medium", "font-weight-semibold", "tracking-caption", "tracking-tight", "duration-quick", "duration-base", "duration-slow", "ease-out", "ease-in-out", "z-popup", "z-sticky", "z-menu", "z-floating", "z-overlay", "z-modal", "z-palette", "z-toast"];
31
+ export declare const allTokens: readonly ["bg", "raise", "soft", "softer", "line", "line-2", "text", "dim", "faint", "accent", "accent-2", "accent-soft", "on-accent", "on-good", "on-warn", "on-bad", "on-info", "good", "good-soft", "warn", "warn-soft", "bad", "bad-soft", "info", "info-soft", "accent-base", "neutral-base", "neutral-tint", "neutral-tint-strong", "ground", "ink", "shadow-lift", "shadow-raise", "shadow-float", "radius-xs", "radius-sm", "radius-md", "radius-lg", "radius-xl", "radius-2xl", "radius-inner", "font-sans", "font-mono", "text-2xs", "text-xs", "text-sm", "text-base", "text-lg", "text-xl", "text-2xl", "font-weight-normal", "font-weight-medium", "font-weight-semibold", "tracking-caption", "tracking-tight", "duration-quick", "duration-base", "duration-slow", "ease-out", "ease-in-out", "z-popup", "z-sticky", "z-menu", "z-floating", "z-overlay", "z-modal", "z-palette", "z-toast"];
32
32
  export type ColorToken = (typeof colorTokens)[number];
33
33
  export type ThemeParameter = (typeof themeParameters)[number];
34
34
  export type ElevationToken = (typeof elevationTokens)[number];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,EACd,KAAK,KAAK,GACX,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,eAAO,MAAM,WAAW,0NAsBd,CAAA;AAEV;oEACoE;AACpE,eAAO,MAAM,eAAe,kGAOlB,CAAA;AAEV;yDACyD;AACzD,eAAO,MAAM,eAAe,0DAA2D,CAAA;AAEvF;8DAC8D;AAC9D,eAAO,MAAM,YAAY,0GAQf,CAAA;AAEV;iCACiC;AACjC,eAAO,MAAM,UAAU,gOAeb,CAAA;AAEV;;0CAE0C;AAC1C,eAAO,MAAM,YAAY,0FAMf,CAAA;AAEV;;qEAEqE;AACrE,eAAO,MAAM,WAAW,0GASd,CAAA;AAEV;;;yDAGyD;AACzD,eAAO,MAAM,SAAS,k0BAQZ,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AACnD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,KAAK,GACb,UAAU,GACV,cAAc,GACd,cAAc,GACd,WAAW,GACX,SAAS,GACT,WAAW,GACX,UAAU,CAAA;AAEd;;uCAEuC;AACvC,wBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAEzC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,EACd,KAAK,KAAK,GACX,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,eAAO,MAAM,WAAW,qQA0Bd,CAAA;AAEV;oEACoE;AACpE,eAAO,MAAM,eAAe,kGAOlB,CAAA;AAEV;yDACyD;AACzD,eAAO,MAAM,eAAe,0DAA2D,CAAA;AAEvF;8DAC8D;AAC9D,eAAO,MAAM,YAAY,0GAQf,CAAA;AAEV;iCACiC;AACjC,eAAO,MAAM,UAAU,gOAeb,CAAA;AAEV;;0CAE0C;AAC1C,eAAO,MAAM,YAAY,0FAMf,CAAA;AAEV;;qEAEqE;AACrE,eAAO,MAAM,WAAW,0GASd,CAAA;AAEV;;;yDAGyD;AACzD,eAAO,MAAM,SAAS,62BAQZ,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AACnD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AACvD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,KAAK,GACb,UAAU,GACV,cAAc,GACd,cAAc,GACd,WAAW,GACX,SAAS,GACT,WAAW,GACX,UAAU,CAAA;AAEd;;uCAEuC;AACvC,wBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAEzC"}
package/dist/index.js CHANGED
@@ -33,6 +33,10 @@ export const colorTokens = [
33
33
  'accent-2',
34
34
  'accent-soft',
35
35
  'on-accent',
36
+ 'on-good',
37
+ 'on-warn',
38
+ 'on-bad',
39
+ 'on-info',
36
40
  'good',
37
41
  'good-soft',
38
42
  'warn',
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAoB,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,GAEf,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI;IACJ,OAAO;IACP,MAAM;IACN,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,UAAU;IACV,aAAa;IACb,WAAW;IACX,MAAM;IACN,WAAW;IACX,MAAM;IACN,WAAW;IACX,KAAK;IACL,UAAU;IACV,MAAM;IACN,WAAW;CACH,CAAA;AAEV;oEACoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa;IACb,cAAc;IACd,cAAc;IACd,qBAAqB;IACrB,QAAQ;IACR,KAAK;CACG,CAAA;AAEV;yDACyD;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,cAAc,CAAU,CAAA;AAEvF;8DAC8D;AAC9D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,YAAY;IACZ,cAAc;CACN,CAAA;AAEV;iCACiC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,WAAW;IACX,WAAW;IACX,UAAU;IACV,SAAS;IACT,SAAS;IACT,WAAW;IACX,SAAS;IACT,SAAS;IACT,UAAU;IACV,oBAAoB;IACpB,oBAAoB;IACpB,sBAAsB;IACtB,kBAAkB;IAClB,gBAAgB;CACR,CAAA;AAEV;;0CAE0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,UAAU;IACV,aAAa;CACL,CAAA;AAEV;;qEAEqE;AACrE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,SAAS;IACT,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,SAAS;IACT,WAAW;IACX,SAAS;CACD,CAAA;AAEV;;;yDAGyD;AACzD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,GAAG,WAAW;IACd,GAAG,eAAe;IAClB,GAAG,eAAe;IAClB,GAAG,YAAY;IACf,GAAG,UAAU;IACb,GAAG,YAAY;IACf,GAAG,WAAW;CACN,CAAA;AAkBV;;uCAEuC;AACvC,MAAM,UAAU,KAAK,CAAC,IAAW;IAC/B,OAAO,KAAK,IAAI,EAAE,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAoB,MAAM,WAAW,CAAA;AACvE,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACR,cAAc,GAEf,MAAM,YAAY,CAAA;AAEnB;6DAC6D;AAC7D,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI;IACJ,OAAO;IACP,MAAM;IACN,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,UAAU;IACV,aAAa;IACb,WAAW;IACX,SAAS;IACT,SAAS;IACT,QAAQ;IACR,SAAS;IACT,MAAM;IACN,WAAW;IACX,MAAM;IACN,WAAW;IACX,KAAK;IACL,UAAU;IACV,MAAM;IACN,WAAW;CACH,CAAA;AAEV;oEACoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa;IACb,cAAc;IACd,cAAc;IACd,qBAAqB;IACrB,QAAQ;IACR,KAAK;CACG,CAAA;AAEV;yDACyD;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,cAAc,EAAE,cAAc,CAAU,CAAA;AAEvF;8DAC8D;AAC9D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,WAAW;IACX,YAAY;IACZ,cAAc;CACN,CAAA;AAEV;iCACiC;AACjC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,WAAW;IACX,WAAW;IACX,UAAU;IACV,SAAS;IACT,SAAS;IACT,WAAW;IACX,SAAS;IACT,SAAS;IACT,UAAU;IACV,oBAAoB;IACpB,oBAAoB;IACpB,sBAAsB;IACtB,kBAAkB;IAClB,gBAAgB;CACR,CAAA;AAEV;;0CAE0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,UAAU;IACV,aAAa;CACL,CAAA;AAEV;;qEAEqE;AACrE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,SAAS;IACT,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,WAAW;IACX,SAAS;IACT,WAAW;IACX,SAAS;CACD,CAAA;AAEV;;;yDAGyD;AACzD,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,GAAG,WAAW;IACd,GAAG,eAAe;IAClB,GAAG,eAAe;IAClB,GAAG,YAAY;IACf,GAAG,UAAU;IACb,GAAG,YAAY;IACf,GAAG,WAAW;CACN,CAAA;AAkBV;;uCAEuC;AACvC,MAAM,UAAU,KAAK,CAAC,IAAW;IAC/B,OAAO,KAAK,IAAI,EAAE,CAAA;AACpB,CAAC"}
package/dist/theme.css CHANGED
@@ -123,7 +123,7 @@
123
123
  --accent-2: oklch(from var(--accent-base) 0.4 c h);
124
124
  --accent-soft: color-mix(in oklab, var(--accent-base) 12%, transparent);
125
125
 
126
- --good: #178a58;
126
+ --good: #0c8554;
127
127
  --warn: #9a6b0c;
128
128
  --bad: #c93b3b;
129
129
  --info: #0d7f9c;
@@ -161,7 +161,7 @@
161
161
  --accent-2: oklch(from var(--accent-base) 0.4 c h);
162
162
  --accent-soft: color-mix(in oklab, var(--accent-base) 12%, transparent);
163
163
 
164
- --good: #178a58;
164
+ --good: #0c8554;
165
165
  --warn: #9a6b0c;
166
166
  --bad: #c93b3b;
167
167
  --info: #0d7f9c;
@@ -202,11 +202,31 @@
202
202
  */
203
203
  :root {
204
204
  --on-accent: oklch(from var(--accent) clamp(0, (0.58 - l) * 1000, 1) 0 0);
205
+
206
+ /*
207
+ * The same question for the status fills, and it has to be asked separately:
208
+ * `--on-accent` is derived from the accent, so using it on a `--warn` fill
209
+ * is only ever right by coincidence. The line's first consumer did exactly
210
+ * that - a count on a yellow badge, drawn in white at 1.95:1 - and it read
211
+ * as correct for as long as the product happened to pin white.
212
+ *
213
+ * The status hues do not follow the product accent, so these four are the
214
+ * same for every product; they are still derived rather than written down,
215
+ * because the status colours themselves change between the themes.
216
+ */
217
+ --on-good: oklch(from var(--good) clamp(0, (0.58 - l) * 1000, 1) 0 0);
218
+ --on-warn: oklch(from var(--warn) clamp(0, (0.58 - l) * 1000, 1) 0 0);
219
+ --on-bad: oklch(from var(--bad) clamp(0, (0.58 - l) * 1000, 1) 0 0);
220
+ --on-info: oklch(from var(--info) clamp(0, (0.58 - l) * 1000, 1) 0 0);
205
221
  }
206
222
 
207
223
  @supports (color: contrast-color(red)) {
208
224
  :root {
209
225
  --on-accent: contrast-color(var(--accent));
226
+ --on-good: contrast-color(var(--good));
227
+ --on-warn: contrast-color(var(--warn));
228
+ --on-bad: contrast-color(var(--bad));
229
+ --on-info: contrast-color(var(--info));
210
230
  }
211
231
  }
212
232
 
@@ -230,6 +250,10 @@
230
250
  --color-accent-2: var(--accent-2);
231
251
  --color-accent-soft: var(--accent-soft);
232
252
  --color-on-accent: var(--on-accent);
253
+ --color-on-good: var(--on-good);
254
+ --color-on-warn: var(--on-warn);
255
+ --color-on-bad: var(--on-bad);
256
+ --color-on-info: var(--on-info);
233
257
  --color-good: var(--good);
234
258
  --color-good-soft: var(--good-soft);
235
259
  --color-warn: var(--warn);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dowel-ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
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",
@@ -19,7 +19,8 @@
19
19
  "react",
20
20
  "tailwindcss",
21
21
  "shadcn",
22
- "registry"
22
+ "registry",
23
+ "eslint-plugin"
23
24
  ],
24
25
  "sideEffects": [
25
26
  "*.css"
@@ -33,6 +34,10 @@
33
34
  "types": "./dist/index.d.ts",
34
35
  "import": "./dist/index.js"
35
36
  },
37
+ "./eslint": {
38
+ "types": "./dist/eslint/index.d.ts",
39
+ "import": "./dist/eslint/index.js"
40
+ },
36
41
  "./theme.css": "./dist/theme.css",
37
42
  "./tokens.json": "./dist/tokens.json",
38
43
  "./accents/*.css": "./dist/accents/*.css"
@@ -44,7 +49,8 @@
44
49
  },
45
50
  "peerDependencies": {
46
51
  "react": "^19.0.0",
47
- "react-dom": "^19.0.0"
52
+ "react-dom": "^19.0.0",
53
+ "eslint": "^9.0.0 || ^10.0.0"
48
54
  },
49
55
  "devDependencies": {
50
56
  "@radix-ui/react-slot": "^1.3.3",
@@ -52,6 +58,7 @@
52
58
  "@types/react": "^19.2.18",
53
59
  "@types/react-dom": "^19.2.5",
54
60
  "class-variance-authority": "^0.7.1",
61
+ "eslint": "^10.9.1",
55
62
  "react": "^19.2.8",
56
63
  "react-dom": "^19.2.8"
57
64
  },
@@ -62,5 +69,10 @@
62
69
  "dependencies": {
63
70
  "clsx": "^2.1.1",
64
71
  "tailwind-merge": "^3.6.0"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "eslint": {
75
+ "optional": true
76
+ }
65
77
  }
66
78
  }