@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.
- package/README.md +130 -0
- package/dist/index.d.ts +427 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +269 -0
- package/dist/index.js.map +1 -0
- package/dist/ink.d.ts +52 -0
- package/dist/ink.d.ts.map +1 -0
- package/dist/obligations.d.ts +45 -0
- package/dist/obligations.d.ts.map +1 -0
- package/dist/sections.d.ts +304 -0
- package/dist/sections.d.ts.map +1 -0
- package/package.json +43 -0
- package/theme-data.json +311 -0
- package/themes/acid.css +54 -0
- package/themes/bank-dark.css +64 -0
- package/themes/bank-private-dark.css +64 -0
- package/themes/bank-private.css +63 -0
- package/themes/bank.css +63 -0
- package/themes/catppuccin-latte-dark.css +64 -0
- package/themes/catppuccin-latte.css +63 -0
- package/themes/catppuccin-mocha-dark.css +64 -0
- package/themes/catppuccin-mocha.css +63 -0
- package/themes/cmyk.css +54 -0
- package/themes/coffee.css +54 -0
- package/themes/cyberpunk.css +54 -0
- package/themes/dim.css +54 -0
- package/themes/dracula-dark.css +64 -0
- package/themes/dracula.css +63 -0
- package/themes/forest.css +54 -0
- package/themes/kanzo-dark.css +73 -0
- package/themes/kanzo.css +70 -0
- package/themes/lemonade.css +54 -0
- package/themes/lofi.css +54 -0
- package/themes/luxury.css +54 -0
- package/themes/monochrome-dark.css +64 -0
- package/themes/monochrome.css +63 -0
- package/themes/night.css +54 -0
- package/themes/nord-dark.css +64 -0
- package/themes/nord.css +63 -0
- package/themes/sunset.css +54 -0
- package/themes/synthwave.css +54 -0
- package/themes/wireframe.css +54 -0
- package/themes.css +92 -0
- 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.
|
package/dist/index.d.ts
ADDED
|
@@ -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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"}
|