@stapel/tokens 0.1.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 Stapel 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,41 @@
1
+ # @stapel/tokens
2
+
3
+ Semantic design tokens for the Stapel frontend pipeline (L0, frontend-standard
4
+ §1). TypeScript is the single source of truth; `tokens.css` (CSS custom
5
+ properties with `[data-theme="dark"]` overrides) is generated from it at build
6
+ time. **No components live here.**
7
+
8
+ ## What's inside
9
+
10
+ - `colors` — semantic colors; **every token is a light/dark pair**
11
+ - `typography` — font families, size/line-height scale, weights
12
+ - `spacing` — spacing scale (px)
13
+ - `radii` — border radii
14
+ - `elevation` — box-shadow levels (light/dark pairs)
15
+ - `breakpoints` — exactly three: `phone` / `tablet` / `desktop` (min-width px)
16
+ - `generateTokensCss()` — deterministic (byte-stable) CSS generator
17
+ - `cssVar(name)` — helper returning `var(--stapel-…)` references
18
+
19
+ ## Usage
20
+
21
+ ```ts
22
+ import { colors, breakpoints, cssVar } from "@stapel/tokens";
23
+ import "@stapel/tokens/tokens.css";
24
+
25
+ colors.primary.light; // "#4657d9"
26
+ breakpoints.tablet; // 768
27
+ cssVar("color-primary"); // "var(--stapel-color-primary)"
28
+ ```
29
+
30
+ Dark theme: set `data-theme="dark"` on `<html>` (or any subtree root).
31
+
32
+ ## Rules it exists to enforce
33
+
34
+ Raw hex/px values are banned outside this package (frontend-standard §4.1) —
35
+ consume `var(--stapel-*)` or the typed TS objects only.
36
+
37
+ ## Notes
38
+
39
+ - Standalone-buildable; the npm tarball ships `src/` (frontend-standard §7).
40
+ - TODO: React Compiler precompile at publish is not applicable here (no
41
+ components/hooks), tracked at the monorepo level for consistency.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Exactly three breakpoints (frontend-standard §4.3): phone / tablet /
3
+ * desktop. Values are min-width px: a viewport is `tablet` when
4
+ * `width >= breakpoints.tablet` and `< breakpoints.desktop`.
5
+ * `@stapel/core`'s `useBreakpoint()` reads these.
6
+ */
7
+ export declare const breakpoints: {
8
+ readonly phone: 0;
9
+ readonly tablet: 768;
10
+ readonly desktop: 1200;
11
+ };
12
+ export type Breakpoint = keyof typeof breakpoints;
13
+ export declare const breakpointOrder: readonly ["phone", "tablet", "desktop"];
14
+ /** Resolve which breakpoint a viewport width falls into. */
15
+ export declare function breakpointForWidth(width: number): Breakpoint;
16
+ /** CSS min-width media query for a breakpoint, e.g. `(min-width: 768px)`. */
17
+ export declare function mediaQuery(breakpoint: Breakpoint): string;
18
+ //# sourceMappingURL=breakpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,WAAW,CAAC;AAElD,eAAO,MAAM,eAAe,yCAA0C,CAAC;AAEvE,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAI5D;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Exactly three breakpoints (frontend-standard §4.3): phone / tablet /
3
+ * desktop. Values are min-width px: a viewport is `tablet` when
4
+ * `width >= breakpoints.tablet` and `< breakpoints.desktop`.
5
+ * `@stapel/core`'s `useBreakpoint()` reads these.
6
+ */
7
+ export const breakpoints = {
8
+ phone: 0,
9
+ tablet: 768,
10
+ desktop: 1200,
11
+ };
12
+ export const breakpointOrder = ["phone", "tablet", "desktop"];
13
+ /** Resolve which breakpoint a viewport width falls into. */
14
+ export function breakpointForWidth(width) {
15
+ if (width >= breakpoints.desktop)
16
+ return "desktop";
17
+ if (width >= breakpoints.tablet)
18
+ return "tablet";
19
+ return "phone";
20
+ }
21
+ /** CSS min-width media query for a breakpoint, e.g. `(min-width: 768px)`. */
22
+ export function mediaQuery(breakpoint) {
23
+ return `(min-width: ${String(breakpoints[breakpoint])}px)`;
24
+ }
25
+ //# sourceMappingURL=breakpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,IAAI;CACL,CAAC;AAIX,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;AAEvE,4DAA4D;AAC5D,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,KAAK,IAAI,WAAW,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACnD,IAAI,KAAK,IAAI,WAAW,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IACjD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAC,UAAsB;IAC/C,OAAO,eAAe,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Semantic color tokens. Every token carries a light/dark pair
3
+ * (frontend-standard §4.1) — the CSS build emits the light value on `:root`
4
+ * and the dark value under `[data-theme="dark"]`.
5
+ */
6
+ export interface ColorToken {
7
+ readonly light: string;
8
+ readonly dark: string;
9
+ }
10
+ export declare const colors: {
11
+ /** Page background. */
12
+ readonly background: {
13
+ readonly light: "#ffffff";
14
+ readonly dark: "#0b0e14";
15
+ };
16
+ /** Default surface (cards, panels). */
17
+ readonly surface: {
18
+ readonly light: "#f6f7f9";
19
+ readonly dark: "#151a23";
20
+ };
21
+ /** Raised surface (popovers, menus). */
22
+ readonly surfaceRaised: {
23
+ readonly light: "#ffffff";
24
+ readonly dark: "#1c2230";
25
+ };
26
+ /** Primary text. */
27
+ readonly text: {
28
+ readonly light: "#171b21";
29
+ readonly dark: "#e8eaf0";
30
+ };
31
+ /** Secondary / muted text. */
32
+ readonly textMuted: {
33
+ readonly light: "#5c6470";
34
+ readonly dark: "#9aa3b2";
35
+ };
36
+ /** Text on primary-colored surfaces. */
37
+ readonly textOnPrimary: {
38
+ readonly light: "#ffffff";
39
+ readonly dark: "#ffffff";
40
+ };
41
+ /** Brand / primary interactive color. */
42
+ readonly primary: {
43
+ readonly light: "#4657d9";
44
+ readonly dark: "#7c8cf8";
45
+ };
46
+ /** Primary hover state. */
47
+ readonly primaryHover: {
48
+ readonly light: "#3948b8";
49
+ readonly dark: "#98a5fa";
50
+ };
51
+ /** Subtle primary background (selected rows, badges). */
52
+ readonly primarySubtle: {
53
+ readonly light: "#eef0fd";
54
+ readonly dark: "#232b4d";
55
+ };
56
+ /** Default border. */
57
+ readonly border: {
58
+ readonly light: "#d9dde3";
59
+ readonly dark: "#2a3242";
60
+ };
61
+ /** Emphasised border (inputs on focus-within, table headers). */
62
+ readonly borderStrong: {
63
+ readonly light: "#aeb6c2";
64
+ readonly dark: "#3d4759";
65
+ };
66
+ /** Destructive actions and errors. */
67
+ readonly danger: {
68
+ readonly light: "#c93a3a";
69
+ readonly dark: "#f28b8b";
70
+ };
71
+ /** Subtle danger background. */
72
+ readonly dangerSubtle: {
73
+ readonly light: "#fdeeee";
74
+ readonly dark: "#3a2020";
75
+ };
76
+ /** Success states. */
77
+ readonly success: {
78
+ readonly light: "#1f7a4d";
79
+ readonly dark: "#6fd0a0";
80
+ };
81
+ /** Subtle success background. */
82
+ readonly successSubtle: {
83
+ readonly light: "#eaf7f0";
84
+ readonly dark: "#152e22";
85
+ };
86
+ /** Warnings. */
87
+ readonly warning: {
88
+ readonly light: "#9a6700";
89
+ readonly dark: "#e8b34b";
90
+ };
91
+ /** Subtle warning background. */
92
+ readonly warningSubtle: {
93
+ readonly light: "#fdf6e7";
94
+ readonly dark: "#33270f";
95
+ };
96
+ /** Informational accents. */
97
+ readonly info: {
98
+ readonly light: "#1168a7";
99
+ readonly dark: "#6cb8e8";
100
+ };
101
+ /** Subtle info background. */
102
+ readonly infoSubtle: {
103
+ readonly light: "#e9f4fb";
104
+ readonly dark: "#12293a";
105
+ };
106
+ /** Focus ring. */
107
+ readonly focusRing: {
108
+ readonly light: "#4657d9";
109
+ readonly dark: "#98a5fa";
110
+ };
111
+ /** Overlay scrim behind modals. */
112
+ readonly overlay: {
113
+ readonly light: "rgba(15, 18, 24, 0.45)";
114
+ readonly dark: "rgba(0, 0, 0, 0.6)";
115
+ };
116
+ };
117
+ export type ColorTokenName = keyof typeof colors;
118
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,MAAM;IACjB,uBAAuB;;;;;IAEvB,uCAAuC;;;;;IAEvC,wCAAwC;;;;;IAExC,oBAAoB;;;;;IAEpB,8BAA8B;;;;;IAE9B,wCAAwC;;;;;IAExC,yCAAyC;;;;;IAEzC,2BAA2B;;;;;IAE3B,yDAAyD;;;;;IAEzD,sBAAsB;;;;;IAEtB,iEAAiE;;;;;IAEjE,sCAAsC;;;;;IAEtC,gCAAgC;;;;;IAEhC,sBAAsB;;;;;IAEtB,iCAAiC;;;;;IAEjC,gBAAgB;;;;;IAEhB,iCAAiC;;;;;IAEjC,6BAA6B;;;;;IAE7B,8BAA8B;;;;;IAE9B,kBAAkB;;;;;IAElB,mCAAmC;;;;;CAE3B,CAAC;AAMX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,MAAM,CAAC"}
package/dist/colors.js ADDED
@@ -0,0 +1,48 @@
1
+ export const colors = {
2
+ /** Page background. */
3
+ background: { light: "#ffffff", dark: "#0b0e14" },
4
+ /** Default surface (cards, panels). */
5
+ surface: { light: "#f6f7f9", dark: "#151a23" },
6
+ /** Raised surface (popovers, menus). */
7
+ surfaceRaised: { light: "#ffffff", dark: "#1c2230" },
8
+ /** Primary text. */
9
+ text: { light: "#171b21", dark: "#e8eaf0" },
10
+ /** Secondary / muted text. */
11
+ textMuted: { light: "#5c6470", dark: "#9aa3b2" },
12
+ /** Text on primary-colored surfaces. */
13
+ textOnPrimary: { light: "#ffffff", dark: "#ffffff" },
14
+ /** Brand / primary interactive color. */
15
+ primary: { light: "#4657d9", dark: "#7c8cf8" },
16
+ /** Primary hover state. */
17
+ primaryHover: { light: "#3948b8", dark: "#98a5fa" },
18
+ /** Subtle primary background (selected rows, badges). */
19
+ primarySubtle: { light: "#eef0fd", dark: "#232b4d" },
20
+ /** Default border. */
21
+ border: { light: "#d9dde3", dark: "#2a3242" },
22
+ /** Emphasised border (inputs on focus-within, table headers). */
23
+ borderStrong: { light: "#aeb6c2", dark: "#3d4759" },
24
+ /** Destructive actions and errors. */
25
+ danger: { light: "#c93a3a", dark: "#f28b8b" },
26
+ /** Subtle danger background. */
27
+ dangerSubtle: { light: "#fdeeee", dark: "#3a2020" },
28
+ /** Success states. */
29
+ success: { light: "#1f7a4d", dark: "#6fd0a0" },
30
+ /** Subtle success background. */
31
+ successSubtle: { light: "#eaf7f0", dark: "#152e22" },
32
+ /** Warnings. */
33
+ warning: { light: "#9a6700", dark: "#e8b34b" },
34
+ /** Subtle warning background. */
35
+ warningSubtle: { light: "#fdf6e7", dark: "#33270f" },
36
+ /** Informational accents. */
37
+ info: { light: "#1168a7", dark: "#6cb8e8" },
38
+ /** Subtle info background. */
39
+ infoSubtle: { light: "#e9f4fb", dark: "#12293a" },
40
+ /** Focus ring. */
41
+ focusRing: { light: "#4657d9", dark: "#98a5fa" },
42
+ /** Overlay scrim behind modals. */
43
+ overlay: { light: "rgba(15, 18, 24, 0.45)", dark: "rgba(0, 0, 0, 0.6)" },
44
+ };
45
+ // Constraint check (kept out of the public type surface for isolatedDeclarations).
46
+ const _colorsCheck = colors;
47
+ void _colorsCheck;
48
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../src/colors.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,uBAAuB;IACvB,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACjD,uCAAuC;IACvC,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC9C,wCAAwC;IACxC,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpD,oBAAoB;IACpB,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC3C,8BAA8B;IAC9B,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAChD,wCAAwC;IACxC,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpD,yCAAyC;IACzC,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC9C,2BAA2B;IAC3B,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACnD,yDAAyD;IACzD,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpD,sBAAsB;IACtB,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC7C,iEAAiE;IACjE,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACnD,sCAAsC;IACtC,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC7C,gCAAgC;IAChC,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACnD,sBAAsB;IACtB,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC9C,iCAAiC;IACjC,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpD,gBAAgB;IAChB,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC9C,iCAAiC;IACjC,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACpD,6BAA6B;IAC7B,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAC3C,8BAA8B;IAC9B,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IACjD,kBAAkB;IAClB,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE;IAChD,mCAAmC;IACnC,OAAO,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,IAAI,EAAE,oBAAoB,EAAE;CAChE,CAAC;AAEX,mFAAmF;AACnF,MAAM,YAAY,GAA+B,MAAM,CAAC;AACxD,KAAK,YAAY,CAAC"}
package/dist/css.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /** `cssVar("color-primary")` → `var(--stapel-color-primary)` */
2
+ export declare function cssVar(name: string): string;
3
+ /** Render the full tokens stylesheet (`:root` + `[data-theme="dark"]`). */
4
+ export declare function generateTokensCss(): string;
5
+ //# sourceMappingURL=css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AA2BA,gEAAgE;AAChE,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,IAAI,MAAM,CA8C1C"}
package/dist/css.js ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Deterministic tokens.css generator. TS tokens are the single source of
3
+ * truth; this module renders them to CSS custom properties. The output is
4
+ * byte-stable: keys are sorted, numbers stringified canonically, no
5
+ * timestamps — same input, same bytes (unit-tested).
6
+ */
7
+ import { colors } from "./colors.js";
8
+ import { elevation } from "./elevation.js";
9
+ import { breakpoints } from "./breakpoints.js";
10
+ import { radii } from "./radii.js";
11
+ import { spacing } from "./spacing.js";
12
+ import { fontFamily, fontSize, fontWeight } from "./typography.js";
13
+ const PREFIX = "--stapel";
14
+ function kebab(name) {
15
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
16
+ }
17
+ function sortedEntries(record) {
18
+ return Object.entries(record).sort(([a], [b]) => (a < b ? -1 : 1));
19
+ }
20
+ function px(value) {
21
+ return value === 0 ? "0" : `${String(value)}px`;
22
+ }
23
+ /** `cssVar("color-primary")` → `var(--stapel-color-primary)` */
24
+ export function cssVar(name) {
25
+ return `var(${PREFIX}-${name})`;
26
+ }
27
+ /** Render the full tokens stylesheet (`:root` + `[data-theme="dark"]`). */
28
+ export function generateTokensCss() {
29
+ const root = [];
30
+ const dark = [];
31
+ for (const [name, pair] of sortedEntries(colors)) {
32
+ root.push(`${PREFIX}-color-${kebab(name)}: ${pair.light};`);
33
+ dark.push(`${PREFIX}-color-${kebab(name)}: ${pair.dark};`);
34
+ }
35
+ for (const [name, pair] of sortedEntries(elevation)) {
36
+ root.push(`${PREFIX}-elevation-${kebab(name)}: ${pair.light};`);
37
+ dark.push(`${PREFIX}-elevation-${kebab(name)}: ${pair.dark};`);
38
+ }
39
+ for (const [name, value] of sortedEntries(fontFamily)) {
40
+ root.push(`${PREFIX}-font-family-${kebab(name)}: ${value};`);
41
+ }
42
+ for (const [name, step] of sortedEntries(fontSize)) {
43
+ root.push(`${PREFIX}-font-size-${kebab(name)}: ${px(step.fontSize)};`);
44
+ root.push(`${PREFIX}-line-height-${kebab(name)}: ${px(step.lineHeight)};`);
45
+ }
46
+ for (const [name, value] of sortedEntries(fontWeight)) {
47
+ root.push(`${PREFIX}-font-weight-${kebab(name)}: ${String(value)};`);
48
+ }
49
+ for (const [name, value] of sortedEntries(spacing)) {
50
+ root.push(`${PREFIX}-space-${kebab(name)}: ${px(value)};`);
51
+ }
52
+ for (const [name, value] of sortedEntries(radii)) {
53
+ root.push(`${PREFIX}-radius-${kebab(name)}: ${px(value)};`);
54
+ }
55
+ for (const [name, value] of sortedEntries(breakpoints)) {
56
+ root.push(`${PREFIX}-breakpoint-${kebab(name)}: ${px(value)};`);
57
+ }
58
+ const indent = (lines) => lines.map((l) => ` ${l}`).join("\n");
59
+ return [
60
+ "/* Generated by @stapel/tokens — do not edit. Source: src/*.ts */",
61
+ ":root {",
62
+ indent(root),
63
+ "}",
64
+ "",
65
+ '[data-theme="dark"] {',
66
+ indent(dark),
67
+ "}",
68
+ "",
69
+ ].join("\n");
70
+ }
71
+ //# sourceMappingURL=css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Elevation (box-shadow) levels. Light/dark pairs like colors: dark themes
3
+ * need stronger shadows to read as raised.
4
+ */
5
+ export interface ElevationToken {
6
+ readonly light: string;
7
+ readonly dark: string;
8
+ }
9
+ export declare const elevation: {
10
+ readonly none: {
11
+ readonly light: "none";
12
+ readonly dark: "none";
13
+ };
14
+ readonly low: {
15
+ readonly light: "0 1px 2px rgba(15, 18, 24, 0.08)";
16
+ readonly dark: "0 1px 2px rgba(0, 0, 0, 0.4)";
17
+ };
18
+ readonly medium: {
19
+ readonly light: "0 2px 8px rgba(15, 18, 24, 0.12)";
20
+ readonly dark: "0 2px 8px rgba(0, 0, 0, 0.5)";
21
+ };
22
+ readonly high: {
23
+ readonly light: "0 8px 24px rgba(15, 18, 24, 0.16)";
24
+ readonly dark: "0 8px 24px rgba(0, 0, 0, 0.6)";
25
+ };
26
+ };
27
+ export type ElevationName = keyof typeof elevation;
28
+ //# sourceMappingURL=elevation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elevation.d.ts","sourceRoot":"","sources":["../src/elevation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;CAcZ,CAAC;AAKX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,SAAS,CAAC"}
@@ -0,0 +1,18 @@
1
+ export const elevation = {
2
+ none: { light: "none", dark: "none" },
3
+ low: {
4
+ light: "0 1px 2px rgba(15, 18, 24, 0.08)",
5
+ dark: "0 1px 2px rgba(0, 0, 0, 0.4)",
6
+ },
7
+ medium: {
8
+ light: "0 2px 8px rgba(15, 18, 24, 0.12)",
9
+ dark: "0 2px 8px rgba(0, 0, 0, 0.5)",
10
+ },
11
+ high: {
12
+ light: "0 8px 24px rgba(15, 18, 24, 0.16)",
13
+ dark: "0 8px 24px rgba(0, 0, 0, 0.6)",
14
+ },
15
+ };
16
+ const _elevationCheck = elevation;
17
+ void _elevationCheck;
18
+ //# sourceMappingURL=elevation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elevation.js","sourceRoot":"","sources":["../src/elevation.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;IACrC,GAAG,EAAE;QACH,KAAK,EAAE,kCAAkC;QACzC,IAAI,EAAE,8BAA8B;KACrC;IACD,MAAM,EAAE;QACN,KAAK,EAAE,kCAAkC;QACzC,IAAI,EAAE,8BAA8B;KACrC;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,mCAAmC;QAC1C,IAAI,EAAE,+BAA+B;KACtC;CACO,CAAC;AAEX,MAAM,eAAe,GAAmC,SAAS,CAAC;AAClE,KAAK,eAAe,CAAC"}
@@ -0,0 +1,14 @@
1
+ export { colors } from "./colors.js";
2
+ export type { ColorToken, ColorTokenName } from "./colors.js";
3
+ export { typography, fontFamily, fontSize, fontWeight, } from "./typography.js";
4
+ export type { TypeStep, FontSizeName, FontWeightName } from "./typography.js";
5
+ export { spacing } from "./spacing.js";
6
+ export type { SpacingStep } from "./spacing.js";
7
+ export { radii } from "./radii.js";
8
+ export type { RadiusName } from "./radii.js";
9
+ export { elevation } from "./elevation.js";
10
+ export type { ElevationName, ElevationToken } from "./elevation.js";
11
+ export { breakpoints, breakpointOrder, breakpointForWidth, mediaQuery, } from "./breakpoints.js";
12
+ export type { Breakpoint } from "./breakpoints.js";
13
+ export { generateTokensCss, cssVar } from "./css.js";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,GACX,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EACL,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,UAAU,GACX,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { colors } from "./colors.js";
2
+ export { typography, fontFamily, fontSize, fontWeight, } from "./typography.js";
3
+ export { spacing } from "./spacing.js";
4
+ export { radii } from "./radii.js";
5
+ export { elevation } from "./elevation.js";
6
+ export { breakpoints, breakpointOrder, breakpointForWidth, mediaQuery, } from "./breakpoints.js";
7
+ export { generateTokensCss, cssVar } from "./css.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EACL,UAAU,EACV,UAAU,EACV,QAAQ,EACR,UAAU,GACX,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EACL,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,UAAU,GACX,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Border radii in px (`full` is a pill/circle sentinel). */
2
+ export declare const radii: {
3
+ readonly none: 0;
4
+ readonly sm: 4;
5
+ readonly md: 8;
6
+ readonly lg: 12;
7
+ readonly xl: 20;
8
+ readonly full: 9999;
9
+ };
10
+ export type RadiusName = keyof typeof radii;
11
+ //# sourceMappingURL=radii.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radii.d.ts","sourceRoot":"","sources":["../src/radii.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,eAAO,MAAM,KAAK;;;;;;;CAOR,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,KAAK,CAAC"}
package/dist/radii.js ADDED
@@ -0,0 +1,10 @@
1
+ /** Border radii in px (`full` is a pill/circle sentinel). */
2
+ export const radii = {
3
+ none: 0,
4
+ sm: 4,
5
+ md: 8,
6
+ lg: 12,
7
+ xl: 20,
8
+ full: 9999,
9
+ };
10
+ //# sourceMappingURL=radii.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radii.js","sourceRoot":"","sources":["../src/radii.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,IAAI,EAAE,CAAC;IACP,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,IAAI,EAAE,IAAI;CACF,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** Spacing scale in px. Consume via TS or `--stapel-space-*` CSS vars. */
2
+ export declare const spacing: {
3
+ readonly "0": 0;
4
+ readonly "1": 4;
5
+ readonly "2": 8;
6
+ readonly "3": 12;
7
+ readonly "4": 16;
8
+ readonly "5": 24;
9
+ readonly "6": 32;
10
+ readonly "7": 48;
11
+ readonly "8": 64;
12
+ };
13
+ export type SpacingStep = keyof typeof spacing;
14
+ //# sourceMappingURL=spacing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spacing.d.ts","sourceRoot":"","sources":["../src/spacing.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,eAAO,MAAM,OAAO;;;;;;;;;;CAUV,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,OAAO,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** Spacing scale in px. Consume via TS or `--stapel-space-*` CSS vars. */
2
+ export const spacing = {
3
+ "0": 0,
4
+ "1": 4,
5
+ "2": 8,
6
+ "3": 12,
7
+ "4": 16,
8
+ "5": 24,
9
+ "6": 32,
10
+ "7": 48,
11
+ "8": 64,
12
+ };
13
+ //# sourceMappingURL=spacing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spacing.js","sourceRoot":"","sources":["../src/spacing.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,CAAC;IACN,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;CACC,CAAC"}
@@ -0,0 +1,94 @@
1
+ /* Generated by @stapel/tokens — do not edit. Source: src/*.ts */
2
+ :root {
3
+ --stapel-color-background: #ffffff;
4
+ --stapel-color-border: #d9dde3;
5
+ --stapel-color-border-strong: #aeb6c2;
6
+ --stapel-color-danger: #c93a3a;
7
+ --stapel-color-danger-subtle: #fdeeee;
8
+ --stapel-color-focus-ring: #4657d9;
9
+ --stapel-color-info: #1168a7;
10
+ --stapel-color-info-subtle: #e9f4fb;
11
+ --stapel-color-overlay: rgba(15, 18, 24, 0.45);
12
+ --stapel-color-primary: #4657d9;
13
+ --stapel-color-primary-hover: #3948b8;
14
+ --stapel-color-primary-subtle: #eef0fd;
15
+ --stapel-color-success: #1f7a4d;
16
+ --stapel-color-success-subtle: #eaf7f0;
17
+ --stapel-color-surface: #f6f7f9;
18
+ --stapel-color-surface-raised: #ffffff;
19
+ --stapel-color-text: #171b21;
20
+ --stapel-color-text-muted: #5c6470;
21
+ --stapel-color-text-on-primary: #ffffff;
22
+ --stapel-color-warning: #9a6700;
23
+ --stapel-color-warning-subtle: #fdf6e7;
24
+ --stapel-elevation-high: 0 8px 24px rgba(15, 18, 24, 0.16);
25
+ --stapel-elevation-low: 0 1px 2px rgba(15, 18, 24, 0.08);
26
+ --stapel-elevation-medium: 0 2px 8px rgba(15, 18, 24, 0.12);
27
+ --stapel-elevation-none: none;
28
+ --stapel-font-family-mono: 'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace;
29
+ --stapel-font-family-sans: 'Inter', 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif;
30
+ --stapel-font-size-2xl: 28px;
31
+ --stapel-line-height-2xl: 36px;
32
+ --stapel-font-size-3xl: 36px;
33
+ --stapel-line-height-3xl: 44px;
34
+ --stapel-font-size-lg: 18px;
35
+ --stapel-line-height-lg: 28px;
36
+ --stapel-font-size-md: 16px;
37
+ --stapel-line-height-md: 24px;
38
+ --stapel-font-size-sm: 14px;
39
+ --stapel-line-height-sm: 20px;
40
+ --stapel-font-size-xl: 22px;
41
+ --stapel-line-height-xl: 30px;
42
+ --stapel-font-size-xs: 12px;
43
+ --stapel-line-height-xs: 16px;
44
+ --stapel-font-weight-bold: 700;
45
+ --stapel-font-weight-medium: 500;
46
+ --stapel-font-weight-regular: 400;
47
+ --stapel-font-weight-semibold: 600;
48
+ --stapel-space-0: 0;
49
+ --stapel-space-1: 4px;
50
+ --stapel-space-2: 8px;
51
+ --stapel-space-3: 12px;
52
+ --stapel-space-4: 16px;
53
+ --stapel-space-5: 24px;
54
+ --stapel-space-6: 32px;
55
+ --stapel-space-7: 48px;
56
+ --stapel-space-8: 64px;
57
+ --stapel-radius-full: 9999px;
58
+ --stapel-radius-lg: 12px;
59
+ --stapel-radius-md: 8px;
60
+ --stapel-radius-none: 0;
61
+ --stapel-radius-sm: 4px;
62
+ --stapel-radius-xl: 20px;
63
+ --stapel-breakpoint-desktop: 1200px;
64
+ --stapel-breakpoint-phone: 0;
65
+ --stapel-breakpoint-tablet: 768px;
66
+ }
67
+
68
+ [data-theme="dark"] {
69
+ --stapel-color-background: #0b0e14;
70
+ --stapel-color-border: #2a3242;
71
+ --stapel-color-border-strong: #3d4759;
72
+ --stapel-color-danger: #f28b8b;
73
+ --stapel-color-danger-subtle: #3a2020;
74
+ --stapel-color-focus-ring: #98a5fa;
75
+ --stapel-color-info: #6cb8e8;
76
+ --stapel-color-info-subtle: #12293a;
77
+ --stapel-color-overlay: rgba(0, 0, 0, 0.6);
78
+ --stapel-color-primary: #7c8cf8;
79
+ --stapel-color-primary-hover: #98a5fa;
80
+ --stapel-color-primary-subtle: #232b4d;
81
+ --stapel-color-success: #6fd0a0;
82
+ --stapel-color-success-subtle: #152e22;
83
+ --stapel-color-surface: #151a23;
84
+ --stapel-color-surface-raised: #1c2230;
85
+ --stapel-color-text: #e8eaf0;
86
+ --stapel-color-text-muted: #9aa3b2;
87
+ --stapel-color-text-on-primary: #ffffff;
88
+ --stapel-color-warning: #e8b34b;
89
+ --stapel-color-warning-subtle: #33270f;
90
+ --stapel-elevation-high: 0 8px 24px rgba(0, 0, 0, 0.6);
91
+ --stapel-elevation-low: 0 1px 2px rgba(0, 0, 0, 0.4);
92
+ --stapel-elevation-medium: 0 2px 8px rgba(0, 0, 0, 0.5);
93
+ --stapel-elevation-none: none;
94
+ }
@@ -0,0 +1,54 @@
1
+ /** Typography scale. Sizes/line-heights in px, emitted as rem-friendly px CSS. */
2
+ export interface TypeStep {
3
+ readonly fontSize: number;
4
+ readonly lineHeight: number;
5
+ }
6
+ export declare const fontFamily: {
7
+ readonly sans: "'Inter', 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif";
8
+ readonly mono: "'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace";
9
+ };
10
+ export declare const fontSize: {
11
+ readonly xs: {
12
+ readonly fontSize: 12;
13
+ readonly lineHeight: 16;
14
+ };
15
+ readonly sm: {
16
+ readonly fontSize: 14;
17
+ readonly lineHeight: 20;
18
+ };
19
+ readonly md: {
20
+ readonly fontSize: 16;
21
+ readonly lineHeight: 24;
22
+ };
23
+ readonly lg: {
24
+ readonly fontSize: 18;
25
+ readonly lineHeight: 28;
26
+ };
27
+ readonly xl: {
28
+ readonly fontSize: 22;
29
+ readonly lineHeight: 30;
30
+ };
31
+ readonly "2xl": {
32
+ readonly fontSize: 28;
33
+ readonly lineHeight: 36;
34
+ };
35
+ readonly "3xl": {
36
+ readonly fontSize: 36;
37
+ readonly lineHeight: 44;
38
+ };
39
+ };
40
+ export declare const fontWeight: {
41
+ readonly regular: 400;
42
+ readonly medium: 500;
43
+ readonly semibold: 600;
44
+ readonly bold: 700;
45
+ };
46
+ export interface Typography {
47
+ readonly fontFamily: typeof fontFamily;
48
+ readonly fontSize: typeof fontSize;
49
+ readonly fontWeight: typeof fontWeight;
50
+ }
51
+ export declare const typography: Typography;
52
+ export type FontSizeName = keyof typeof fontSize;
53
+ export type FontWeightName = keyof typeof fontWeight;
54
+ //# sourceMappingURL=typography.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typography.d.ts","sourceRoot":"","sources":["../src/typography.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,UAAU;;;CAGb,CAAC;AAEX,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQX,CAAC;AAEX,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,UAAU,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,OAAO,QAAQ,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,UAAU,CAAC;CACxC;AAED,eAAO,MAAM,UAAU,EAAE,UAIxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,QAAQ,CAAC;AACjD,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,UAAU,CAAC"}
@@ -0,0 +1,25 @@
1
+ export const fontFamily = {
2
+ sans: "'Inter', 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif",
3
+ mono: "'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace",
4
+ };
5
+ export const fontSize = {
6
+ xs: { fontSize: 12, lineHeight: 16 },
7
+ sm: { fontSize: 14, lineHeight: 20 },
8
+ md: { fontSize: 16, lineHeight: 24 },
9
+ lg: { fontSize: 18, lineHeight: 28 },
10
+ xl: { fontSize: 22, lineHeight: 30 },
11
+ "2xl": { fontSize: 28, lineHeight: 36 },
12
+ "3xl": { fontSize: 36, lineHeight: 44 },
13
+ };
14
+ export const fontWeight = {
15
+ regular: 400,
16
+ medium: 500,
17
+ semibold: 600,
18
+ bold: 700,
19
+ };
20
+ export const typography = {
21
+ fontFamily: fontFamily,
22
+ fontSize: fontSize,
23
+ fontWeight: fontWeight,
24
+ };
25
+ //# sourceMappingURL=typography.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typography.js","sourceRoot":"","sources":["../src/typography.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,oEAAoE;IAC1E,IAAI,EAAE,yDAAyD;CACvD,CAAC;AAEX,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACpC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACpC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACpC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACpC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACpC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACvC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;CAC/B,CAAC;AAEX,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,GAAG;IACX,QAAQ,EAAE,GAAG;IACb,IAAI,EAAE,GAAG;CACD,CAAC;AAQX,MAAM,CAAC,MAAM,UAAU,GAAe;IACpC,UAAU,EAAE,UAAU;IACtB,QAAQ,EAAE,QAAQ;IAClB,UAAU,EAAE,UAAU;CACvB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@stapel/tokens",
3
+ "version": "0.1.0",
4
+ "description": "Stapel design tokens: semantic colors (light/dark pairs), typography, spacing, radii, elevation, and the three breakpoints. TypeScript source of truth + generated CSS custom properties. Not components.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/usestapel/stapel-react.git",
9
+ "directory": "packages/tokens"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./tokens.css": "./dist/tokens.css",
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "src",
26
+ "tsconfig.json",
27
+ "README.md"
28
+ ],
29
+ "size-limit": [
30
+ {
31
+ "path": "dist/index.js",
32
+ "limit": "4 KB"
33
+ }
34
+ ],
35
+ "devDependencies": {
36
+ "@size-limit/preset-small-lib": "^11.2.0",
37
+ "size-limit": "^11.2.0",
38
+ "typescript": "^5.8.3",
39
+ "vitest": "^3.2.4"
40
+ },
41
+ "engines": {
42
+ "node": ">=22"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc -p tsconfig.json && node ./scripts/build-css.mjs",
49
+ "test": "vitest run",
50
+ "lint": "eslint .",
51
+ "size": "size-limit"
52
+ }
53
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Exactly three breakpoints (frontend-standard §4.3): phone / tablet /
3
+ * desktop. Values are min-width px: a viewport is `tablet` when
4
+ * `width >= breakpoints.tablet` and `< breakpoints.desktop`.
5
+ * `@stapel/core`'s `useBreakpoint()` reads these.
6
+ */
7
+ export const breakpoints = {
8
+ phone: 0,
9
+ tablet: 768,
10
+ desktop: 1200,
11
+ } as const;
12
+
13
+ export type Breakpoint = keyof typeof breakpoints;
14
+
15
+ export const breakpointOrder = ["phone", "tablet", "desktop"] as const;
16
+
17
+ /** Resolve which breakpoint a viewport width falls into. */
18
+ export function breakpointForWidth(width: number): Breakpoint {
19
+ if (width >= breakpoints.desktop) return "desktop";
20
+ if (width >= breakpoints.tablet) return "tablet";
21
+ return "phone";
22
+ }
23
+
24
+ /** CSS min-width media query for a breakpoint, e.g. `(min-width: 768px)`. */
25
+ export function mediaQuery(breakpoint: Breakpoint): string {
26
+ return `(min-width: ${String(breakpoints[breakpoint])}px)`;
27
+ }
package/src/colors.ts ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Semantic color tokens. Every token carries a light/dark pair
3
+ * (frontend-standard §4.1) — the CSS build emits the light value on `:root`
4
+ * and the dark value under `[data-theme="dark"]`.
5
+ */
6
+ export interface ColorToken {
7
+ readonly light: string;
8
+ readonly dark: string;
9
+ }
10
+
11
+ export const colors = {
12
+ /** Page background. */
13
+ background: { light: "#ffffff", dark: "#0b0e14" },
14
+ /** Default surface (cards, panels). */
15
+ surface: { light: "#f6f7f9", dark: "#151a23" },
16
+ /** Raised surface (popovers, menus). */
17
+ surfaceRaised: { light: "#ffffff", dark: "#1c2230" },
18
+ /** Primary text. */
19
+ text: { light: "#171b21", dark: "#e8eaf0" },
20
+ /** Secondary / muted text. */
21
+ textMuted: { light: "#5c6470", dark: "#9aa3b2" },
22
+ /** Text on primary-colored surfaces. */
23
+ textOnPrimary: { light: "#ffffff", dark: "#ffffff" },
24
+ /** Brand / primary interactive color. */
25
+ primary: { light: "#4657d9", dark: "#7c8cf8" },
26
+ /** Primary hover state. */
27
+ primaryHover: { light: "#3948b8", dark: "#98a5fa" },
28
+ /** Subtle primary background (selected rows, badges). */
29
+ primarySubtle: { light: "#eef0fd", dark: "#232b4d" },
30
+ /** Default border. */
31
+ border: { light: "#d9dde3", dark: "#2a3242" },
32
+ /** Emphasised border (inputs on focus-within, table headers). */
33
+ borderStrong: { light: "#aeb6c2", dark: "#3d4759" },
34
+ /** Destructive actions and errors. */
35
+ danger: { light: "#c93a3a", dark: "#f28b8b" },
36
+ /** Subtle danger background. */
37
+ dangerSubtle: { light: "#fdeeee", dark: "#3a2020" },
38
+ /** Success states. */
39
+ success: { light: "#1f7a4d", dark: "#6fd0a0" },
40
+ /** Subtle success background. */
41
+ successSubtle: { light: "#eaf7f0", dark: "#152e22" },
42
+ /** Warnings. */
43
+ warning: { light: "#9a6700", dark: "#e8b34b" },
44
+ /** Subtle warning background. */
45
+ warningSubtle: { light: "#fdf6e7", dark: "#33270f" },
46
+ /** Informational accents. */
47
+ info: { light: "#1168a7", dark: "#6cb8e8" },
48
+ /** Subtle info background. */
49
+ infoSubtle: { light: "#e9f4fb", dark: "#12293a" },
50
+ /** Focus ring. */
51
+ focusRing: { light: "#4657d9", dark: "#98a5fa" },
52
+ /** Overlay scrim behind modals. */
53
+ overlay: { light: "rgba(15, 18, 24, 0.45)", dark: "rgba(0, 0, 0, 0.6)" },
54
+ } as const;
55
+
56
+ // Constraint check (kept out of the public type surface for isolatedDeclarations).
57
+ const _colorsCheck: Record<string, ColorToken> = colors;
58
+ void _colorsCheck;
59
+
60
+ export type ColorTokenName = keyof typeof colors;
package/src/css.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Deterministic tokens.css generator. TS tokens are the single source of
3
+ * truth; this module renders them to CSS custom properties. The output is
4
+ * byte-stable: keys are sorted, numbers stringified canonically, no
5
+ * timestamps — same input, same bytes (unit-tested).
6
+ */
7
+ import { colors } from "./colors.js";
8
+ import { elevation } from "./elevation.js";
9
+ import { breakpoints } from "./breakpoints.js";
10
+ import { radii } from "./radii.js";
11
+ import { spacing } from "./spacing.js";
12
+ import { fontFamily, fontSize, fontWeight } from "./typography.js";
13
+
14
+ const PREFIX = "--stapel";
15
+
16
+ function kebab(name: string): string {
17
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
18
+ }
19
+
20
+ function sortedEntries<T>(record: Record<string, T>): [string, T][] {
21
+ return Object.entries(record).sort(([a], [b]) => (a < b ? -1 : 1));
22
+ }
23
+
24
+ function px(value: number): string {
25
+ return value === 0 ? "0" : `${String(value)}px`;
26
+ }
27
+
28
+ /** `cssVar("color-primary")` → `var(--stapel-color-primary)` */
29
+ export function cssVar(name: string): string {
30
+ return `var(${PREFIX}-${name})`;
31
+ }
32
+
33
+ /** Render the full tokens stylesheet (`:root` + `[data-theme="dark"]`). */
34
+ export function generateTokensCss(): string {
35
+ const root: string[] = [];
36
+ const dark: string[] = [];
37
+
38
+ for (const [name, pair] of sortedEntries(colors)) {
39
+ root.push(`${PREFIX}-color-${kebab(name)}: ${pair.light};`);
40
+ dark.push(`${PREFIX}-color-${kebab(name)}: ${pair.dark};`);
41
+ }
42
+ for (const [name, pair] of sortedEntries(elevation)) {
43
+ root.push(`${PREFIX}-elevation-${kebab(name)}: ${pair.light};`);
44
+ dark.push(`${PREFIX}-elevation-${kebab(name)}: ${pair.dark};`);
45
+ }
46
+ for (const [name, value] of sortedEntries(fontFamily)) {
47
+ root.push(`${PREFIX}-font-family-${kebab(name)}: ${value};`);
48
+ }
49
+ for (const [name, step] of sortedEntries(fontSize)) {
50
+ root.push(`${PREFIX}-font-size-${kebab(name)}: ${px(step.fontSize)};`);
51
+ root.push(`${PREFIX}-line-height-${kebab(name)}: ${px(step.lineHeight)};`);
52
+ }
53
+ for (const [name, value] of sortedEntries(fontWeight)) {
54
+ root.push(`${PREFIX}-font-weight-${kebab(name)}: ${String(value)};`);
55
+ }
56
+ for (const [name, value] of sortedEntries(spacing)) {
57
+ root.push(`${PREFIX}-space-${kebab(name)}: ${px(value)};`);
58
+ }
59
+ for (const [name, value] of sortedEntries(radii)) {
60
+ root.push(`${PREFIX}-radius-${kebab(name)}: ${px(value)};`);
61
+ }
62
+ for (const [name, value] of sortedEntries(breakpoints)) {
63
+ root.push(`${PREFIX}-breakpoint-${kebab(name)}: ${px(value)};`);
64
+ }
65
+
66
+ const indent = (lines: string[]): string =>
67
+ lines.map((l) => ` ${l}`).join("\n");
68
+
69
+ return [
70
+ "/* Generated by @stapel/tokens — do not edit. Source: src/*.ts */",
71
+ ":root {",
72
+ indent(root),
73
+ "}",
74
+ "",
75
+ '[data-theme="dark"] {',
76
+ indent(dark),
77
+ "}",
78
+ "",
79
+ ].join("\n");
80
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Elevation (box-shadow) levels. Light/dark pairs like colors: dark themes
3
+ * need stronger shadows to read as raised.
4
+ */
5
+ export interface ElevationToken {
6
+ readonly light: string;
7
+ readonly dark: string;
8
+ }
9
+
10
+ export const elevation = {
11
+ none: { light: "none", dark: "none" },
12
+ low: {
13
+ light: "0 1px 2px rgba(15, 18, 24, 0.08)",
14
+ dark: "0 1px 2px rgba(0, 0, 0, 0.4)",
15
+ },
16
+ medium: {
17
+ light: "0 2px 8px rgba(15, 18, 24, 0.12)",
18
+ dark: "0 2px 8px rgba(0, 0, 0, 0.5)",
19
+ },
20
+ high: {
21
+ light: "0 8px 24px rgba(15, 18, 24, 0.16)",
22
+ dark: "0 8px 24px rgba(0, 0, 0, 0.6)",
23
+ },
24
+ } as const;
25
+
26
+ const _elevationCheck: Record<string, ElevationToken> = elevation;
27
+ void _elevationCheck;
28
+
29
+ export type ElevationName = keyof typeof elevation;
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export { colors } from "./colors.js";
2
+ export type { ColorToken, ColorTokenName } from "./colors.js";
3
+ export {
4
+ typography,
5
+ fontFamily,
6
+ fontSize,
7
+ fontWeight,
8
+ } from "./typography.js";
9
+ export type { TypeStep, FontSizeName, FontWeightName } from "./typography.js";
10
+ export { spacing } from "./spacing.js";
11
+ export type { SpacingStep } from "./spacing.js";
12
+ export { radii } from "./radii.js";
13
+ export type { RadiusName } from "./radii.js";
14
+ export { elevation } from "./elevation.js";
15
+ export type { ElevationName, ElevationToken } from "./elevation.js";
16
+ export {
17
+ breakpoints,
18
+ breakpointOrder,
19
+ breakpointForWidth,
20
+ mediaQuery,
21
+ } from "./breakpoints.js";
22
+ export type { Breakpoint } from "./breakpoints.js";
23
+ export { generateTokensCss, cssVar } from "./css.js";
package/src/radii.ts ADDED
@@ -0,0 +1,11 @@
1
+ /** Border radii in px (`full` is a pill/circle sentinel). */
2
+ export const radii = {
3
+ none: 0,
4
+ sm: 4,
5
+ md: 8,
6
+ lg: 12,
7
+ xl: 20,
8
+ full: 9999,
9
+ } as const;
10
+
11
+ export type RadiusName = keyof typeof radii;
package/src/spacing.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** Spacing scale in px. Consume via TS or `--stapel-space-*` CSS vars. */
2
+ export const spacing = {
3
+ "0": 0,
4
+ "1": 4,
5
+ "2": 8,
6
+ "3": 12,
7
+ "4": 16,
8
+ "5": 24,
9
+ "6": 32,
10
+ "7": 48,
11
+ "8": 64,
12
+ } as const;
13
+
14
+ export type SpacingStep = keyof typeof spacing;
@@ -0,0 +1,42 @@
1
+ /** Typography scale. Sizes/line-heights in px, emitted as rem-friendly px CSS. */
2
+ export interface TypeStep {
3
+ readonly fontSize: number;
4
+ readonly lineHeight: number;
5
+ }
6
+
7
+ export const fontFamily = {
8
+ sans: "'Inter', 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif",
9
+ mono: "'JetBrains Mono', 'SF Mono', Menlo, Consolas, monospace",
10
+ } as const;
11
+
12
+ export const fontSize = {
13
+ xs: { fontSize: 12, lineHeight: 16 },
14
+ sm: { fontSize: 14, lineHeight: 20 },
15
+ md: { fontSize: 16, lineHeight: 24 },
16
+ lg: { fontSize: 18, lineHeight: 28 },
17
+ xl: { fontSize: 22, lineHeight: 30 },
18
+ "2xl": { fontSize: 28, lineHeight: 36 },
19
+ "3xl": { fontSize: 36, lineHeight: 44 },
20
+ } as const;
21
+
22
+ export const fontWeight = {
23
+ regular: 400,
24
+ medium: 500,
25
+ semibold: 600,
26
+ bold: 700,
27
+ } as const;
28
+
29
+ export interface Typography {
30
+ readonly fontFamily: typeof fontFamily;
31
+ readonly fontSize: typeof fontSize;
32
+ readonly fontWeight: typeof fontWeight;
33
+ }
34
+
35
+ export const typography: Typography = {
36
+ fontFamily: fontFamily,
37
+ fontSize: fontSize,
38
+ fontWeight: fontWeight,
39
+ };
40
+
41
+ export type FontSizeName = keyof typeof fontSize;
42
+ export type FontWeightName = keyof typeof fontWeight;
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "_comment": "Self-contained on purpose: standalone-buildable per frontend-standard §7. Mirrors the root tsconfig.base.json settings.",
4
+ "compilerOptions": {
5
+ "target": "ES2022",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "moduleResolution": "bundler",
9
+ "jsx": "react-jsx",
10
+ "strict": true,
11
+ "noUncheckedIndexedAccess": true,
12
+ "noImplicitOverride": true,
13
+ "exactOptionalPropertyTypes": true,
14
+ "isolatedModules": true,
15
+ "isolatedDeclarations": true,
16
+ "verbatimModuleSyntax": true,
17
+ "declaration": true,
18
+ "declarationMap": true,
19
+ "sourceMap": true,
20
+ "skipLibCheck": true,
21
+ "forceConsistentCasingInFileNames": true,
22
+ "outDir": "dist",
23
+ "rootDir": "src"
24
+ },
25
+ "include": ["src"]
26
+ }