@marianmeres/design-tokens 1.3.1 → 1.4.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.
Files changed (55) hide show
  1. package/AGENTS.md +65 -36
  2. package/API.md +199 -52
  3. package/README.md +31 -0
  4. package/dist/css/amber-olive-safari.css +24 -50
  5. package/dist/css/blue-orange.css +24 -50
  6. package/dist/css/cyan-red.css +24 -50
  7. package/dist/css/cyan-slate.css +24 -50
  8. package/dist/css/emerald-amber-forest.css +24 -50
  9. package/dist/css/emerald-pink.css +24 -50
  10. package/dist/css/fuchsia-emerald.css +24 -50
  11. package/dist/css/gray.css +24 -50
  12. package/dist/css/indigo-amber.css +24 -50
  13. package/dist/css/lime-fuchsia-neon.css +24 -50
  14. package/dist/css/mauve-lime-electric.css +24 -50
  15. package/dist/css/mauve-teal.css +24 -50
  16. package/dist/css/mauve.css +24 -50
  17. package/dist/css/mist-indigo-fjord.css +24 -50
  18. package/dist/css/mist-violet-aurora.css +24 -50
  19. package/dist/css/mist.css +24 -50
  20. package/dist/css/monokai-cyan.css +24 -50
  21. package/dist/css/monokai-green.css +24 -50
  22. package/dist/css/monokai-pink.css +24 -50
  23. package/dist/css/olive-amber-safari.css +24 -50
  24. package/dist/css/olive.css +24 -50
  25. package/dist/css/orange-pink-sunset.css +24 -50
  26. package/dist/css/pink-emerald.css +24 -50
  27. package/dist/css/pink-teal.css +24 -50
  28. package/dist/css/purple-yellow.css +24 -50
  29. package/dist/css/rainbow.css +24 -50
  30. package/dist/css/red-blue.css +24 -50
  31. package/dist/css/red-cyan.css +24 -50
  32. package/dist/css/red-sky-slate.css +24 -50
  33. package/dist/css/red-sky.css +24 -50
  34. package/dist/css/rose-teal.css +24 -50
  35. package/dist/css/sky-amber.css +24 -50
  36. package/dist/css/slate-cyan.css +24 -50
  37. package/dist/css/slate-teal-ocean.css +24 -50
  38. package/dist/css/stone-orange-earth.css +24 -50
  39. package/dist/css/stone.css +24 -50
  40. package/dist/css/taupe-olive-clay.css +24 -50
  41. package/dist/css/taupe-rose-blush.css +24 -50
  42. package/dist/css/taupe.css +24 -50
  43. package/dist/css/teal-rose.css +24 -50
  44. package/dist/css/violet-lime.css +24 -50
  45. package/dist/css/violet-rose-dusk.css +24 -50
  46. package/dist/css/zinc.css +24 -50
  47. package/dist/generate.d.ts +56 -7
  48. package/dist/generate.js +91 -39
  49. package/dist/mod.d.ts +9 -1
  50. package/dist/reboot-bridge.d.ts +20 -5
  51. package/dist/reboot-bridge.js +45 -12
  52. package/dist/themes/mod.d.ts +7 -0
  53. package/dist/themes/mod.js +95 -0
  54. package/dist/types.d.ts +20 -4
  55. package/package.json +10 -1
package/AGENTS.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # @marianmeres/design-tokens — Agent Guide
2
2
 
3
3
  ## Quick Reference
4
+
4
5
  - **Stack**: Deno / TypeScript
5
6
  - **Run**: `deno task test` | **Watch**: `deno task test:watch`
6
7
  - **Build (npm)**: `deno task npm:build`
7
- - **Build (CSS)**: `deno task css:build` (generates pre-built CSS files + updates deno.json exports)
8
+ - **Build (CSS)**: `deno task css:build` (generates pre-built CSS files; accepts `CSS_PREFIX` env var, default `"stuic-"`)
9
+ - **Build (themes barrel)**: `deno task themes:build` (regenerates `src/themes/mod.ts`)
8
10
  - **Build (preview)**: `deno task preview:build`
9
11
  - **Publish**: `deno task publish` (JSR + npm)
10
12
  - **Format**: `deno fmt` (tabs, 90 chars, indent 4)
@@ -16,10 +18,11 @@ CSS custom property generator for design token systems. Takes a structured
16
18
  declarations with automatic hover/active state derivation via `color-mix()`.
17
19
 
18
20
  Entry points:
21
+
19
22
  - `.` — core generation functions, types, color palette
20
23
  - `./reboot` — Bootstrap Reboot `--bs-*` variable bridge
21
- - `./themes` — 43 pre-built theme definitions + `themeNames` constant
22
- - `./css/*.css` — pre-built CSS files with `stuic-` prefix (npm only, not on JSR)
24
+ - `./themes` — 43 pre-built theme definitions + `themeNames` constant + `bundledThemes` / `bundledThemeNames` / `getBundledTheme` runtime helpers
25
+ - `./css/*.css` — pre-built CSS files (**npm only** — JSR does not support directory exports)
23
26
 
24
27
  ## Project Structure
25
28
 
@@ -29,66 +32,85 @@ Entry points:
29
32
  types.ts — core type definitions (ThemeSchema, TokenSchema, etc.)
30
33
  generate.ts — token generation + CSS output (generateCssTokens, toCssString, generateThemeCss)
31
34
  utils.ts — hex color parsing (hexToRgb, hexToRgbTriplet)
32
- colors.ts — Tailwind color palette (v3 + v4.2 additions: taupe, mauve, mist, olive)
35
+ colors.ts — Tailwind color palette (v3 + v4.2 additions: taupe, mauve, mist, olive), as const
33
36
  reboot-bridge.ts — Bootstrap Reboot --bs-* variable mapping
34
37
  /themes
35
- mod.ts — auto-generated barrel (via scripts/build-themes-mod.ts)
38
+ mod.ts — auto-generated barrel + runtime registry (via scripts/build-themes-mod.ts)
36
39
  *.ts — individual theme files (default export: ThemeSchema)
37
- /css — pre-built CSS files (generated by scripts/build-css.ts)
40
+ /css — pre-built CSS files (generated by scripts/build-css.ts; filenames = kebab-case theme names)
38
41
  /tests — test files (deno test --allow-read)
39
42
  /scripts
40
43
  build-npm.ts — npm package build (also copies css/ into dist)
41
- build-css.ts — generates css/*.css from all themes with "stuic-" prefix
42
- build-themes-mod.ts — auto-generates src/themes/mod.ts from discovered theme files
44
+ build-css.ts — generates css/*.css from all themes; honors CSS_PREFIX env var
45
+ build-themes-mod.ts — auto-generates src/themes/mod.ts + inline registry helpers
43
46
  /theme-preview-builder — HTML preview tool
47
+ /mcp.ts — MCP tool definitions (generate-theme-css, preview-bundled-theme)
44
48
  ```
45
49
 
46
50
  ## Public API Surface
47
51
 
48
52
  ### Functions (from `./src/mod.ts`)
49
- | Function | Purpose |
50
- |----------|---------|
51
- | `generateCssTokens(schema, prefix, mode?)` | Generate token key-value pairs from a TokenSchema |
52
- | `toCssString(tokens, selector?)` | Convert tokens to a CSS declaration block |
53
- | `generateThemeCss(schema, prefix)` | Generate complete CSS for light + optional dark mode |
54
- | `hexToRgb(hex)` | Parse hex to `[r, g, b]` tuple |
55
- | `hexToRgbTriplet(hex)` | Convert hex to `"r, g, b"` string |
53
+
54
+ | Function | Purpose |
55
+ | --------------------------------------------- | --------------------------------------------------------------------------------------------------- |
56
+ | `generateCssTokens(schema, prefix, options?)` | Generate token key-value pairs. Third arg: `GenerateOptions` object or `"light"`/`"dark"` shorthand |
57
+ | `toCssString(tokens, selector?)` | Convert tokens to a CSS declaration block (groups by base color name) |
58
+ | `generateThemeCss(schema, prefix, options?)` | Generate complete CSS for light + optional dark mode; supports `cssLayer` wrapping |
59
+ | `hexToRgb(hex)` | Parse hex to `[r, g, b]` tuple |
60
+ | `hexToRgbTriplet(hex)` | Convert hex to `"r, g, b"` string |
56
61
 
57
62
  ### Functions (from `./reboot`)
58
- | Function | Purpose |
59
- |----------|---------|
60
- | `generateRebootBridge(tokens, prefix)` | Map tokens to `--bs-*` variables |
61
- | `generateThemedCss(schema, prefix)` | Combined tokens + reboot bridge CSS |
63
+
64
+ | Function | Purpose |
65
+ | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `generateRebootBridge(tokens, prefix)` | Map tokens to `--bs-*` variables. `-rgb` companions only emitted for literal hex values — auto-derived `color-mix()` values cannot produce triplets |
67
+ | `generateThemedCss(schema, prefix, options?)` | Combined tokens + reboot bridge CSS |
68
+
69
+ ### Functions (from `./themes`)
70
+
71
+ | Function | Purpose |
72
+ | ----------------------- | --------------------------------------------------------------------------- |
73
+ | `getBundledTheme(name)` | Look up bundled theme by camelCase name; returns `ThemeSchema \| undefined` |
62
74
 
63
75
  ### Key Types
64
- | Type | Purpose |
65
- |------|---------|
66
- | `ThemeSchema` | `{ light: TokenSchema; dark?: TokenSchema }` |
67
- | `TokenSchema` | Intent colors, role paired/single colors |
68
- | `ColorPair` | `{ DEFAULT, foreground, hover?, active? }` |
69
- | `ColorValue` | `{ DEFAULT, hover?, active? }` |
70
- | `SingleColor` | `string \| ColorValue` |
71
- | `GeneratedTokens` | `Record<string, string>` |
76
+
77
+ | Type | Purpose |
78
+ | ----------------------- | ------------------------------------------------------------------------------- |
79
+ | `ThemeSchema` | `{ light: TokenSchema; dark?: TokenSchema }` |
80
+ | `TokenSchema` | Intent colors, role paired/single colors |
81
+ | `ColorPair` | `{ DEFAULT, foreground, hover?, active?, foregroundHover?, foregroundActive? }` |
82
+ | `ColorValue` | `{ DEFAULT, hover?, active? }` |
83
+ | `SingleColor` | `string \| ColorValue` |
84
+ | `GeneratedTokens` | `Record<string, string>` |
85
+ | `GenerateOptions` | `{ mode?, deriveStates?, surfaceForegroundContrast? }` |
86
+ | `GenerateThemeOptions` | `Omit<GenerateOptions, "mode"> & { cssLayer? }` |
87
+ | `RolePairedKey` | Required: `"background" \| "muted" \| "surface"` |
88
+ | `RolePairedOptionalKey` | Optional: `"surface-1"` (all bundled themes ship it) |
72
89
 
73
90
  ### Constants
74
- | Export | Purpose |
75
- |--------|---------|
76
- | `colors` | Tailwind hex palette including v4.2 additions (`colors.blue[600]`, `colors.mauve[500]`) |
77
- | `themeNames` | Kebab-case theme name strings matching CSS filenames (from `./themes`) |
91
+
92
+ | Export | Purpose |
93
+ | ------------------- | ---------------------------------------------------------------- |
94
+ | `colors` | Tailwind hex palette (with `as const`, full literal types) |
95
+ | `themeNames` | Kebab-case theme names (readonly tuple) |
96
+ | `bundledThemes` | Map of camelCase name → ThemeSchema (no leaky non-theme exports) |
97
+ | `bundledThemeNames` | Array of camelCase theme names |
78
98
 
79
99
  ## Critical Conventions
80
100
 
81
101
  1. All public functions must have JSDoc and explicit return types (Deno/JSR requirement)
82
102
  2. Token naming: `--{prefix}color-{key}`, `--{prefix}color-{key}-hover`, etc.
83
- 3. Hover/active auto-derived via `color-mix(in oklch, ...)` — light darkens with black, dark lightens with white
84
- 4. Surface-intent tokens derived via `color-mix(in srgb, ...)` from intent + background
85
- 5. Theme files use `export default` with `ThemeSchema` type annotation
86
- 6. Formatting: tabs, 90-char line width, 4-space indent width (`deno fmt`)
103
+ 3. Prefix is auto-normalized — `"my"` and `"my-"` both produce `--my-color-*`. Empty string produces unprefixed.
104
+ 4. Hover/active auto-derived via `color-mix(in oklch, ...)` — light darkens with black, dark lightens with white. Disable with `deriveStates: false`.
105
+ 5. Surface-intent tokens derived via `color-mix(in srgb, ...)` from intent + background. Foreground contrast defaults to 50% — tune via `surfaceForegroundContrast`.
106
+ 6. Theme files use `export default` with `ThemeSchema` type annotation. All bundled themes include an optional `surface-1` paired role (extra elevation).
107
+ 7. Formatting: tabs, 90-char line width, 4-space indent width (`deno fmt`)
108
+ 8. MCP handlers must return `Promise<string>` — framework contract from `jsr:@marianmeres/mcp-server/types`.
87
109
 
88
110
  ## Adding a New Theme
89
111
 
90
112
  1. Create `src/themes/{name}.ts` with `export default` of `ThemeSchema`
91
- 2. Run `deno task themes:build` to regenerate `src/themes/mod.ts`
113
+ 2. Run `deno task themes:build` to regenerate `src/themes/mod.ts` (barrel + registry)
92
114
  3. Run `deno task css:build` to regenerate CSS files
93
115
  4. Run `deno task preview:build` to rebuild the preview tool
94
116
  5. If the theme uses new color families, update `COLOR_FAMILIES` in `theme-preview-builder/index.html`
@@ -97,5 +119,12 @@ Entry points:
97
119
 
98
120
  - [ ] Read existing patterns in similar files
99
121
  - [ ] Run `deno task test` — all tests must pass
122
+ - [ ] `deno check src/mod.ts src/themes/mod.ts src/reboot-bridge.ts mcp.ts scripts/*.ts` — typecheck
100
123
  - [ ] Follow `deno fmt` conventions
101
124
  - [ ] Ensure new exports have JSDoc + explicit return types
125
+ - [ ] If changing generator output (CSS text), note as potential BC change
126
+
127
+ ## Known Limitations
128
+
129
+ - **JSR does not support directory exports** → pre-built CSS is npm-only.
130
+ - **Reboot `-rgb` companions** are omitted for tokens whose values are `color-mix()` expressions (auto-derived hovers). Provide explicit hex `hover` / `active` if you need full alpha-blend support through Reboot's `rgba(var(--bs-…-rgb), α)` rules.
package/API.md CHANGED
@@ -2,38 +2,53 @@
2
2
 
3
3
  ## Functions
4
4
 
5
- ### `generateCssTokens(schema, prefix, mode?)`
5
+ ### `generateCssTokens(schema, prefix, options?)`
6
6
 
7
7
  Generate CSS token key-value pairs from a single-mode token schema.
8
8
 
9
9
  **Parameters:**
10
+
10
11
  - `schema` (`TokenSchema`) — Color definitions for intent, role paired, and role single colors
11
- - `prefix` (`string`) — CSS variable prefix (e.g. `"my-"` produces `--my-color-primary`)
12
- - `mode` (`"light" | "dark"`, optional) — Affects auto-derivation direction. Light darkens with black, dark lightens with white. Default: `"light"`
12
+ - `prefix` (`string`) — CSS variable prefix. Auto-normalized: `"my"` and `"my-"` are equivalent. `""` produces unprefixed tokens.
13
+ - `options` (`GenerateOptions | "light" | "dark"`, optional) — A mode string for quick switching, or an options object. Default: `{}`
14
+ - `mode` (`"light" | "dark"`, optional) — Affects auto-derivation direction. Light darkens with black, dark lightens with white. Default: `"light"`
15
+ - `deriveStates` (`boolean`, optional) — When `false`, missing hover/active fall back to DEFAULT instead of being derived via `color-mix()`. Useful for environments without `color-mix()` support. Default: `true`
16
+ - `surfaceForegroundContrast` (`number`, optional) — Mix percentage (0–100) used to derive the `surface-{intent}-foreground` token. Higher values increase contrast against the tinted surface background. Default: `50`
13
17
 
14
18
  **Returns:** `GeneratedTokens` — Key-value record of token names to CSS values (keys without the `--` prefix)
15
19
 
16
20
  **Example:**
21
+
17
22
  ```typescript
18
23
  import { generateCssTokens } from "@marianmeres/design-tokens";
19
24
 
20
- const tokens = generateCssTokens(schema.light, "app-", "light");
21
- // { "app-color-primary": "#2563eb", "app-color-primary-hover": "color-mix(...)", ... }
25
+ // Shorthand mode string
26
+ const tokens = generateCssTokens(schema.light, "app-", "dark");
27
+
28
+ // Full options object
29
+ const flat = generateCssTokens(schema.light, "app", {
30
+ mode: "light",
31
+ deriveStates: false,
32
+ surfaceForegroundContrast: 70,
33
+ });
34
+ // { "app-color-primary": "#2563eb", "app-color-primary-hover": "#2563eb", ... }
22
35
  ```
23
36
 
24
37
  ---
25
38
 
26
39
  ### `toCssString(tokens, selector?)`
27
40
 
28
- Convert a tokens record to a formatted CSS declaration block, grouped by base color name.
41
+ Convert a tokens record to a formatted CSS declaration block, grouped by base color name (e.g. all `surface-*` tokens cluster together).
29
42
 
30
43
  **Parameters:**
44
+
31
45
  - `tokens` (`GeneratedTokens`) — Key-value token pairs (from `generateCssTokens`)
32
46
  - `selector` (`string`, optional) — CSS selector for the block. Default: `":root"`
33
47
 
34
48
  **Returns:** `string` — Formatted CSS string
35
49
 
36
50
  **Example:**
51
+
37
52
  ```typescript
38
53
  import { generateCssTokens, toCssString } from "@marianmeres/design-tokens";
39
54
 
@@ -46,22 +61,30 @@ const css = toCssString(tokens, ":root.dark");
46
61
 
47
62
  ---
48
63
 
49
- ### `generateThemeCss(schema, prefix)`
64
+ ### `generateThemeCss(schema, prefix, options?)`
50
65
 
51
66
  Generate complete CSS for a theme with light mode and optional dark mode.
52
67
 
53
68
  **Parameters:**
69
+
54
70
  - `schema` (`ThemeSchema`) — Theme with required `light` and optional `dark` token schemas
55
- - `prefix` (`string`) — CSS variable prefix
71
+ - `prefix` (`string`) — CSS variable prefix (trailing dash optional)
72
+ - `options` (`GenerateThemeOptions`, optional) — Accepts all `GenerateOptions` (except `mode`, which is set per section) plus:
73
+ - `cssLayer` (`string`, optional) — Wrap the output in `@layer {name} { ... }`. Useful for CSS cascade control.
56
74
 
57
- **Returns:** `string` — Complete CSS with `:root` for light and `:root.dark` for dark mode
75
+ **Returns:** `string` — Complete CSS with `:root` for light and `:root.dark` for dark mode, optionally wrapped in `@layer`.
58
76
 
59
77
  **Example:**
78
+
60
79
  ```typescript
61
80
  import { generateThemeCss } from "@marianmeres/design-tokens";
62
81
  import { zinc } from "@marianmeres/design-tokens/themes";
63
82
 
64
- const css = generateThemeCss(zinc, "app-");
83
+ const css = generateThemeCss(zinc, "app-", { cssLayer: "tokens" });
84
+ // @layer tokens {
85
+ // :root { ... }
86
+ // :root.dark { ... }
87
+ // }
65
88
  ```
66
89
 
67
90
  ---
@@ -71,17 +94,19 @@ const css = generateThemeCss(zinc, "app-");
71
94
  Parse a hex color string to RGB components. Supports 3-digit (`#abc`) and 6-digit (`#aabbcc`) formats, with or without `#` prefix.
72
95
 
73
96
  **Parameters:**
97
+
74
98
  - `hex` (`string`) — Hex color string
75
99
 
76
100
  **Returns:** `[number, number, number] | null` — RGB tuple, or `null` if invalid
77
101
 
78
102
  **Example:**
103
+
79
104
  ```typescript
80
105
  import { hexToRgb } from "@marianmeres/design-tokens";
81
106
 
82
107
  hexToRgb("#2563eb"); // → [37, 99, 235]
83
- hexToRgb("fff"); // → [255, 255, 255]
84
- hexToRgb("nope"); // → null
108
+ hexToRgb("fff"); // → [255, 255, 255]
109
+ hexToRgb("nope"); // → null
85
110
  ```
86
111
 
87
112
  ---
@@ -91,11 +116,13 @@ hexToRgb("nope"); // → null
91
116
  Convert a hex color to an `"r, g, b"` triplet string suitable for CSS `rgb()` usage.
92
117
 
93
118
  **Parameters:**
119
+
94
120
  - `hex` (`string`) — Hex color string
95
121
 
96
122
  **Returns:** `string | null` — Comma-separated RGB triplet, or `null` if invalid
97
123
 
98
124
  **Example:**
125
+
99
126
  ```typescript
100
127
  import { hexToRgbTriplet } from "@marianmeres/design-tokens";
101
128
 
@@ -106,31 +133,37 @@ hexToRgbTriplet("#2563eb"); // → "37, 99, 235"
106
133
 
107
134
  ### `generateRebootBridge(tokens, prefix)`
108
135
 
109
- *Import from `@marianmeres/design-tokens/reboot`*
136
+ _Import from `@marianmeres/design-tokens/reboot`_
110
137
 
111
138
  Map design tokens to Bootstrap Reboot `--bs-*` CSS variables. Automatically generates `-rgb` triplet companions for hex color values.
112
139
 
113
140
  **Parameters:**
141
+
114
142
  - `tokens` (`GeneratedTokens`) — Generated token pairs (from `generateCssTokens`)
115
- - `prefix` (`string`) — The same prefix used in `generateCssTokens`
143
+ - `prefix` (`string`) — The same prefix used in `generateCssTokens` (trailing dash optional)
116
144
 
117
145
  **Returns:** `GeneratedTokens` — Bootstrap bridge variables (`bs-body-bg`, `bs-link-color`, etc.)
118
146
 
147
+ **Note on `-rgb` companions:** Bootstrap's alpha-blended rules (e.g. `rgba(var(--bs-link-color-rgb), 0.5)`) require raw RGB triplets. This bridge emits `--bs-*-rgb` only when the underlying token resolves to a literal hex color. Auto-derived hover/active tokens are `color-mix()` expressions and cannot produce triplets — the companions are omitted, and Reboot rules that rely on them fall back to defaults. Provide explicit hex `hover` / `active` values on your `ColorPair`s if you need full alpha-blend support.
148
+
119
149
  ---
120
150
 
121
- ### `generateThemedCss(schema, prefix)`
151
+ ### `generateThemedCss(schema, prefix, options?)`
122
152
 
123
- *Import from `@marianmeres/design-tokens/reboot`*
153
+ _Import from `@marianmeres/design-tokens/reboot`_
124
154
 
125
155
  Generate complete themed CSS including both design tokens AND the Bootstrap Reboot bridge.
126
156
 
127
157
  **Parameters:**
158
+
128
159
  - `schema` (`ThemeSchema`) — Theme schema with light and optional dark modes
129
- - `prefix` (`string`) — CSS variable prefix
160
+ - `prefix` (`string`) — CSS variable prefix (trailing dash optional)
161
+ - `options` (`Omit<GenerateThemeOptions, "cssLayer">`, optional) — Forwarded to `generateCssTokens` for each mode (see `generateCssTokens`)
130
162
 
131
163
  **Returns:** `string` — Combined CSS with token variables and `--bs-*` bridge variables
132
164
 
133
165
  **Example:**
166
+
134
167
  ```typescript
135
168
  import { generateThemedCss } from "@marianmeres/design-tokens/reboot";
136
169
  import { zinc } from "@marianmeres/design-tokens/themes";
@@ -141,14 +174,37 @@ const css = generateThemedCss(zinc, "app-");
141
174
 
142
175
  ---
143
176
 
177
+ ### `getBundledTheme(name)`
178
+
179
+ _Import from `@marianmeres/design-tokens/themes`_
180
+
181
+ Look up a bundled theme by its camelCase name. Returns `undefined` if the name is not a known theme.
182
+
183
+ **Parameters:**
184
+
185
+ - `name` (`string`) — Theme name (camelCase, e.g. `"blueOrange"`)
186
+
187
+ **Returns:** `ThemeSchema | undefined`
188
+
189
+ **Example:**
190
+
191
+ ```typescript
192
+ import { getBundledTheme } from "@marianmeres/design-tokens/themes";
193
+
194
+ const theme = getBundledTheme("mauveTeal");
195
+ if (theme) { /* use theme */ }
196
+ ```
197
+
198
+ ---
199
+
144
200
  ## Types
145
201
 
146
202
  ### `ThemeSchema`
147
203
 
148
204
  ```typescript
149
205
  type ThemeSchema = {
150
- light: TokenSchema;
151
- dark?: TokenSchema;
206
+ light: TokenSchema;
207
+ dark?: TokenSchema;
152
208
  };
153
209
  ```
154
210
 
@@ -160,17 +216,17 @@ Complete theme definition with required light mode and optional dark mode.
160
216
 
161
217
  ```typescript
162
218
  type TokenSchema = {
163
- colors: {
164
- intent: WithRequired<IntentColorKey, ColorPair>;
165
- role: {
166
- paired: WithRequired<RolePairedKey, ColorPair>;
167
- single: WithRequired<RoleSingleKey, SingleColor>;
168
- };
169
- };
219
+ colors: {
220
+ intent: WithRequired<IntentColorKey, ColorPair>;
221
+ role: {
222
+ paired: WithKnown<RolePairedKey, RolePairedOptionalKey, ColorPair>;
223
+ single: WithRequired<RoleSingleKey, SingleColor>;
224
+ };
225
+ };
170
226
  };
171
227
  ```
172
228
 
173
- Single-mode token schema. Intent colors define primary UI actions; role colors define structural UI elements.
229
+ Single-mode token schema. Intent colors define primary UI actions; role colors define structural UI elements. Arbitrary string keys are allowed on every collection (for custom additions).
174
230
 
175
231
  ---
176
232
 
@@ -178,14 +234,16 @@ Single-mode token schema. Intent colors define primary UI actions; role colors d
178
234
 
179
235
  ```typescript
180
236
  type ColorPair = {
181
- DEFAULT: string;
182
- foreground: string;
183
- hover?: string;
184
- active?: string;
237
+ DEFAULT: string;
238
+ foreground: string;
239
+ hover?: string;
240
+ active?: string;
241
+ foregroundHover?: string;
242
+ foregroundActive?: string;
185
243
  };
186
244
  ```
187
245
 
188
- Color with a foreground companion. Hover/active are auto-derived when omitted.
246
+ Color with a foreground companion. Hover/active are auto-derived when omitted. `foregroundHover` and `foregroundActive` default to `foreground` — provide them if your foreground should change on interaction.
189
247
 
190
248
  ---
191
249
 
@@ -193,9 +251,9 @@ Color with a foreground companion. Hover/active are auto-derived when omitted.
193
251
 
194
252
  ```typescript
195
253
  type ColorValue = {
196
- DEFAULT: string;
197
- hover?: string;
198
- active?: string;
254
+ DEFAULT: string;
255
+ hover?: string;
256
+ active?: string;
199
257
  };
200
258
  ```
201
259
 
@@ -213,6 +271,32 @@ Either a plain CSS color string or an object with optional hover/active states.
213
271
 
214
272
  ---
215
273
 
274
+ ### `GenerateOptions`
275
+
276
+ ```typescript
277
+ type GenerateOptions = {
278
+ mode?: "light" | "dark";
279
+ deriveStates?: boolean;
280
+ surfaceForegroundContrast?: number;
281
+ };
282
+ ```
283
+
284
+ Options passed to `generateCssTokens`. See the function docs above for field meanings.
285
+
286
+ ---
287
+
288
+ ### `GenerateThemeOptions`
289
+
290
+ ```typescript
291
+ type GenerateThemeOptions = Omit<GenerateOptions, "mode"> & {
292
+ cssLayer?: string;
293
+ };
294
+ ```
295
+
296
+ Options passed to `generateThemeCss`. Omits `mode` (set automatically per section) and adds `cssLayer` for optional `@layer` wrapping.
297
+
298
+ ---
299
+
216
300
  ### `GeneratedTokens`
217
301
 
218
302
  ```typescript
@@ -237,6 +321,18 @@ type IntentColorKey = "primary" | "accent" | "destructive" | "warning" | "succes
237
321
  type RolePairedKey = "background" | "muted" | "surface";
238
322
  ```
239
323
 
324
+ Required paired role keys.
325
+
326
+ ---
327
+
328
+ ### `RolePairedOptionalKey`
329
+
330
+ ```typescript
331
+ type RolePairedOptionalKey = "surface-1";
332
+ ```
333
+
334
+ Optional paired role keys. Conventional but not required — all bundled themes define `surface-1` as an additional elevation layer. Additional arbitrary string keys are also allowed.
335
+
240
336
  ---
241
337
 
242
338
  ### `RoleSingleKey`
@@ -247,6 +343,27 @@ type RoleSingleKey = "foreground" | "border" | "input" | "ring";
247
343
 
248
344
  ---
249
345
 
346
+ ### `WithRequired` / `WithOptional` / `WithKnown`
347
+
348
+ Helper types for schema collections:
349
+
350
+ ```typescript
351
+ type WithRequired<K extends string, V> =
352
+ & Record<K, V>
353
+ & Record<string, V>;
354
+
355
+ type WithOptional<K extends string, V> =
356
+ & Partial<Record<K, V>>
357
+ & Record<string, V>;
358
+
359
+ type WithKnown<Req extends string, Opt extends string, V> =
360
+ & Record<Req, V>
361
+ & Partial<Record<Opt, V>>
362
+ & Record<string, V>;
363
+ ```
364
+
365
+ ---
366
+
250
367
  ## Constants
251
368
 
252
369
  ### `colors`
@@ -262,45 +379,75 @@ Tailwind CSS color palette as hex values. Contains `black`, `white`, and 26 colo
262
379
  ```typescript
263
380
  import { colors } from "@marianmeres/design-tokens";
264
381
 
265
- colors.blue[600] // → "#2563eb"
266
- colors.mauve[500] // → "#79697b"
267
- colors.white // → "#ffffff"
382
+ colors.blue[600]; // → "#2563eb"
383
+ colors.mauve[500]; // → "#79697b"
384
+ colors.white; // → "#ffffff"
268
385
  ```
269
386
 
270
387
  ---
271
388
 
272
- ## Bundled Themes
389
+ ### `bundledThemes`
273
390
 
274
- *Import from `@marianmeres/design-tokens/themes`*
391
+ _Import from `@marianmeres/design-tokens/themes`_
275
392
 
276
- 43 pre-built `ThemeSchema` definitions, each with light + dark mode:
393
+ Map of all bundled themes keyed by their camelCase name. Safe to iterate — the non-theme exports (`themeNames`, `ThemeSchema`) are filtered out.
277
394
 
278
- `amberOliveSafari`, `blueOrange`, `cyanRed`, `cyanSlate`, `emeraldAmberForest`, `emeraldPink`, `fuchsiaEmerald`, `gray`, `indigoAmber`, `limeFuchsiaNeon`, `mauve`, `mauveLimeElectric`, `mauveTeal`, `mist`, `mistIndigoFjord`, `mistVioletAurora`, `monokaiCyan`, `monokaiGreen`, `monokaiPink`, `olive`, `oliveAmberSafari`, `orangePinkSunset`, `pinkEmerald`, `pinkTeal`, `purpleYellow`, `rainbow`, `redBlue`, `redCyan`, `redSky`, `redSkySlate`, `roseTeal`, `skyAmber`, `slateCyan`, `slateTealOcean`, `stone`, `stoneOrangeEarth`, `taupe`, `taupeOliveClay`, `taupeRoseBlush`, `tealRose`, `violetLime`, `violetRoseDusk`, `zinc`
395
+ ```typescript
396
+ import { bundledThemes } from "@marianmeres/design-tokens/themes";
397
+
398
+ Object.keys(bundledThemes); // → ["amberOliveSafari", "blueOrange", ...]
399
+ ```
279
400
 
280
401
  ---
281
402
 
282
- ## Pre-built CSS
403
+ ### `bundledThemeNames`
404
+
405
+ _Import from `@marianmeres/design-tokens/themes`_
406
+
407
+ Array of all bundled theme names (camelCase), discovered at runtime from `bundledThemes`.
408
+
409
+ ---
410
+
411
+ ### `themeNames`
283
412
 
284
- *npm only — not available on JSR*
413
+ _Import from `@marianmeres/design-tokens/themes`_
285
414
 
286
- All bundled themes are also distributed as pre-built CSS files using the `stuic-` prefix. Import via `@marianmeres/design-tokens/css/{theme-name}.css`.
415
+ Kebab-case theme name strings matching the generated CSS filenames (without extension). Typed as a `readonly` tuple.
287
416
 
288
417
  ```typescript
289
- import "@marianmeres/design-tokens/css/mauve-teal.css";
418
+ import { themeNames } from "@marianmeres/design-tokens/themes";
419
+
420
+ themeNames; // → ["amber-olive-safari", "blue-orange", ..., "zinc"]
290
421
  ```
291
422
 
292
- Available files: `amber-olive-safari.css`, `blue-orange.css`, `cyan-red.css`, `mauve-teal.css`, `mist-indigo-fjord.css`, `taupe-rose-blush.css`, etc. (one per bundled theme)
423
+ ---
424
+
425
+ ## Bundled Themes
426
+
427
+ _Import from `@marianmeres/design-tokens/themes`_
428
+
429
+ 43 pre-built `ThemeSchema` definitions, each with light + dark mode:
430
+
431
+ `amberOliveSafari`, `blueOrange`, `cyanRed`, `cyanSlate`, `emeraldAmberForest`, `emeraldPink`, `fuchsiaEmerald`, `gray`, `indigoAmber`, `limeFuchsiaNeon`, `mauve`, `mauveLimeElectric`, `mauveTeal`, `mist`, `mistIndigoFjord`, `mistVioletAurora`, `monokaiCyan`, `monokaiGreen`, `monokaiPink`, `olive`, `oliveAmberSafari`, `orangePinkSunset`, `pinkEmerald`, `pinkTeal`, `purpleYellow`, `rainbow`, `redBlue`, `redCyan`, `redSky`, `redSkySlate`, `roseTeal`, `skyAmber`, `slateCyan`, `slateTealOcean`, `stone`, `stoneOrangeEarth`, `taupe`, `taupeOliveClay`, `taupeRoseBlush`, `tealRose`, `violetLime`, `violetRoseDusk`, `zinc`
432
+
433
+ All bundled themes include a `surface-1` paired role (on top of the required `background`, `muted`, `surface`) as a conventional extra elevation.
293
434
 
294
435
  ---
295
436
 
296
- ### `themeNames`
437
+ ## Pre-built CSS
297
438
 
298
- *Import from `@marianmeres/design-tokens/themes`*
439
+ _npm only — JSR does not support directory exports, so the pre-built CSS files are not available via JSR._
299
440
 
300
- Kebab-case theme name strings matching the generated CSS filenames (without extension). Typed as `readonly` tuple.
441
+ All bundled themes are also distributed as pre-built CSS files. Import via `@marianmeres/design-tokens/css/{theme-name}.css`.
301
442
 
302
443
  ```typescript
303
- import { themeNames } from "@marianmeres/design-tokens/themes";
444
+ import "@marianmeres/design-tokens/css/mauve-teal.css";
445
+ ```
304
446
 
305
- themeNames; // → ["amber-olive-safari", "blue-orange", ..., "zinc"]
447
+ The default prefix on the published files is `stuic-` (i.e. tokens are named `--stuic-color-primary`, etc.). To regenerate the CSS files locally with a different prefix, set the `CSS_PREFIX` environment variable before running the build:
448
+
449
+ ```bash
450
+ CSS_PREFIX=app- deno task css:build
306
451
  ```
452
+
453
+ Available files: `amber-olive-safari.css`, `blue-orange.css`, `cyan-red.css`, `mauve-teal.css`, `mist-indigo-fjord.css`, `taupe-rose-blush.css`, etc. (one per bundled theme).