@latty-ds/tokens 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Latty Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # @latty-ds/tokens
2
+
3
+ Design token package for the Latty Design System. Generates CSS custom properties, a JSON token object, and a browser-runtime API from a single configuration file.
4
+
5
+ ---
6
+
7
+ ## How it works
8
+
9
+ The build pipeline takes `tokens.config.json` as input and runs in three steps:
10
+
11
+ ```text
12
+ tokens.config.json
13
+ ↓
14
+ tsup (compile build script)
15
+ ↓
16
+ node dist-scripts/build-tokens.js
17
+ ↓
18
+ dist/tokens.css ← primitive CSS custom properties
19
+ dist/semantic.css ← semantic CSS custom properties (var() references)
20
+ dist/tokens.json ← full token object (primitives + semantic map)
21
+ dist/tokens.js ← ES module export of token object
22
+ ↓
23
+ tsc (generate public type definitions)
24
+ ↓
25
+ dist/public-types.d.ts
26
+ ```
27
+
28
+ In addition, `tsup` also bundles `src/configure/` into `dist/configure.js` — a browser-safe module that runs the same generation at runtime, so consumers can customise the palette without a build step.
29
+
30
+ ---
31
+
32
+ ## Configuring your palette
33
+
34
+ Edit `tokens.config.json` to set the base hex for each semantic color. The build generates a full 10-step OKLCH scale (50–900, plus a desaturated muted variant) from each value.
35
+
36
+ ```json
37
+ {
38
+ "color": {
39
+ "primary": "#05b8e1",
40
+ "secondary": "#e26d05",
41
+ "success": "#22c55e",
42
+ "warning": "#eeb308",
43
+ "error": "#ef4444",
44
+ "info": "#0ea5e9"
45
+ }
46
+ }
47
+ ```
48
+
49
+ Then rebuild:
50
+
51
+ ```bash
52
+ pnpm --filter @latty-ds/tokens build
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Consuming tokens
58
+
59
+ ### Option A — Static CSS import
60
+
61
+ Import once in your app's global stylesheet. Requires a build step.
62
+
63
+ ```css
64
+ /* Primitive tokens (color scales, spacing, elevation, …) */
65
+ @import '@latty-ds/tokens/tokens.css';
66
+
67
+ /* Semantic tokens (intent-based, reference primitives via var()) */
68
+ @import '@latty-ds/tokens/semantic.css';
69
+ ```
70
+
71
+ ### Option B — Runtime `configure()` (no build step for the consumer)
72
+
73
+ Call `configure()` once at your app's entry point. It generates the full token set from the provided config and injects it into `document.head`. Useful when the palette must be determined at runtime (e.g. white-labelling, per-tenant theming).
74
+
75
+ ```ts
76
+ import { configure } from '@latty-ds/tokens/configure';
77
+
78
+ configure({
79
+ colors: {
80
+ primary: '#6366f1',
81
+ secondary: '#f59e0b'
82
+ },
83
+ font: { family: 'Inter, sans-serif' },
84
+ border: { radius: '0.375rem' }
85
+ });
86
+ ```
87
+
88
+ All fields are optional — omitted values fall back to the defaults in `tokens.config.json`. Can be called again to hot-swap the theme; the existing `<style>` element is replaced.
89
+
90
+ ### Option C — SSR (`createStyleSheet`)
91
+
92
+ For server-side rendering, use `createStyleSheet()` to get the CSS string and inject it yourself.
93
+
94
+ ```ts
95
+ import { createStyleSheet } from '@latty-ds/tokens/configure';
96
+
97
+ const css = createStyleSheet({ colors: { primary: '#6366f1' } });
98
+ // inject `css` into a <style> tag in your server-rendered HTML
99
+ ```
100
+
101
+ ### Option D — JS token object
102
+
103
+ Import the static token object for programmatic access (e.g. in a Tailwind config or a JS-driven style system).
104
+
105
+ ```ts
106
+ import { tokens } from '@latty-ds/tokens';
107
+
108
+ console.log(tokens.color.primary['500']); // "#0097be"
109
+ console.log(tokens.spacing.rem['4']); // "1rem"
110
+ console.log(tokens.elevation['2']); // "0 4px 6px -1px rgb(…)"
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Token reference
116
+
117
+ ### Primitive tokens (`tokens.css`)
118
+
119
+ | Prefix | Example | Description |
120
+ | --------------------------------------- | ------------------------------ | --------------------------------- |
121
+ | `--lt-color-[name]-[step]` | `--lt-color-primary-500` | Color palette steps 50–900 |
122
+ | `--lt-color-[name]-muted-[step]` | `--lt-color-primary-muted-300` | Desaturated variant of each color |
123
+ | `--lt-color-neutral-[step]` | `--lt-color-neutral-100` | Greyscale scale |
124
+ | `--lt-color-white` / `--lt-color-black` | | System colors |
125
+ | `--lt-spacing-[n]` | `--lt-spacing-4` → `1rem` | Spacing scale (rem), n = 0–24 |
126
+ | `--lt-spacing-px-[n]` | `--lt-spacing-px-4` → `16px` | Spacing scale (px) |
127
+ | `--lt-border-radius` | `0.5rem` | Global border radius |
128
+ | `--lt-elevation-[n]` | `--lt-elevation-2` | Box shadows, n = 0–5 |
129
+ | `--lt-typography-fontFamily` | `"Hanken Grotesk", sans-serif` | Base font family |
130
+
131
+ Color names: `primary`, `secondary`, `success`, `warning`, `error`, `info` (each with a `-muted` variant).
132
+
133
+ ### Semantic tokens (`semantic.css`)
134
+
135
+ Semantic tokens encode intent, not raw values. They reference primitives via `var()` so theming only requires overriding this layer.
136
+
137
+ | Group | Example | Intent |
138
+ | -------------------- | ----------------------------------- | ------------------------------ |
139
+ | `--lt-text-*` | `--lt-text-default` | Foreground / text colors |
140
+ | `--lt-bg-*` | `--lt-bg-surface` | Background fills |
141
+ | `--lt-border-*` | `--lt-border-focus` | Stroke colors |
142
+ | `--lt-interactive-*` | `--lt-interactive-primary-bg-hover` | Stateful component backgrounds |
143
+
144
+ Key semantic tokens:
145
+
146
+ ```css
147
+ /* Text */
148
+ --lt-text-default → --lt-color-neutral-900 /* body text */
149
+ --lt-text-subtle → --lt-color-neutral-600 /* secondary / captions */
150
+ --lt-text-muted → --lt-color-neutral-400 /* placeholder, hints */
151
+ --lt-text-inverse → --lt-color-white /* text on dark bg */
152
+ --lt-text-[variant] → --lt-color-[variant]-700 /* colored label text */
153
+ --lt-text-on-[variant] → white or neutral-900 /* text on solid colored bg */
154
+
155
+ /* Background */
156
+ --lt-bg-default → --lt-color-white
157
+ --lt-bg-surface → --lt-color-neutral-100 /* cards, panels */
158
+ --lt-bg-[variant] → --lt-color-[variant]-500 /* solid colored bg */
159
+ --lt-bg-[variant]-subtle → --lt-color-[variant]-100 /* tinted bg (chips, badges) */
160
+
161
+ /* Border */
162
+ --lt-border-default → --lt-color-neutral-200
163
+ --lt-border-focus → --lt-color-primary-400 /* focus ring */
164
+ --lt-border-[variant] → --lt-color-[variant]-200 /* subtle colored border */
165
+ --lt-border-[variant]-strong → --lt-color-[variant]-500
166
+
167
+ /* Interactive */
168
+ --lt-interactive-[variant]-bg → --lt-color-[variant]-500
169
+ --lt-interactive-[variant]-bg-hover → --lt-color-[variant]-600
170
+ --lt-interactive-[variant]-bg-active → --lt-color-[variant]-700
171
+ ```
172
+
173
+ `[variant]` = `primary` | `secondary` | `success` | `warning` | `error` | `info`
174
+
175
+ ---
176
+
177
+ ## Package exports
178
+
179
+ | Export | File | Use |
180
+ | ------------------------------- | ------------------- | -------------------------------------------- |
181
+ | `@latty-ds/tokens` | `dist/index.js` | JS token object |
182
+ | `@latty-ds/tokens/configure` | `dist/configure.js` | Runtime `configure()` / `createStyleSheet()` |
183
+ | `@latty-ds/tokens/tokens.css` | `dist/tokens.css` | Primitive CSS custom properties |
184
+ | `@latty-ds/tokens/semantic.css` | `dist/semantic.css` | Semantic CSS custom properties |
185
+ | `@latty-ds/tokens/tokens.json` | `dist/tokens.json` | Full token object as JSON |
186
+
187
+ ---
188
+
189
+ ## Source structure
190
+
191
+ ```text
192
+ src/
193
+ constants/ Shared constants (color steps, spacing base, default radius/font)
194
+ types/ TypeScript type definitions (Tokens, LattyConfig, palette types, …)
195
+
196
+ colors/ OKLCH color palette generator (main + muted scales)
197
+ spacing/ Spacing scale generator (rem + px, 0–24 steps)
198
+ elevation/ Box-shadow elevation generator (0–5 levels)
199
+ semantic/ Semantic token map (intent → primitive reference)
200
+
201
+ core/ Orchestration — combines generators into a Tokens object;
202
+ exports tokensToCss and semanticTokensToCss
203
+
204
+ configure/ Public browser-runtime API (configure, createStyleSheet)
205
+ Includes unit tests
206
+
207
+ build/ Node.js build pipeline (not browser-safe)
208
+ colors.ts Color token builder helper
209
+ tokens.ts Entry point — reads config, writes dist/ files
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Development
215
+
216
+ ```bash
217
+ # Build the package (all three steps)
218
+ pnpm --filter @latty-ds/tokens build
219
+
220
+ # Run tests
221
+ pnpm test
222
+
223
+ # Clean dist
224
+ pnpm --filter @latty-ds/tokens clean
225
+ ```
@@ -0,0 +1,54 @@
1
+ interface LattyConfig {
2
+ /** Built-in names: primary, secondary, success, warning, error, info. Any additional name generates a new palette. */
3
+ colors?: Record<string, string>;
4
+ font?: {
5
+ family?: string;
6
+ };
7
+ border?: {
8
+ radius?: string;
9
+ };
10
+ /**
11
+ * Controls which semantic token layer is emitted.
12
+ * - `'light'` (default) — light tokens only; ignores OS preference
13
+ * - `'dark'` — dark tokens only; ignores OS preference
14
+ * - `'system'` — light at `:root`, dark inside `@media (prefers-color-scheme: dark)` + `[data-theme]` overrides
15
+ * - `'auto'` — alias for `'system'`, deprecated
16
+ */
17
+ theme?: 'light' | 'dark' | 'system' | 'auto';
18
+ }
19
+
20
+ /**
21
+ * Generates a complete CSS stylesheet string (primitive + semantic tokens)
22
+ * from the provided config. Useful for SSR — inject the returned string into
23
+ * a `<style>` tag server-side.
24
+ */
25
+ declare const createStyleSheet: (userConfig?: LattyConfig) => string;
26
+ /**
27
+ * Injects design tokens into the document as a `<style>` element.
28
+ * Call once at your app's entry point — before any components render.
29
+ *
30
+ * Add `data-lt` to `<html>` to enable automatic FOUC prevention: configure()
31
+ * hides the document during token injection and reveals it on the next frame.
32
+ * For zero FOUC on initial load, also add this one-liner before your module script:
33
+ * `<style>html[data-lt]:not([data-lt-ready]){visibility:hidden}</style>`
34
+ *
35
+ * @example
36
+ * ```html
37
+ * <html lang="en" data-lt>
38
+ * <head>
39
+ * <style>html[data-lt]:not([data-lt-ready]){visibility:hidden}</style>
40
+ * <script type="module">
41
+ * import { configure } from '@latty-ds/tokens/configure';
42
+ * configure({
43
+ * colors: { primary: '#6366f1', secondary: '#f59e0b' },
44
+ * font: { family: 'Inter, sans-serif' },
45
+ * border: { radius: '0.375rem' },
46
+ * });
47
+ * </script>
48
+ * </head>
49
+ * </html>
50
+ * ```
51
+ */
52
+ declare const configure: (userConfig?: LattyConfig) => void;
53
+
54
+ export { type LattyConfig, configure, createStyleSheet };
@@ -0,0 +1,371 @@
1
+ // src/constants/colors.constants.ts
2
+ var STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
3
+ var L_BY_STEP = {
4
+ 50: 0.93,
5
+ 100: 0.89,
6
+ 200: 0.81,
7
+ 300: 0.76,
8
+ 400: 0.7,
9
+ 500: 0.62,
10
+ 600: 0.54,
11
+ 700: 0.42,
12
+ 800: 0.36,
13
+ 900: 0.27
14
+ };
15
+ var L_BY_STEP_NEUTRAL = {
16
+ 50: 0.985,
17
+ 100: 0.965,
18
+ 200: 0.92,
19
+ 300: 0.84,
20
+ 400: 0.74,
21
+ 500: 0.62,
22
+ 600: 0.52,
23
+ 700: 0.42,
24
+ 800: 0.32,
25
+ 900: 0.22
26
+ };
27
+
28
+ // src/constants/spacing.constants.ts
29
+ var SPACE_BASE_PX = 4;
30
+ var SPACE_STEPS = 24;
31
+ var REM_BASE_PX = 16;
32
+
33
+ // src/constants/tokens.constants.ts
34
+ var DEFAULT_BORDER_RADIUS = "0.5rem";
35
+ var DEFAULT_FONT_FAMILY = '"Hanken Grotesk", sans-serif';
36
+
37
+ // src/spacing/spacing.ts
38
+ var buildSpacing = () => {
39
+ const px = {};
40
+ const rem = {};
41
+ for (let i = 0; i <= SPACE_STEPS; i++) {
42
+ const v = i * SPACE_BASE_PX;
43
+ px[i] = `${v}px`;
44
+ rem[i] = `${v / REM_BASE_PX}rem`;
45
+ }
46
+ return { px, rem };
47
+ };
48
+
49
+ // src/elevation/elevation.ts
50
+ var buildElevation = (neutral) => {
51
+ const shadowColor = neutral["900"] || "#171717";
52
+ const hex = shadowColor.replace("#", "");
53
+ const r = parseInt(hex.substring(0, 2), 16);
54
+ const g = parseInt(hex.substring(2, 4), 16);
55
+ const b = parseInt(hex.substring(4, 6), 16);
56
+ const rgb = `${r} ${g} ${b}`;
57
+ return {
58
+ "0": "none",
59
+ "1": `0 1px 3px 0 rgb(${rgb} / 0.1), 0 1px 2px 0 rgb(${rgb} / 0.06)`,
60
+ "2": `0 4px 6px -1px rgb(${rgb} / 0.1), 0 2px 4px -1px rgb(${rgb} / 0.06)`,
61
+ "3": `0 10px 15px -3px rgb(${rgb} / 0.1), 0 4px 6px -2px rgb(${rgb} / 0.05)`,
62
+ "4": `0 20px 25px -5px rgb(${rgb} / 0.1), 0 10px 10px -5px rgb(${rgb} / 0.04)`,
63
+ "5": `0 25px 50px -12px rgb(${rgb} / 0.25)`
64
+ };
65
+ };
66
+
67
+ // src/colors/palette.ts
68
+ import { formatHex, converter } from "culori";
69
+ var toOklch = converter("oklch");
70
+ var toRgb = converter("rgb");
71
+ var clamp = (n, min, max) => Math.max(min, Math.min(max, n));
72
+ var rampFromBase = (base, opts) => {
73
+ const chromaScale = opts?.chromaScale ?? 1;
74
+ const lPivot = opts?.lPivot ?? L_BY_STEP[500];
75
+ const midBoost = opts?.midBoost ?? 0;
76
+ const darkClamp = opts?.darkClamp ?? 0.06;
77
+ const minChromaFactor = opts?.minChromaFactor ?? 0.08;
78
+ const out = {};
79
+ const h = (base.h % 360 + 360) % 360;
80
+ const cMax = clamp(base.c * chromaScale, 0, 0.5);
81
+ const c500 = L_BY_STEP[500];
82
+ const c50 = L_BY_STEP[50];
83
+ const c900 = L_BY_STEP[900];
84
+ for (const step of STEPS) {
85
+ const canonical = L_BY_STEP[step];
86
+ let lTarget;
87
+ if (canonical >= c500) {
88
+ const t2 = (canonical - c500) / (c50 - c500);
89
+ lTarget = lPivot + t2 * (c50 - lPivot);
90
+ } else {
91
+ const t2 = (canonical - c500) / (c900 - c500);
92
+ lTarget = lPivot + t2 * (c900 - lPivot);
93
+ }
94
+ lTarget = clamp(lTarget, 0, 1);
95
+ const t = STEPS.indexOf(step) / (STEPS.length - 1);
96
+ const peak = clamp(1 - Math.pow(Math.abs(t - 0.55) / 0.55, 1.7), 0, 1);
97
+ const cMin = cMax * minChromaFactor;
98
+ let c = clamp(cMin + (cMax - cMin) * peak, 0, 0.5);
99
+ c = clamp(c * (1 + midBoost * peak), 0, 0.5);
100
+ if (step >= 800) c = Math.min(c, darkClamp);
101
+ const color = { mode: "oklch", l: lTarget, c, h };
102
+ const rgb = toRgb(color);
103
+ out[step] = formatHex(rgb);
104
+ }
105
+ return out;
106
+ };
107
+ var generatePalettes = (hex, useMuted) => {
108
+ const base = toOklch(hex);
109
+ if (!base) throw new Error(`Invalid color: ${hex}`);
110
+ const main = rampFromBase(base, {
111
+ chromaScale: 1,
112
+ lPivot: base.l,
113
+ midBoost: 0,
114
+ darkClamp: 0.06
115
+ });
116
+ main[500] = formatHex(hex) ?? hex;
117
+ const muted = useMuted ? rampFromBase(base, {
118
+ chromaScale: 0.2,
119
+ lPivot: clamp(base.l + 0.04, 0, 1),
120
+ midBoost: -0.2,
121
+ darkClamp: 0.045,
122
+ minChromaFactor: 0.06
123
+ }) : null;
124
+ return { main, muted };
125
+ };
126
+ var generateNeutralScale = () => {
127
+ const out = {};
128
+ for (const step of STEPS) {
129
+ const l = L_BY_STEP_NEUTRAL[step];
130
+ const color = { mode: "oklch", l, c: 0, h: 0 };
131
+ out[step] = formatHex(toRgb(color));
132
+ }
133
+ return out;
134
+ };
135
+ var generateBWColors = () => {
136
+ return {
137
+ white: "#FFFFFF",
138
+ black: "#000000"
139
+ };
140
+ };
141
+
142
+ // src/build/colors.ts
143
+ var toStepObj = (pal) => STEPS.reduce((acc, s) => {
144
+ acc[`${s}`] = pal[s];
145
+ return acc;
146
+ }, {});
147
+ var buildColorTokens = (cfg) => {
148
+ const color = {};
149
+ for (const [name, value] of Object.entries(cfg.color)) {
150
+ const palettes = generatePalettes(value, true);
151
+ color[name] = toStepObj(palettes.main);
152
+ if (palettes.muted) {
153
+ color[`${name}-muted`] = toStepObj(palettes.muted);
154
+ }
155
+ }
156
+ return color;
157
+ };
158
+ var addSystemColors = (color) => {
159
+ color.neutral = toStepObj(generateNeutralScale());
160
+ return { ...color, ...generateBWColors() };
161
+ };
162
+
163
+ // src/core/generate.ts
164
+ var buildTokens = (cfg) => {
165
+ let color = buildColorTokens(cfg);
166
+ color = addSystemColors(color);
167
+ const spacing = buildSpacing();
168
+ const border = { radius: DEFAULT_BORDER_RADIUS, square: false };
169
+ const typography = { fontFamily: DEFAULT_FONT_FAMILY };
170
+ const elevation = buildElevation(color.neutral);
171
+ return { color, spacing, border, typography, elevation };
172
+ };
173
+ var tokensToCss = (tokens) => {
174
+ const lines = [];
175
+ const walker = (value, parts) => {
176
+ if (typeof value === "string") {
177
+ lines.push(` --lt-${parts.join("-")}: ${value};`);
178
+ return;
179
+ }
180
+ if (!value || typeof value !== "object") return;
181
+ for (const key of Object.keys(value).sort()) {
182
+ const next = value[key];
183
+ if (parts.length === 1 && parts[0] === "spacing" && (key === "rem" || key === "px")) {
184
+ if (key === "rem") walker(next, ["spacing"]);
185
+ else walker(next, ["spacing", "px"]);
186
+ continue;
187
+ }
188
+ walker(next, [...parts, key]);
189
+ }
190
+ };
191
+ walker(tokens, []);
192
+ return `:root{
193
+ ${lines.join("\n")}
194
+ }
195
+ `;
196
+ };
197
+ var semanticTokensToCss = (map, wrapper) => {
198
+ const lines = Object.entries(map).sort(([a], [b]) => a.localeCompare(b)).map(([key, ref]) => ` --lt-${key}: var(--lt-${ref});`);
199
+ if (!wrapper) {
200
+ return `/* Semantic tokens */
201
+ :root {
202
+ ${lines.join("\n")}
203
+ }
204
+ `;
205
+ }
206
+ if (wrapper.startsWith("@")) {
207
+ const indented = lines.map((l) => " " + l);
208
+ return `${wrapper} {
209
+ :root {
210
+ ${indented.join("\n")}
211
+ }
212
+ }
213
+ `;
214
+ }
215
+ return `${wrapper} {
216
+ ${lines.join("\n")}
217
+ }
218
+ `;
219
+ };
220
+
221
+ // src/semantic/semantic-tokens.ts
222
+ var VARIANTS = ["primary", "secondary", "success", "warning", "error", "info"];
223
+ var relativeLuminance = (hex) => {
224
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
225
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
226
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
227
+ const lin = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
228
+ return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
229
+ };
230
+ var accessibleTextOn = (bgHex) => {
231
+ const L = relativeLuminance(bgHex);
232
+ const whiteContrast = 1.05 / (L + 0.05);
233
+ const darkContrast = (L + 0.05) / 0.059;
234
+ return whiteContrast >= darkContrast ? "color-white" : "color-neutral-900";
235
+ };
236
+ var buildLightSemanticTokens = (opts = {}) => {
237
+ const map = {};
238
+ map["text-default"] = "color-neutral-900";
239
+ map["text-subtle"] = "color-neutral-600";
240
+ map["text-muted"] = "color-neutral-400";
241
+ map["text-disabled"] = "color-neutral-400";
242
+ map["text-inverse"] = "color-white";
243
+ map["text-neutral"] = "color-neutral-700";
244
+ for (const v of VARIANTS) {
245
+ map[`text-${v}`] = `color-${v}-700`;
246
+ map[`text-on-${v}`] = "color-white";
247
+ map[`text-on-warning`] = "color-warning-800";
248
+ }
249
+ if (opts.primary500) map["text-on-primary"] = accessibleTextOn(opts.primary500);
250
+ map["bg-default"] = "color-white";
251
+ map["bg-subtle"] = "color-neutral-50";
252
+ map["bg-surface"] = "color-neutral-100";
253
+ map["bg-overlay"] = "color-neutral-200";
254
+ map["bg-inverse"] = "color-neutral-900";
255
+ map["bg-neutral-subtle"] = "color-neutral-100";
256
+ for (const v of VARIANTS) {
257
+ map[`bg-${v}`] = `color-${v}-500`;
258
+ map[`bg-${v}-subtle`] = `color-${v}-100`;
259
+ }
260
+ map["border-default"] = "color-neutral-200";
261
+ map["border-strong"] = "color-neutral-400";
262
+ map["border-subtle"] = "color-neutral-100";
263
+ map["border-focus"] = "color-primary-200";
264
+ for (const v of VARIANTS) {
265
+ map[`border-${v}`] = `color-${v}-200`;
266
+ map[`border-${v}-strong`] = `color-${v}-500`;
267
+ }
268
+ for (const v of VARIANTS) {
269
+ map[`interactive-${v}-bg`] = `color-${v}-500`;
270
+ map[`interactive-${v}-bg-hover`] = `color-${v}-600`;
271
+ map[`interactive-${v}-bg-active`] = `color-${v}-700`;
272
+ }
273
+ return map;
274
+ };
275
+ var buildDarkSemanticTokens = (opts = {}) => {
276
+ const map = {};
277
+ map["text-default"] = "color-neutral-100";
278
+ map["text-subtle"] = "color-neutral-300";
279
+ map["text-muted"] = "color-neutral-500";
280
+ map["text-disabled"] = "color-neutral-600";
281
+ map["text-inverse"] = "color-neutral-900";
282
+ map["text-neutral"] = "color-neutral-200";
283
+ for (const v of VARIANTS) {
284
+ map[`text-${v}`] = `color-${v}-300`;
285
+ map[`text-on-${v}`] = "color-white";
286
+ }
287
+ if (opts.primary400) map["text-on-primary"] = accessibleTextOn(opts.primary400);
288
+ map["bg-default"] = "color-neutral-900";
289
+ map["bg-subtle"] = "color-neutral-800";
290
+ map["bg-surface"] = "color-neutral-800";
291
+ map["bg-overlay"] = "color-neutral-700";
292
+ map["bg-inverse"] = "color-neutral-50";
293
+ map["bg-neutral-subtle"] = "color-neutral-800";
294
+ for (const v of VARIANTS) {
295
+ map[`bg-${v}`] = `color-${v}-500`;
296
+ map[`bg-${v}-subtle`] = `color-${v}-900`;
297
+ }
298
+ map["border-default"] = "color-neutral-700";
299
+ map["border-strong"] = "color-neutral-500";
300
+ map["border-subtle"] = "color-neutral-800";
301
+ map["border-focus"] = "color-primary-400";
302
+ for (const v of VARIANTS) {
303
+ map[`border-${v}`] = `color-${v}-700`;
304
+ map[`border-${v}-strong`] = `color-${v}-500`;
305
+ }
306
+ for (const v of VARIANTS) {
307
+ map[`interactive-${v}-bg`] = `color-${v}-400`;
308
+ map[`interactive-${v}-bg-hover`] = `color-${v}-300`;
309
+ map[`interactive-${v}-bg-active`] = `color-${v}-500`;
310
+ }
311
+ return map;
312
+ };
313
+ var buildSemanticTokens = (mode = "light", opts = {}) => mode === "dark" ? buildDarkSemanticTokens(opts) : buildLightSemanticTokens(opts);
314
+
315
+ // tokens.config.json
316
+ var tokens_config_default = {
317
+ color: {
318
+ primary: "#FF8200",
319
+ secondary: "#5252c5",
320
+ success: "#22c55e",
321
+ warning: "#eeb308",
322
+ error: "#ef4444",
323
+ info: "#0ea5e9"
324
+ },
325
+ border: {
326
+ radius: "2px"
327
+ }
328
+ };
329
+
330
+ // src/configure/configure.ts
331
+ var DEFAULTS = {
332
+ colors: tokens_config_default.color,
333
+ font: { family: DEFAULT_FONT_FAMILY },
334
+ border: { radius: DEFAULT_BORDER_RADIUS }
335
+ };
336
+ var toInternalConfig = (userConfig) => ({
337
+ color: { ...DEFAULTS.colors, ...userConfig.colors }
338
+ });
339
+ var createStyleSheet = (userConfig = {}) => {
340
+ const cfg = toInternalConfig(userConfig);
341
+ let tokens = buildTokens(cfg);
342
+ if (userConfig.font?.family) {
343
+ tokens = { ...tokens, typography: { fontFamily: userConfig.font.family } };
344
+ }
345
+ if (userConfig.border?.radius) {
346
+ tokens = { ...tokens, border: { ...tokens.border, radius: userConfig.border.radius } };
347
+ }
348
+ const primitives = tokensToCss(tokens);
349
+ const theme = userConfig.theme ?? "light";
350
+ const primary = tokens.color.primary;
351
+ const semanticOpts = { primary500: primary["500"], primary400: primary["400"] };
352
+ if (theme === "dark") return primitives + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts));
353
+ if (theme === "light") return primitives + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts));
354
+ return primitives + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts)) + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts), "@media (prefers-color-scheme: dark)") + "\n" + semanticTokensToCss(buildSemanticTokens("dark", semanticOpts), '[data-theme="dark"]') + "\n" + semanticTokensToCss(buildSemanticTokens("light", semanticOpts), '[data-theme="light"]');
355
+ };
356
+ var configure = (userConfig = {}) => {
357
+ const root = document.documentElement;
358
+ root.removeAttribute("data-lt-ready");
359
+ let style = document.getElementById("lt-tokens");
360
+ if (!style) {
361
+ style = document.createElement("style");
362
+ style.id = "lt-tokens";
363
+ document.head.prepend(style);
364
+ }
365
+ style.textContent = "html[data-lt]:not([data-lt-ready]){visibility:hidden}\n" + createStyleSheet(userConfig);
366
+ requestAnimationFrame(() => root.setAttribute("data-lt-ready", ""));
367
+ };
368
+ export {
369
+ configure,
370
+ createStyleSheet
371
+ };