@kanzo-tech/theme 0.0.1-alpha

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 (44) hide show
  1. package/README.md +130 -0
  2. package/dist/index.d.ts +427 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +269 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/ink.d.ts +52 -0
  7. package/dist/ink.d.ts.map +1 -0
  8. package/dist/obligations.d.ts +45 -0
  9. package/dist/obligations.d.ts.map +1 -0
  10. package/dist/sections.d.ts +304 -0
  11. package/dist/sections.d.ts.map +1 -0
  12. package/package.json +43 -0
  13. package/theme-data.json +311 -0
  14. package/themes/acid.css +54 -0
  15. package/themes/bank-dark.css +64 -0
  16. package/themes/bank-private-dark.css +64 -0
  17. package/themes/bank-private.css +63 -0
  18. package/themes/bank.css +63 -0
  19. package/themes/catppuccin-latte-dark.css +64 -0
  20. package/themes/catppuccin-latte.css +63 -0
  21. package/themes/catppuccin-mocha-dark.css +64 -0
  22. package/themes/catppuccin-mocha.css +63 -0
  23. package/themes/cmyk.css +54 -0
  24. package/themes/coffee.css +54 -0
  25. package/themes/cyberpunk.css +54 -0
  26. package/themes/dim.css +54 -0
  27. package/themes/dracula-dark.css +64 -0
  28. package/themes/dracula.css +63 -0
  29. package/themes/forest.css +54 -0
  30. package/themes/kanzo-dark.css +73 -0
  31. package/themes/kanzo.css +70 -0
  32. package/themes/lemonade.css +54 -0
  33. package/themes/lofi.css +54 -0
  34. package/themes/luxury.css +54 -0
  35. package/themes/monochrome-dark.css +64 -0
  36. package/themes/monochrome.css +63 -0
  37. package/themes/night.css +54 -0
  38. package/themes/nord-dark.css +64 -0
  39. package/themes/nord.css +63 -0
  40. package/themes/sunset.css +54 -0
  41. package/themes/synthwave.css +54 -0
  42. package/themes/wireframe.css +54 -0
  43. package/themes.css +92 -0
  44. package/tokens.css +311 -0
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @kanzo-tech/theme
2
+
3
+ The theme catalogue and the axes a user layers over it. **A theme is one flat block of CSS, and it
4
+ carries one mode.**
5
+
6
+ - **Theme** — `packages/theme/themes/<name>.css`, hand-written source. Twenty-one authored colours,
7
+ the shape knobs, the font stacks and its own `color-scheme`. Selected with `data-theme`.
8
+ - **Radius** — `none` · `xs` · `sm` · `md` · `lg`, a user preference over the theme's own three
9
+ radius knobs.
10
+ - **Font** / **Mono font** — `--font-sans` / `--font-heading` / `--font-mono`.
11
+ - **Density** — the root font-size the whole `rem` scale resolves against.
12
+ - **Appearance** — `light` / `dark`, and it chooses *which theme* is worn, because a theme is a side.
13
+
14
+ This package ships **no components** and no colour maths. It is CSS, the declared axes and the value
15
+ types.
16
+
17
+ ## A theme
18
+
19
+ ```css
20
+ /* packages/theme/themes/acme.css */
21
+ [data-theme="acme"] {
22
+ color-scheme: light;
23
+ --background: #fbfcfd; --foreground: #10151c;
24
+ --primary: #1f6feb; --primary-foreground: #ffffff;
25
+ /* …nineteen more, then the shape knobs and the fonts… */
26
+ --radius-box: 0.75rem; --radius-field: 0.5rem; --radius-selector: 0.25rem;
27
+ --stroke: 1px; --depth: 0;
28
+ }
29
+ ```
30
+
31
+ That is the whole mechanism: a block somebody writes, an `@import` in `themes.css`, and an attribute
32
+ on `<html>`. Adding a client touches no code and needs no deploy.
33
+
34
+ **Twenty-one carry a value; everything else uses one.** `--card-foreground`, the sidebar tokens and
35
+ `--popover` are *uses*, bridged once in `tokens.css` through `@theme inline` and never re-declared —
36
+ which is also what makes a scoped `<div data-theme="…">` resolve correctly, since a custom property
37
+ declared in `:root` would inherit already substituted.
38
+
39
+ **Light and dark are two themes.** There is no second block and no `.dark` that flips a token; the
40
+ class survives only as the selector for the `dark:` variant at the call sites that still ask for one.
41
+
42
+ **There is no derivation.** A `@kanzo-tech/palette` package used to take two seeds through thirteen
43
+ stages and publish 144 reference steps; components used eighteen of them, and all eighteen were
44
+ tints that `color-mix` now computes at the point of use. It is deleted. What that costs is a contrast
45
+ guarantee at authoring time — the author answers for AA, and a guard over the shipped themes is what
46
+ catches a mistake.
47
+
48
+ ## How the other axes work
49
+
50
+ Every axis is a `data-*` attribute **on `<html>`**, and the token values behind it live in
51
+ `themes.css`. Change an attribute and every component re-skins, with no per-component work.
52
+
53
+ | Axis | Attribute | Sets |
54
+ |---|---|---|
55
+ | radius | `data-radius` | `--radius` |
56
+ | font | `data-font` | `--font-sans` |
57
+ | monoFont | `data-mono-font` | `--font-mono` |
58
+ | density | `data-font-size` | the root font-size |
59
+
60
+ **The attributes must be on `<html>`, not a wrapper element.** Ark UI's overlays — Dialog,
61
+ Popover, Menu, Select, Tooltip, Toast, HoverCard, Command — portal into `document.body`, outside
62
+ any wrapper you render, so tokens set on a wrapper never reach them.
63
+ Density is stricter still: it sets the root font-size, and every size in the system is `rem`.
64
+
65
+ Writing them is `<KanzoThemeProvider>`'s job, from `@kanzo-tech/ui`:
66
+
67
+ ```tsx
68
+ import { KanzoThemeProvider } from "@kanzo-tech/ui";
69
+ import "@kanzo-tech/ui/styles.css"; // once, at the root
70
+
71
+ <KanzoThemeProvider defaults={{ radius: "md", density: "compact" }}>
72
+ {children}
73
+ </KanzoThemeProvider>;
74
+ ```
75
+
76
+ Read or change the live preferences with `useKanzoTheme()`, or drop in the ready-made
77
+ `<Preferences />` panel. Both come from `@kanzo-tech/ui`.
78
+
79
+ ## Dark mode
80
+
81
+ Not owned here. The host toggles `.dark` on `<html>`, and the `.dark` block of the compiled
82
+ document keys off it. If you already run a theme manager, hand it to the provider:
83
+
84
+ ```tsx
85
+ import { useTheme } from "next-themes";
86
+
87
+ const { resolvedTheme, setTheme } = useTheme();
88
+ <KanzoThemeProvider appearance={{ resolvedTheme, setTheme }}>{children}</KanzoThemeProvider>;
89
+ ```
90
+
91
+ Omit the prop and the provider's built-in fallback toggles `.dark` itself.
92
+
93
+ ## SSR
94
+
95
+ Server-render the preferences with `themeScript()` from `@kanzo-tech/ui`, which writes the
96
+ attributes before first paint so there is no flash of the wrong theme. Pair it with
97
+ `cookieStorageAdapter()` so the server can read the same source from the request cookie:
98
+
99
+ ```tsx
100
+ import { themeScript, cookieStorageAdapter } from "@kanzo-tech/ui";
101
+
102
+ <head><script dangerouslySetInnerHTML={{ __html: themeScript() }} /></head>
103
+ <KanzoThemeProvider storage={cookieStorageAdapter()}>{children}</KanzoThemeProvider>
104
+ ```
105
+
106
+ ## What's in the package
107
+
108
+ The compiled styles ship with `@kanzo-tech/ui` (`import "@kanzo-tech/ui/styles.css"`), which
109
+ already pulls in this package's `tokens.css` + `themes.css`. Subpath exports
110
+ (`@kanzo-tech/theme/tokens.css`, `/themes.css`, `/themes/<name>.css`) are available for tooling —
111
+ the last one so a consumer can import a subset of the catalogue instead of all of it.
112
+
113
+ The non-colour axis tables — and the DECLARATION of every axis, `CORE_PREFS`, generated beside
114
+ them — are exported from the JS entry as `themeData` / `CORE_PREFS`. Import those, **not**
115
+ `@kanzo-tech/theme/theme-data.json`. A raw JSON subpath import is an ESM JSON import at
116
+ runtime, which Node rejects without `with { type: "json" }`, and Rollup strips that attribute
117
+ when bundling. `themeData.themes` is the catalogue, read off the `themes/` directory by the
118
+ generator, so adding a theme is adding a file and nothing lists them twice.
119
+
120
+ `CHART_SLOTS` is a fact about the **sheet** — how many `--chart-*` properties a theme publishes —
121
+ and a chart resolving them off the cascade runs in a browser. It is checked against what actually
122
+ ships rather than trusted: `packages/ui/src/lib/token-color.test.ts` reads every theme file and
123
+ fails on one that declares a partial set.
124
+
125
+ `themes.css` and `theme-data.json` are generated — **edit `scripts/gen-theme.mjs`, not those two.**
126
+ `pnpm gen` runs it; CI regenerates and fails on any diff.
127
+
128
+ **`tokens.css` and `themes/*.css` are NOT generated.** They are hand-written source, and a guard that
129
+ regenerated them would have nothing to regenerate them from. That is the whole shape of the change:
130
+ colour stopped being output.
@@ -0,0 +1,427 @@
1
+ import { default as themeDataJson } from '../theme-data.json';
2
+ import { SectionPrefDecl } from './sections.js';
3
+ /**
4
+ * The generated theme tables — the four non-colour axes — as a JS module.
5
+ *
6
+ * Consumers must read them through this export rather than importing
7
+ * `@kanzo-tech/theme/theme-data.json` directly. A raw JSON subpath import is an ESM JSON
8
+ * import at runtime, which Node rejects without `with { type: "json" }` — and Rollup strips
9
+ * that attribute when bundling, so there is no way to make the direct import survive a build.
10
+ * Bundling the data into this package's own JS entry is safe: it is the package that owns the
11
+ * data, so the copy can never skew from the CSS generated alongside it.
12
+ *
13
+ * There is no derivation any more, so there are no tables for one to read: a theme is
14
+ * `themes/<name>.css`, hand-written source, and `themes` below is the catalogue read off disk by
15
+ * the generator so no second list can drift from it.
16
+ */
17
+ export declare const themeData: {
18
+ radii: {
19
+ none: string;
20
+ xs: string;
21
+ sm: string;
22
+ md: string;
23
+ lg: string;
24
+ };
25
+ fonts: {
26
+ system: string;
27
+ geist: string;
28
+ inter: string;
29
+ };
30
+ monoFonts: {
31
+ system: string;
32
+ "geist-mono": string;
33
+ "jetbrains-mono": string;
34
+ };
35
+ densities: {
36
+ default: string;
37
+ compact: string;
38
+ comfortable: string;
39
+ };
40
+ themes: {
41
+ name: string;
42
+ dark: boolean;
43
+ }[];
44
+ prefs: {
45
+ appearance: {
46
+ kind: string;
47
+ options: {
48
+ value: string;
49
+ label: string;
50
+ }[];
51
+ default: string;
52
+ label: string;
53
+ doc: string;
54
+ };
55
+ radius: {
56
+ kind: string;
57
+ options: {
58
+ value: string;
59
+ label: string;
60
+ }[];
61
+ default: string;
62
+ attr: string;
63
+ source: string;
64
+ label: string;
65
+ doc: string;
66
+ };
67
+ font: {
68
+ kind: string;
69
+ options: {
70
+ value: string;
71
+ label: string;
72
+ }[];
73
+ default: string;
74
+ attr: string;
75
+ source: string;
76
+ label: string;
77
+ doc: string;
78
+ };
79
+ monoFont: {
80
+ kind: string;
81
+ options: {
82
+ value: string;
83
+ label: string;
84
+ }[];
85
+ default: string;
86
+ attr: string;
87
+ source: string;
88
+ label: string;
89
+ doc: string;
90
+ };
91
+ density: {
92
+ kind: string;
93
+ options: {
94
+ value: string;
95
+ label: string;
96
+ }[];
97
+ default: string;
98
+ attr: string;
99
+ source: string;
100
+ label: string;
101
+ doc: string;
102
+ };
103
+ themeByAppearance: {
104
+ kind: string;
105
+ options: {
106
+ from: string;
107
+ };
108
+ default: string;
109
+ attr: string;
110
+ source: string;
111
+ byAppearance: boolean;
112
+ label: string;
113
+ doc: string;
114
+ };
115
+ };
116
+ fallbacks: {
117
+ "--secondary-foreground": string[];
118
+ "--accent-foreground": string[];
119
+ "--popover": string[];
120
+ "--input": string[];
121
+ "--field": string[];
122
+ "--faint": string[];
123
+ "--destructive-foreground": string[];
124
+ "--info-foreground": string[];
125
+ "--success-foreground": string[];
126
+ "--warning-foreground": string[];
127
+ "--sidebar": string[];
128
+ "--sidebar-foreground": string[];
129
+ };
130
+ };
131
+ export type ThemeData = typeof themeDataJson;
132
+ /**
133
+ * The themes this package ships, as data a picker can render.
134
+ *
135
+ * daisyUI keeps two registries — `themeOrder` (the ordered names) and `theme/object` (name → the
136
+ * variable map) — precisely so a switcher can draw a theme without parsing its CSS. This is the
137
+ * first: an entry carries what a control needs to *offer* a theme and nothing a page needs to
138
+ * *paint* one, which is the theme's own stylesheet's job.
139
+ *
140
+ * **It replaces `paletteIndex`, and it is a flat list where that was a tree.** A palette used to
141
+ * contain identities, so an entry had `children` and a picker had two levels. A brand is a theme
142
+ * now, so `bank` and `bank-private` sit side by side and the second level is gone.
143
+ *
144
+ * Generated from the directory, not listed: `scripts/gen-theme.mjs` reads `themes/` and records
145
+ * each file's own `color-scheme`. Adding a theme is adding a file.
146
+ *
147
+ * Read through this export rather than importing `@kanzo-tech/theme/theme-data.json`, for the
148
+ * reason given on {@link themeData}: a raw JSON subpath import is an ESM JSON import at runtime, and
149
+ * Rollup strips the attribute that would make it legal.
150
+ */
151
+ export declare const themeIndex: ThemeIndexEntry[];
152
+ /**
153
+ * One theme, as a control sees it.
154
+ *
155
+ * **It carries no colours.** A theme travels in the page under its own `[data-theme]`, so a control
156
+ * depicts one by *setting the attribute* and letting the cascade answer — which is also why a
157
+ * preview is a `div` and not a strip of swatches. A handful of hexes could not depict a theme
158
+ * anyway; on the default's own, two of the four a picker used to publish were the same value.
159
+ *
160
+ * `dark` is the theme's own `color-scheme`, and it is the whole of what "a theme is one mode"
161
+ * means at this layer: it is a property of the theme, not a second axis crossed with it.
162
+ */
163
+ export interface ThemeIndexEntry {
164
+ name: string;
165
+ dark: boolean;
166
+ }
167
+ /**
168
+ * How many `--chart-N` custom properties the stylesheet declares. A 9th series folds into "Other" —
169
+ * never cycle, or identity stops meaning anything. (`--chart-capacity` is declared beside them and
170
+ * is not one of them; `boundary.test.ts` counts `--chart-N` only.)
171
+ *
172
+ * A fact about the SHEET, which is why it is here and not with the derivation that emits it: a
173
+ * chart resolving `var(--chart-N)` off the cascade needs the count, and a chart runs in a browser
174
+ * and is checked against the shipped theme files rather than trusted. The count is a compile-time constant
175
+ * because a stylesheet cannot have a variable number of custom properties.
176
+ *
177
+ * How many of the slots carry a *real* category is the document's `capacity`, which can be lower;
178
+ * it travels down the cascade as `--chart-capacity`, and past it `compile()` writes
179
+ * `var(--muted-foreground)`.
180
+ *
181
+ * `packages/palette` declares the same number, because it is what emits the properties. Neither
182
+ * copy is trusted: `boundary.test.ts` counts the declarations in the shipped `tokens.css` and
183
+ * holds both against it.
184
+ */
185
+ export declare const CHART_SLOTS = 8;
186
+ /**
187
+ * `@kanzo-tech/theme` — design tokens, the theme axis table, and the value types. No React.
188
+ *
189
+ * **Colour IS an axis now, and it is the same kind of axis as the rest.** A theme is one flat block
190
+ * of CSS under `[data-theme="<name>"]` — about fifty-five declarations somebody writes, pastes and
191
+ * diffs — so applying one is writing an attribute, exactly like radius or density. It stopped being
192
+ * special when it stopped being the output of a thirteen-stage derivation.
193
+ *
194
+ * Everything is driven by `data-*` attributes on `<html>`, and the values live in `themes.css`:
195
+ * · `data-theme` — selects a whole theme: its colours, its shape knobs and its fonts.
196
+ * · `data-radius` — the three radius knobs, as a user preference over the theme's own.
197
+ * · `data-font` — sets `--font-sans` / `--font-heading`.
198
+ * · `data-mono-font` — sets `--font-mono`.
199
+ * · `data-font-size` — sets the root font-size (the rem density scale).
200
+ *
201
+ * **`data-theme` is `data-palette` and `data-identity` collapsed, and the authority question they
202
+ * modelled has dissolved rather than been decided.** `data-palette` selected from a catalogue the
203
+ * LIBRARY shipped, so a user picking Dracula could overrule a client's branding; `data-identity`
204
+ * selected among brands the CLIENT authored, one level down. A tenant's brands are now themes
205
+ * beside every other theme, so what a user may choose is what the tenant's policy admits — the
206
+ * `pinned` / `hidden` chain that already governs every other section, rather than two attributes
207
+ * with different pedigrees.
208
+ *
209
+ * Writing the attributes is `<KanzoThemeProvider>`'s job, from `@kanzo-tech/ui`. It sets them on
210
+ * `document.documentElement` — see the AXES note below for why a wrapper element cannot work.
211
+ *
212
+ * Dark mode is not a token flip any more: a theme carries its own `color-scheme` and its own
213
+ * colours, so `.dark` survives only as the selector for the `dark:` VARIANT at the call sites that
214
+ * still ask for one.
215
+ *
216
+ * Requires `@kanzo-tech/ui/styles.css` (or the raw token/theme CSS) imported once at the root.
217
+ */
218
+ /**
219
+ * A side of the compiled document. `compile()` always emits both blocks, so there are exactly two.
220
+ *
221
+ * **There is no `"system"`, and its absence is the design.** Following the OS is a real behaviour we
222
+ * keep — without it the first visit has to guess, and guessing wrong flashes white at every
223
+ * dark-mode user — but it is the state with *no* value, not a third value.
224
+ *
225
+ * That split is the reference systems', and they divide on which layer they are. The JS
226
+ * theme-switching libraries make it a value: next-themes ships `defaultTheme = "system"` and appends
227
+ * `"system"` to its `themes` array, MUI has `mode: "light" | "dark" | "system"`, Mantine `"auto"`.
228
+ * The *token* layers do not: daisyUI writes `themes: light --default, dark --prefersdark`, where the
229
+ * OS preference is a flag on a theme and `data-theme` overrides it; Tailwind has a media query or a
230
+ * class; Radix Themes declines to model it and delegates to next-themes. And CSS itself has no third
231
+ * keyword — `color-scheme: light dark` means "the OS decides", and an explicit side overrides.
232
+ *
233
+ * We are a token layer: a document with a `:root` block and a `.dark` block. `"system"` arrived here
234
+ * as next-themes vocabulary for a mechanism we do not use, and `themeScript` never believed in it —
235
+ * it has always resolved "anything that is not an explicit side" against `matchMedia`.
236
+ *
237
+ * A host next-themes IS still supported; `KanzoThemeProvider` translates its `"system"` to `null` in
238
+ * one place, the way every other foreign vocabulary enters this system.
239
+ */
240
+ export type Appearance = "light" | "dark";
241
+ /**
242
+ * The appearance PREFERENCE — an explicit side, or `""` for "ask the OS".
243
+ *
244
+ * A value and not an absent key: the read-time whitelist is built from `Object.keys(DEFAULT_PREFS)`,
245
+ * so a key missing from the default blob is dropped on every read. It also survives
246
+ * `JSON.stringify` into both storage adapters, which an `undefined` would not.
247
+ *
248
+ * **`""` and not `null`, which is what it was.** Unset is the same value here as everywhere else in
249
+ * this package: a theme key stores `""` for "defer to the tenant", and the write rule
250
+ * removes an attribute at the default. Two spellings of one idea is what kept appearance out of the
251
+ * declaration — a `SectionPrefDecl`'s values are strings — and therefore out of the one resolution
252
+ * chain, which is the whole of what {@link CORE_PREFS} exists to end. Declared, "follow the OS" is
253
+ * `{ value: "", label: "System" }`: a thing a control can offer, rather than something reachable
254
+ * only through the panel's Reset button.
255
+ */
256
+ export type AppearancePref = Appearance | "";
257
+ /** Radius steps (`md` = 0.5rem default). */
258
+ export type KanzoRadius = "none" | "xs" | "sm" | "md" | "lg";
259
+ /** Density (root font-size rem-scale); `default` omits the attribute. */
260
+ export type KanzoDensity = "default" | "compact" | "comfortable";
261
+ /** Sans font key — host-extensible; the DS ships `system`/`geist`/`inter` stacks. */
262
+ export type KanzoFont = "system" | "geist" | "inter" | (string & {});
263
+ /** Mono font key — host-extensible; the DS ships `system`/`geist-mono`/`jetbrains-mono`. */
264
+ export type KanzoMonoFont = "system" | "geist-mono" | "jetbrains-mono" | (string & {});
265
+ /**
266
+ * Theme key — host-extensible; `""` means "defer to the tenant's default".
267
+ *
268
+ * This is `KanzoFont`'s case, not `KanzoRadius`': a value is a *host's* string, unknown when this
269
+ * package is built. Where `KanzoFont` still names the three stacks the DS happens to ship, there is
270
+ * nothing to union here — a tenant authors their own themes, so a literal union would be a list
271
+ * that is wrong for every client.
272
+ *
273
+ * **It replaces `KanzoPalette`, `KanzoIdentity` and `KanzoIdentityMemory`, and the collapse is the
274
+ * point.** Those were three types because a palette CONTAINED identities: a document was a two-mode
275
+ * stylesheet, a brand was a partial block layered onto it, and a memory recorded which brand you
276
+ * last wore inside each document so switching away and back returned you to it. A theme is one flat
277
+ * block, so a brand is not inside anything — `bank` and `bank-private` are two themes — and there is
278
+ * no containment left for a memory to remember.
279
+ */
280
+ export type KanzoThemeName = string;
281
+ /**
282
+ * One published theme, as the runtime sees it — the contract between the catalogue and the panel.
283
+ *
284
+ * It carries no colours, and it has no `children`. A control depicts a theme by setting
285
+ * `data-theme` on an element and letting the cascade paint it: the theme is already in the page, so
286
+ * a depiction copied out of it is a second spelling that can only ever be the same colours or the
287
+ * wrong ones.
288
+ *
289
+ * **`children` went with the containment it modelled.** A palette used to hold brands, so an option
290
+ * held options and the panel flattened a tree into one list. There is no tree: a brand is a theme.
291
+ */
292
+ export interface ThemeOption {
293
+ value: string;
294
+ label: string;
295
+ }
296
+ /**
297
+ * The user's preferences. Six, and only one of them is a colour.
298
+ *
299
+ * *Free* colour left this table entirely: `palette`, `base`, `accent`, `primary`, `baseTint`,
300
+ * `scheme` and `schemeColors` were seven ways to express *part* of a palette at runtime, and a
301
+ * document expresses all of it at once, before a byte is sent. `appearance` stays because it is
302
+ * the one colour-adjacent thing a user genuinely chooses, and it selects between two blocks of one
303
+ * document. `identity` is the same kind of choice one level up: between blocks the TENANT
304
+ * published, and never between a value they did not.
305
+ */
306
+ export interface ThemePrefs {
307
+ appearance: AppearancePref;
308
+ radius: KanzoRadius;
309
+ font: KanzoFont;
310
+ monoFont: KanzoMonoFont;
311
+ density: KanzoDensity;
312
+ /**
313
+ * Which theme this user wears on each side — `{}` while they have chosen neither.
314
+ *
315
+ * A map rather than a string because the choice is per appearance, and with one-mode themes that
316
+ * is not a refinement but the only shape available: a theme IS a side, so "which theme" without
317
+ * "on which side" does not name a preference. It is also exactly what the per-appearance palette
318
+ * proposal asked for, arrived at from the other
319
+ * direction — that proposal invented a map over documents that each carried both modes, to say
320
+ * something the two-mode document made awkward and the one-mode theme makes trivial.
321
+ *
322
+ * The ordering that makes it possible: the pre-hydration script resolves appearance before it
323
+ * writes anything, so it can index this map. That is the thing to check first when touching it.
324
+ */
325
+ themeByAppearance: Partial<Record<Appearance, KanzoThemeName>>;
326
+ /**
327
+ * What the packages a host installed contribute, keyed by namespace then by preference.
328
+ *
329
+ * **One key, and that is what makes an absent package harmless.** The read-time whitelist is built
330
+ * from `Object.keys(DEFAULT_PREFS)` and drops everything else, which is right for the retired
331
+ * colour axes it was built for and exactly wrong for a contributed choice: a host that drops an
332
+ * optional peer for one release would lose the user's stored value on the next write. Riding on a
333
+ * single known key, an unrecognised namespace survives every read and save without the core
334
+ * knowing it exists — the same opacity {@link LookDocument}'s `sections` already has, which is the
335
+ * point: both halves of a section are stored the same way.
336
+ */
337
+ sections: Record<string, Record<string, string>>;
338
+ }
339
+ /**
340
+ * Every key of `ThemePrefs`, with no exception for the ones whose default is empty.
341
+ *
342
+ * `PREF_KEYS` in the provider is `Object.keys(DEFAULT_PREFS)`, and the read-time whitelist built
343
+ * from it drops anything not listed. A pref missing here therefore works for exactly one session
344
+ * and is gone on the next read, silently and with no type error — the retired-key hygiene rule
345
+ * turned on a live field.
346
+ */
347
+ export declare const DEFAULT_PREFS: ThemePrefs;
348
+ export declare const STORAGE_KEY = "kanzo_theme_prefs";
349
+ /**
350
+ * The core's own preferences, declared — one entry per axis, in the shape a package contributes.
351
+ *
352
+ * **Generated, and that is the point.** `scripts/gen-theme.mjs` authors the values *and* the
353
+ * declaration, so the option list a control offers is the table the CSS was emitted from rather than
354
+ * a hand-copy beside it. `Preferences.tsx` held two such copies — `RADII` and `DENSITIES` — sitting
355
+ * next to the generated tables they duplicated, and `KanzoThemeProvider` held a third of the font
356
+ * stacks with a fallback string that had already drifted from the sheet's. Adding a font is now one
357
+ * line in the generator: the panel grows a card and the docs table grows a row.
358
+ *
359
+ * The rule this installs, and it is the same one the colour half follows: **a configuration is
360
+ * authored once, where its values live.** The declaration, the control, the default and the
361
+ * attribute are resolved from it.
362
+ *
363
+ * `source` is the one field a contributed preference has no use for. It says WHO emits the selectors
364
+ * the attribute matches: `"themes"` is our generator, so the drift guards in `index.test.ts` can
365
+ * hold the declaration against the sheet; `"document"` is `compile()`, from something a TENANT
366
+ * authored after this package was built, and asserting a generated table for it would fail for the
367
+ * right feature.
368
+ */
369
+ export type CorePrefDecl = SectionPrefDecl & {
370
+ source?: "themes" | "document";
371
+ };
372
+ /**
373
+ * Cast, because JSON is data and TypeScript reads it as widened literals — `kind: string` will not
374
+ * narrow to the union however it is written. The check is therefore a runtime one, in
375
+ * `index.test.ts`: every key is a key of `DEFAULT_PREFS`, every declaration is well-formed, and
376
+ * `check:generated` regenerates the file and fails on a diff.
377
+ */
378
+ export declare const CORE_PREFS: Readonly<Record<CorePrefKey, CorePrefDecl>>;
379
+ /**
380
+ * Every preference the core declares — which is every key of {@link ThemePrefs} except the two that
381
+ * are not choices at all.
382
+ *
383
+ * Written as an exclusion rather than a list, so the two exceptions have to justify themselves:
384
+ * `identityByPalette` is a *memory* (what this user last wore in each document, consulted only when
385
+ * the palette changes), and `sections` is the opaque bag another package's preferences ride in. A
386
+ * new axis appears here by appearing in `ThemePrefs`, and the generator has to answer for it.
387
+ */
388
+ export type CorePrefKey = Exclude<keyof ThemePrefs, "sections">;
389
+ /**
390
+ * The namespace the core's own preferences answer to in a tenant's policy.
391
+ *
392
+ * **The core is a section like any other, and this is the whole of what that costs.** A policy is
393
+ * keyed by namespace — `{ theme: { radius: { pinned: "sm" } }, graph: { look: { hidden: true } } }`
394
+ * — so a client shipping *compact and square* uses the mechanism an optional package already uses,
395
+ * and one chain answers for colour, geometry and a contributed choice alike.
396
+ *
397
+ * That is daisyUI's insight, in the mechanism this repo already had: their theme carries the
398
+ * geometry (`--radius-box`, `--size-field`, `--depth`) in the same document as the colours, so a
399
+ * tenant ships a coherent whole rather than a panel of unrelated knobs. Ours went half-way there
400
+ * when a palette became a document; the half not taken was that radius, density and the fonts had no
401
+ * document-level default at all — only a user could move them.
402
+ */
403
+ export declare const CORE_NAMESPACE = "theme";
404
+ /**
405
+ * Each axis → its `<html>` attribute + default value (at the default the attribute is removed).
406
+ *
407
+ * A projection of {@link CORE_PREFS} and no longer a table of its own: three things must agree about
408
+ * an axis — the React provider, the SSR pre-hydration script, and the generator that decides which
409
+ * selectors exist at all — and they now agree because there is one place to disagree with.
410
+ *
411
+ * These attributes go on `<html>`, never a wrapper element. Ark overlays (Dialog, Popover,
412
+ * Menu, Select, Tooltip, Toast…) portal to `document.body`, outside any wrapper, so tokens set
413
+ * on a wrapper would not reach them. `density` additionally *must* be on the root: it sets the
414
+ * root font-size and every size in the system is `rem`.
415
+ */
416
+ export declare const AXES: {
417
+ key: keyof ThemePrefs;
418
+ attr: string;
419
+ def: string;
420
+ source: "themes" | "document";
421
+ /** See {@link SectionPrefDecl}. The stored value is a map keyed by the resolved appearance. */
422
+ byAppearance?: true;
423
+ }[];
424
+ export { fallbackChain, resolvePref, prefBoolean, prefNumber, prefOptions, resolveSectionToken, sectionOf, validatePrefs, validateSection, withSection, type LookDocument, type PrefOption, type PrefOptions, type PrefOrigin, type PrefSource, type PrefSources, type Problem, type ResolvedPref, type SectionManifest, type SectionPolicy, type SectionPrefDecl, type SectionPrefPolicy, type SectionTokenDecl, } from './sections.js';
425
+ export { check as checkDensity, OBLIGATIONS as DENSITY_OBLIGATIONS, type Check as DensityCheck, type Obligation as DensityObligation, } from './obligations.js';
426
+ export { AA, contrast, hex, inkFor, oklch, pageInk, type Oklch, } from './ink.js';
427
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}