@stridge/noctis-design-tokens 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +226 -0
  2. package/dist/apply-scopes.d.ts +12 -0
  3. package/dist/apply-scopes.js +30 -0
  4. package/dist/css.d.ts +10 -0
  5. package/dist/css.js +36 -0
  6. package/dist/graph/components.d.ts +6 -0
  7. package/dist/graph/components.js +4488 -0
  8. package/dist/graph/index.d.ts +11 -0
  9. package/dist/graph/index.js +26 -0
  10. package/dist/graph/inputs.d.ts +1 -0
  11. package/dist/graph/inputs.js +23 -0
  12. package/dist/graph/model.d.ts +205 -0
  13. package/dist/graph/model.js +282 -0
  14. package/dist/graph/registry.d.ts +67 -0
  15. package/dist/graph/registry.js +118 -0
  16. package/dist/graph/roles.d.ts +5 -0
  17. package/dist/graph/roles.js +151 -0
  18. package/dist/graph/scales.d.ts +1 -0
  19. package/dist/graph/scales.js +1296 -0
  20. package/dist/graph/serialize.d.ts +16 -0
  21. package/dist/graph/serialize.js +25 -0
  22. package/dist/graph/statics.d.ts +1 -0
  23. package/dist/graph/statics.js +419 -0
  24. package/dist/index.d.ts +26 -0
  25. package/dist/index.js +25 -0
  26. package/dist/palettes.d.ts +70 -0
  27. package/dist/palettes.js +114 -0
  28. package/dist/react/provider.d.ts +23 -0
  29. package/dist/react/provider.js +28 -0
  30. package/dist/react.d.ts +3 -0
  31. package/dist/react.js +3 -0
  32. package/dist/scales.d.ts +186 -0
  33. package/dist/scales.js +186 -0
  34. package/dist/semantic.d.ts +36 -0
  35. package/dist/semantic.js +49 -0
  36. package/dist/swatches.d.ts +24 -0
  37. package/dist/swatches.js +57 -0
  38. package/dist/tokens.css +2607 -0
  39. package/dist/tokens.dtcg.json +3475 -0
  40. package/dist/tokens.json +14658 -0
  41. package/dist/tokens.tailwind.css +479 -0
  42. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # @stridge/noctis-design-tokens
2
+
3
+ The semantic token layer on top of `@stridge/noctis-theme-engine`. The engine generates the private
4
+ primitive ramp; this package maps it to intent-named **semantic roles** as framework-neutral CSS
5
+ variables (`--noctis-color-*`), ships the foundation scales, and wires the **elevation scopes**.
6
+ The Tailwind bridge is one optional consumer of those roles. Components style with the roles below
7
+ — never the primitives.
8
+
9
+ ## Usage
10
+
11
+ The token CSS stands alone — it is `@import`ed by `@stridge/noctis/styles.css`, and needs no
12
+ styling framework. Import it directly only when consuming this package standalone:
13
+
14
+ ```ts
15
+ // app CSS entry
16
+ @import "@stridge/noctis-design-tokens/tokens.css";
17
+ ```
18
+
19
+ Tailwind authors who want the roles as utilities additionally import the bridge stylesheet
20
+ (`@stridge/noctis-design-tokens/tokens.tailwind.css`).
21
+
22
+ ```tsx
23
+ // app root — keeps the root theme + elevation scopes in sync across theme changes
24
+ import { ThemeProvider } from "@stridge/noctis-design-tokens/react";
25
+
26
+ <ThemeProvider>{children}</ThemeProvider>;
27
+ ```
28
+
29
+ `SEMANTIC_ROLES` and `ELEVATION_SCOPES` export the catalog as data (for the debug swatch
30
+ page); `defaultThemeCss()` returns the build-time default `:root` CSS; `applyThemeWithScopes`
31
+ applies a theme + both scopes to an element.
32
+
33
+ ## Token grammar
34
+
35
+ Every token serializes to a canonical CSS custom property under one closed grammar. The
36
+ `{category}` segment is drawn from the structural-category vocabulary, never written into the
37
+ id; `cssVarName` composes the namespace, tier, and category segments around the serialized
38
+ name body.
39
+
40
+ | Form | Stratum | Shape |
41
+ | --- | --- | --- |
42
+ | Foundation | `foundation` | `--noctis-{category}-{name}` |
43
+ | Semantic | `semantic` | `--noctis-{category}-{name}`, and for color roles specifically `--noctis-color-{role}` |
44
+ | Seed | `seed` | `--noctis-seed-{name}` |
45
+ | Component mint | `component` | `--noctis-{component}(-{anatomy})-{property}(-{state})` |
46
+ | Slot internal | (recipe) | `--_{component}-*` |
47
+
48
+ Foundation and semantic tokens carry a structural category; seeds and component mints do not.
49
+ Semantic tokens are always category `color`, so a semantic role serializes as
50
+ `--noctis-color-{role}`. A component mint omits any segment it lacks, in fixed order
51
+ (`component` → `anatomy` → `property` → `state`); the rest (no-state) form drops the trailing
52
+ state segment. Slot internals live in the `--_` namespace a recipe declares on its slot to
53
+ carry the resolution chain.
54
+
55
+ The 18 structural categories (`TOKEN_CATEGORIES`):
56
+
57
+ `color` · `space` · `size` · `text` · `leading` · `radius` · `shadow` · `duration` · `ease` ·
58
+ `z` · `font` · `tracking` · `breakpoint` · `container` · `border` · `opacity` · `blur` ·
59
+ `animate`.
60
+
61
+ ## Namespace contract
62
+
63
+ Every canonical token serializes under the `--noctis` prefix (`ROLE_VAR_PREFIX`).
64
+
65
+ `--noctis-engine-*` is the engine's **reserved private namespace**. The graph validator
66
+ rejects any graph token whose canonical name serializes into it — the engine owns that
67
+ namespace for its primitive ramp, and no foundation, semantic, seed, or component token may
68
+ collide with it.
69
+
70
+ Canonical names are consumed through the **bridge**: via the Tailwind bridge utilities the
71
+ generator emits (`bg-*`, `text-*`, `border-*`, the named `shadow-*`, the scale keys), or, in a
72
+ recipe, via a minted `--_` internal. A recipe never references the canonical literal directly —
73
+ that boundary is enforced by `stridge/banned-namespaces`.
74
+
75
+ ## Role catalog
76
+
77
+ Each role is a Tailwind utility backed by one engine primitive. The default Tailwind palette
78
+ is disabled, so these are the only colors available.
79
+
80
+ ### Surfaces
81
+
82
+ | Utility | Role |
83
+ | --- | --- |
84
+ | `bg-background` | App canvas |
85
+ | `bg-hover` | Hover on a canvas row |
86
+ | `bg-surface` / `bg-surface-hover` | Card / panel + its hover |
87
+ | `bg-surface-raised` | Highest neutral surface |
88
+ | `bg-sunken` | Recessed wells |
89
+ | `bg-selected` / `bg-selected-hover` | Selected row + its hover |
90
+ | `bg-focus` | Keyboard-focused row |
91
+ | `bg-overlay` | Dialog scrim |
92
+ | `bg-sidebar-item` / `bg-sidebar-item-active` | Sidebar item hover / active |
93
+
94
+ ### Text
95
+
96
+ `text-foreground` (primary) · `text-muted` (secondary) · `text-subtle` (faint) · `text-link`.
97
+
98
+ ### Borders & ring
99
+
100
+ `border-faint` · `border-border` (default) · `border-strong` · `border-selected` ·
101
+ `border-field` · `bg-divider` / `border-divider` · `ring-ring`.
102
+
103
+ ### Primary
104
+
105
+ The neutral (white) emphasis, decoupled from the chromatic accent: `bg-primary` ·
106
+ `text-primary-foreground` · `bg-primary-hover` / `bg-primary-active` (the white dimmed toward the
107
+ seed — real opaque tokens that re-derive per elevation, never a translucent white).
108
+
109
+ ### Accent
110
+
111
+ `bg-accent` · `text-accent-foreground` · `bg-accent-hover` · `bg-accent-active` ·
112
+ `bg-accent-muted`.
113
+
114
+ ### Controls & fields
115
+
116
+ Controls are a derived neutral tier off the seed (their own gentle gain), so each step is a solid
117
+ fill — not an opacity wash — and re-derives inside every elevation scope:
118
+
119
+ `bg-control` + `text-control-foreground` / `bg-control-hover` / `bg-control-selected` /
120
+ `bg-control-selected-hover` (secondary buttons, segmented) · `bg-control-ghost` /
121
+ `bg-control-ghost-hover` / `bg-control-ghost-selected` / `bg-control-ghost-selected-hover` (ghost
122
+ buttons, tabs, menu items) · `bg-field` / `bg-field-hover` / `bg-field-focus` + `border-field`
123
+ (inputs). Filled chromatic controls take a desaturated on-fill label (`text-accent-foreground`,
124
+ `text-danger-foreground`); disabled controls dim with `opacity-disabled`, never a color swap.
125
+
126
+ ### Status
127
+
128
+ For `danger` / `success` / `warning` / `info`: `bg-*` (fill), `text-*-foreground` (on-fill),
129
+ `bg-*-muted` (soft surface), `text-*-muted-foreground` (on-muted).
130
+
131
+ ### Shadows & type
132
+
133
+ `shadow-card` / `shadow-popover` / `shadow-modal` / `shadow-inset`; the named size scale
134
+ `text-micro … text-title-1` (all scaled live by the root `--noctis-seed-font-scale` multiplier); families
135
+ `font-sans` / `font-mono`; the radius scale `rounded-xs … rounded-xl` (`min()`-capped surfaces) plus
136
+ `rounded-control` (buttons/chips — follows the `--noctis-seed-radius` knob uncapped, so it pills at the Pill
137
+ setting) and `rounded-full` (circles); `ease-standard|in|out|in-out`.
138
+
139
+ ## Elevation scopes
140
+
141
+ A scope is a full re-generation of the theme at a shifted base lightness — text re-solves its
142
+ contrast and borders re-derive — so a raised surface is a genuinely different, internally
143
+ consistent color, not a tint. Set a `data-elevation` attribute; everything inside keeps using
144
+ the normal roles and resolves to that scope:
145
+
146
+ - `data-elevation="elevated"` — dialogs, sheets, drawers, popovers, tooltips.
147
+ - `data-elevation="menu"` — the highest float: dropdown and context menus, select listboxes, the command palette (lifts a touch more than `elevated`).
148
+ - `data-elevation="sunken"` — recessed content wells and insets.
149
+
150
+ Page-level chrome (header, sidebar, rail) stays at the **root** level — no scope — so it sits flush with
151
+ the canvas it frames, set off by a border. Only overlays float (`elevated`/`menu`) and only wells recede (`sunken`).
152
+
153
+ **Portals** overlay to `<body>`, so set `data-elevation` on the **portalled node itself**
154
+ (`Popover.Popup`, `Menu.Popup`, …), not a JSX ancestor. Scopes are **absolute and
155
+ single-level** — computed from the active theme's base, they do not compound when nested.
156
+
157
+ **Paint a control-hosting elevated surface with `bg-background`, not `bg-surface`.** Controls
158
+ derive off their scope's *base* (`bg-1`), so a ghost or secondary control only separates from the
159
+ surface it sits on when that surface **is** the scope base. A dialog, sheet, rail, or menu therefore
160
+ uses `bg-background` inside its `data-elevation` scope (the scope's lift, border, and shadow give it
161
+ presence) — then a ghost button's hover clears the panel by the full control delta. `bg-surface`
162
+ (`bg-3`) is for **non-interactive** raised content (cards, wells); a ghost control placed on it would
163
+ hover almost invisibly. The invariant: an elevated surface that hosts controls paints its
164
+ elevation's own base, and controls re-derive off that same base — so surface and control can never
165
+ drift apart.
166
+
167
+ ## Component consumption contract
168
+
169
+ Every planned surface is expressible with roles alone:
170
+
171
+ - **Card / Panel:** `bg-surface border border-border shadow-card`; clickable → `hover:bg-surface-hover`.
172
+ - **Input / Textarea / Select:** `bg-field border border-field shadow-inset`; hover → `hover:bg-field-hover hover:border-field-hover`; focus → `bg-field-focus focus-visible:ring ring-ring border-field-focus`; invalid → `border-field-invalid`.
173
+ - **Button — primary (neutral white):** `bg-primary text-primary-foreground hover:bg-primary-hover active:bg-primary-active`.
174
+ - **Button — accent:** `bg-accent text-accent-foreground hover:bg-accent-hover active:bg-accent-active`.
175
+ - **Button — secondary:** `bg-control text-control-foreground border border-border hover:bg-control-hover`; pressed → `bg-control-selected`.
176
+ - **Button — ghost / Tab / Menu item:** `bg-control-ghost text-muted hover:bg-control-ghost-hover hover:text-foreground`; active → `bg-control-ghost-selected`.
177
+ - **Menu / Select content:** portalled `data-elevation="menu"`, `bg-background border border-border shadow-popover`; items use ghost-control roles; keyboard item → `bg-focus`.
178
+ - **Popover:** `data-elevation="elevated"` + `bg-background border-border shadow-popover` (control-hosting; a pure-content popover may use `bg-surface`).
179
+ - **Dialog / Drawer / Sheet:** scrim `bg-overlay`; panel `data-elevation="elevated"` `bg-background border-border shadow-modal`.
180
+ - **Sidebar / Rail:** page chrome at the root level — `bg-background` set off by a border (no `data-elevation`); items `hover:bg-sidebar-item` or ghost-control roles, active → `bg-sidebar-item-active text-foreground`.
181
+ - **Table rows:** `hover:bg-surface-hover`; selected `bg-selected`, selected+hover `bg-selected-hover`, keyboard `bg-focus`.
182
+ - **Separator:** the standalone hairline fills from the neutral `border` role; heavier group dividers drawn _inside_ a menu/list step up to `divider`.
183
+
184
+ ## Status families
185
+
186
+ Each of `danger` / `success` / `warning` / `info` exposes a complete sub-role set: `bg-<s>`
187
+ (solid fill) · `text-<s>` (colored text on neutral) · `text-<s>-foreground` (on-fill text) ·
188
+ `bg-<s>-hover` · `bg-<s>-muted` + `text-<s>-muted-foreground` (soft surface) · `bg-<s>-faint`
189
+ (row wash) · `border-<s>` (solid outline) · `border-<s>-subtle` (muted-callout outline). A
190
+ status-tint **gradient** (`bg-<s>-gradient`, faint hue → neutral) backs "In review" / "On
191
+ hold" status cards. Destructive button: `bg-danger hover:bg-danger-hover text-danger-foreground`.
192
+
193
+ ## Charts & avatars (static, accent-independent)
194
+
195
+ Chart and avatar palettes are **authored, not engine-derived** — categorical identity must
196
+ stay stable when the accent changes, so they do **not** re-theme. They carry light and dark
197
+ values and switch on the resolved mode (`data-theme`).
198
+
199
+ - **Charts:** `bg-chart-1` … `bg-chart-8` (categorical series, hue- and lightness-separated
200
+ for color-vision-deficiency safety) · `text-chart-positive` / `text-chart-negative` /
201
+ `text-chart-neutral` (= success / danger / muted) · `border-chart-grid` · `text-chart-axis`
202
+ · `bg-chart-track` · `bg-chart-tooltip`.
203
+ - **Avatars:** `bg-avatar-1` … `bg-avatar-10` chosen by `avatarIndex(id)` (a stable hash);
204
+ `text-avatar-foreground` is the knockout for initials.
205
+
206
+ ## States: disabled, inverse, fields, forms
207
+
208
+ - **Disabled = opacity, not a color family.** Apply `opacity-disabled` (the `--noctis-opacity-disabled`
209
+ scalar) to disabled controls. `text-disabled` is the one explicit disabled color, for text
210
+ that must stay legible where dimming the whole element is wrong.
211
+ - **Inverse:** `bg-inverse` + `text-inverse` for inverted chips/snackbars; tooltips reuse the
212
+ same `bg-inverse` + `text-inverse` — there is **no** dedicated tooltip color role.
213
+ - **Fields:** `bg-field border border-field`; state fills `bg-field-hover` (rest hover) and
214
+ `bg-field-focus` (accent-tinted focus surface); state borders `border-field-hover`,
215
+ `border-field-focus`, `border-field-invalid`.
216
+ - **Form elements:** `bg-toggle-track-{off,on}` (+ hover; the on-track stays full accent when
217
+ disabled) + `bg-toggle-thumb`; `bg-checkbox` + `border-checkbox` (+`-hover`) + `bg-checkbox-checked`
218
+ + `text-checkbox-check`; `bg-radio-checked` + `border-radio`.
219
+ - **Chrome / utilities:** `bg-header`; `bg-scrollbar-thumb` / `bg-scrollbar-track`;
220
+ `bg-code` + `text-code-foreground`; `bg-kbd` + `border-kbd`; `text-menu-shortcut`;
221
+ `bg-data-grid-header` / `bg-data-grid-row-hover(-strong)` / `border-data-grid` /
222
+ `border-data-grid-selected`; `shadow-focus`.
223
+
224
+ `SEMANTIC_ROLES` (engine-backed) and `STATIC_TOKENS` (authored) export the full catalog as
225
+ data; the docs site's `/foundations/tokens` page renders the full token graph — every scale,
226
+ role, and component token.
@@ -0,0 +1,12 @@
1
+ import { GenerateThemeOptions, ThemeInput } from "@stridge/noctis-theme-engine";
2
+
3
+ //#region src/apply-scopes.d.ts
4
+ /**
5
+ * Generate `input` and emit it — root primitives plus the elevated/menu/sunken scope namespaces —
6
+ * for `root` (default the document element), as one rule in the engine layer. `options.overrides`
7
+ * thread into every set: root entries participate in the root derivation and shift the scope
8
+ * bases; scope-qualified entries (`"el-bg-3"`) act inside their scope's generation only.
9
+ */
10
+ declare function applyThemeWithScopes(input: ThemeInput, root?: Element, options?: GenerateThemeOptions): void;
11
+ //#endregion
12
+ export { applyThemeWithScopes };
@@ -0,0 +1,30 @@
1
+ import { applyEngineVars, generateScopeTheme, generateTheme, scopedEngineVar } from "@stridge/noctis-theme-engine";
2
+ //#region src/apply-scopes.ts
3
+ /**
4
+ * Runtime theme application with elevation scopes.
5
+ *
6
+ * Emits the root primitives plus the `elevated` (`--noctis-engine-el-*`), `menu`
7
+ * (`--noctis-engine-mn-*`), and `sunken` (`--noctis-engine-su-*`) scope sets for a single target
8
+ * (default `:root`) through the per-document engine sheet, inside `@layer noctis.engine`. One
9
+ * root write re-themes every `[data-elevation]` subtree through the static indirection in
10
+ * `tokens.css` — no per-element work, so any number of dynamically-mounted overlays stay correct
11
+ * across a theme change.
12
+ */
13
+ function addNamespaced(vars, map, prefix) {
14
+ for (const key in map) vars[scopedEngineVar(key, prefix)] = map[key];
15
+ }
16
+ /**
17
+ * Generate `input` and emit it — root primitives plus the elevated/menu/sunken scope namespaces —
18
+ * for `root` (default the document element), as one rule in the engine layer. `options.overrides`
19
+ * thread into every set: root entries participate in the root derivation and shift the scope
20
+ * bases; scope-qualified entries (`"el-bg-3"`) act inside their scope's generation only.
21
+ */
22
+ function applyThemeWithScopes(input, root, options) {
23
+ const vars = { ...generateTheme(input, options) };
24
+ addNamespaced(vars, generateScopeTheme(input, "elevated", options), "el");
25
+ addNamespaced(vars, generateScopeTheme(input, "menu", options), "mn");
26
+ addNamespaced(vars, generateScopeTheme(input, "sunken", options), "su");
27
+ applyEngineVars(vars, root);
28
+ }
29
+ //#endregion
30
+ export { applyThemeWithScopes };
package/dist/css.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { GenerateThemeOptions } from "@stridge/noctis-theme-engine";
2
+
3
+ //#region src/css.d.ts
4
+ /**
5
+ * The default theme's engine primitives — root plus the elevated/menu/sunken scopes — as a
6
+ * static rule inside `@layer noctis.engine`.
7
+ */
8
+ declare function defaultThemeCss(selector?: string, options?: GenerateThemeOptions): string;
9
+ //#endregion
10
+ export { defaultThemeCss };
package/dist/css.js ADDED
@@ -0,0 +1,36 @@
1
+ import { ENGINE_LAYER, declarationsFor, defaultPreset, generateScopeTheme, generateTheme, scopedEngineVar } from "@stridge/noctis-theme-engine";
2
+ //#region src/css.ts
3
+ /**
4
+ * Build-time default theme CSS.
5
+ *
6
+ * Emits the default brand theme's primitive variables as a static `:root` rule so the app's
7
+ * first paint is correct with zero JavaScript. Alongside the root primitives it emits the
8
+ * `elevated`, `menu`, and `sunken` scope sets under the `--noctis-engine-el-*` /
9
+ * `--noctis-engine-mn-*` / `--noctis-engine-su-*` namespaces; the `[data-elevation]` selectors in
10
+ * `tokens.css` re-point the base roles to these, so raised, floating, and recessed surfaces are
11
+ * correct on first paint too. Rendered into the document head by the app layout.
12
+ *
13
+ * The rule lives in `@layer noctis.engine` — the same layer the runtime sheet declares into — so
14
+ * SSR and CSR emission carry identical names, values, and cascade position, and any unlayered
15
+ * author rule on an engine variable beats both. A cascade override replaces one variable's value
16
+ * only; dependents do not re-derive (pass `overrides` for derivation-participating overrides).
17
+ */
18
+ /**
19
+ * Serialize a theme map with the scope-set namespace
20
+ * (`--noctis-engine-bg-1` → `--noctis-engine-el-bg-1`). Routes through {@link scopedEngineVar} so
21
+ * the static first-paint names match the runtime writer and the `tokens.css` scope blocks exactly.
22
+ */
23
+ function namespaced(map, prefix) {
24
+ let out = "";
25
+ for (const key in map) out += `${scopedEngineVar(key, prefix)}:${map[key]};`;
26
+ return out;
27
+ }
28
+ /**
29
+ * The default theme's engine primitives — root plus the elevated/menu/sunken scopes — as a
30
+ * static rule inside `@layer noctis.engine`.
31
+ */
32
+ function defaultThemeCss(selector = ":root", options) {
33
+ return `@layer ${ENGINE_LAYER}{${selector}{${declarationsFor(generateTheme(defaultPreset, options))}${namespaced(generateScopeTheme(defaultPreset, "elevated", options), "el")}${namespaced(generateScopeTheme(defaultPreset, "menu", options), "mn")}${namespaced(generateScopeTheme(defaultPreset, "sunken", options), "su")}}}`;
34
+ }
35
+ //#endregion
36
+ export { defaultThemeCss };
@@ -0,0 +1,6 @@
1
+ import { ComponentDeclaration } from "./model.js";
2
+
3
+ //#region src/graph/components.d.ts
4
+ declare const COMPONENT_DECLARATIONS: readonly ComponentDeclaration[];
5
+ //#endregion
6
+ export { COMPONENT_DECLARATIONS };