@phcdevworks/spectre-tokens 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,33 +1,31 @@
1
1
  # @phcdevworks/spectre-tokens
2
2
 
3
- ### **The DNA (Layer 1 of the Spectre 8-Layer Arsenal)**
3
+ [![GitHub issues](https://img.shields.io/github/issues/phcdevworks/spectre-tokens)](https://github.com/phcdevworks/spectre-tokens/issues)
4
+ [![GitHub pulls](https://img.shields.io/github/issues-pr/phcdevworks/spectre-tokens)](https://github.com/phcdevworks/spectre-tokens/pulls)
5
+ [![License](https://img.shields.io/github/license/phcdevworks/spectre-tokens)](LICENSE)
4
6
 
5
- `@phcdevworks/spectre-tokens` is the Single Source of Truth for the entire Spectre ecosystem. It defines the visual language—colors, typography, space, and semantic roles—that all other layers consume.
7
+ `@phcdevworks/spectre-tokens` is the design-token package of the Spectre system
8
+ for downstream Spectre packages and compatible applications.
6
9
 
7
- 🤝 **[Contributing Guide](CONTRIBUTING.md)** | 📝 **[Changelog](CHANGELOG.md)** | 🏛️ **[Spectre Arsenal](https://github.com/phcdevworks)**
10
+ Maintained by PHCDevworks, it defines the visual language, semantic roles, and
11
+ token contracts consumed downstream. It keeps visual meaning centralized in
12
+ token data while downstream UI packages define structure and adapter packages
13
+ translate those contracts for specific frameworks and runtimes.
8
14
 
9
- ---
15
+ [Contributing](CONTRIBUTING.md) | [Changelog](CHANGELOG.md) |
16
+ [Security Policy](SECURITY.md)
10
17
 
11
- ## 🏗️ Core Architecture
18
+ ## Key capabilities
12
19
 
13
- This package is the foundation. It follows a strict **JSON-First** policy—authoritative values live in `tokens/` and are transformed into multiple consumption modes (CSS, JS/TS, Tailwind).
14
-
15
- - 🧬 **Authoritative JSON**: Domain-specific token storage for better maintainability.
16
- - 🧪 **Type-Safe DNA**: 100% strict TypeScript interfaces derived directly from JSON.
17
- - 🌪️ **Multi-Format Output**: Generates CSS Variables, Tailwind Presets, and ESM/CJS exports.
18
- - **Accessibility First**: Encodes WCAG constraints (contrast, touch targets) at the token level.
19
-
20
- ---
21
-
22
- **Single source of truth:** JSON is the source. Everything else is generated.
23
-
24
- - ✅ **Modular Architecture**: domain-specific token storage for better maintainability
25
- - ✅ **AI-Ready DNA**: Zero `any` types and 100% strict TypeScript build process
26
- - ✅ **Auto-Generated Typings**: Strictly-typed TS interfaces derived directly from JSON
27
- - ✅ **Multi-File Merging**: Type-safe merging of cross-domain token references
28
- - ✅ **JS/TS objects**, Tailwind theme + preset, and CSS variable outputs
29
- - ✅ **CRO-focused surfaces** (buttons, forms, states) and accessibility-first tokens
30
- - ✅ **Type-safe outputs** with bundled `.d.ts` files
20
+ - Uses `tokens/` as the source of truth for design-token data
21
+ - Generates JavaScript, TypeScript, CSS, and Tailwind theme exports from shared
22
+ token sources
23
+ - Defines semantic token contracts for surfaces, text, components, buttons,
24
+ forms, and modes
25
+ - Exposes primitives and semantic roles for downstream packages and compatible
26
+ applications
27
+ - Keeps visual meaning centralized so downstream consumers do not redefine token
28
+ contracts
31
29
 
32
30
  ## Installation
33
31
 
@@ -35,1503 +33,187 @@ This package is the foundation. It follows a strict **JSON-First** policy—auth
35
33
  npm install @phcdevworks/spectre-tokens
36
34
  ```
37
35
 
38
- ## Quick Start
36
+ ## Quick start
39
37
 
40
- ### Option 1: CSS Variables (fastest)
38
+ ### CSS import
41
39
 
42
- **Recommended (bundlers):**
43
-
44
- If you’re not using a bundler, copy `dist/index.css` into your app and link it.
40
+ Import the generated CSS variables:
45
41
 
46
42
  ```css
47
- @import "@phcdevworks/spectre-tokens/dist/index.css";
48
- ```
49
-
50
- or:
51
-
52
- ```ts
53
- import "@phcdevworks/spectre-tokens/dist/index.css";
54
- ```
55
-
56
- **Use semantic tokens (recommended):**
57
-
58
- ```css
59
- .my-button {
60
- background: var(--sp-button-primary-bg);
61
- color: var(--sp-button-primary-text);
62
- padding: var(--sp-space-12) var(--sp-space-24);
63
- border-radius: var(--sp-radius-md);
64
- }
65
- ```
66
-
67
- > Note: Raw palette tokens like `--sp-color-brand-500` are stable utilities, but they do not adapt across modes by themselves. Prefer semantic tokens (`surface.*`, `text.*`, `component.*`, `buttons.*`, `forms.*`) for theme-aware UI.
68
-
69
- ### Option 2: JavaScript/TypeScript
70
-
71
- ```ts
72
- import tokens from "@phcdevworks/spectre-tokens";
73
-
74
- const styles = {
75
- color: tokens.colors.brand["500"],
76
- padding: `${tokens.space["12"]} ${tokens.space["24"]}`,
77
- borderRadius: tokens.radii.md,
78
- };
79
- ```
80
-
81
- ### Option 3: Tailwind CSS
82
-
83
- ```ts
84
- // tailwind.config.ts
85
- import { tailwindPreset } from "@phcdevworks/spectre-tokens";
86
-
87
- export default {
88
- presets: [tailwindPreset],
89
- content: ["./src/**/*.{js,jsx,ts,tsx}"],
90
- };
91
- ```
92
-
93
- ```html
94
- <!-- Use Spectre tokens via Tailwind utilities -->
95
- <button class="bg-brand-500 text-white px-6 py-3 rounded-md shadow-md">
96
- Click me
97
- </button>
43
+ @import '@phcdevworks/spectre-tokens/index.css';
98
44
  ```
99
45
 
100
- > Tip: Tailwind palette utilities like `bg-brand-500` are stable, but they won’t automatically adapt across modes. For theme-aware UI, prefer semantic CSS variables (`surface.*`, `text.*`, `component.*`) or Spectre UI recipes.
46
+ ### Token usage
101
47
 
102
- ## Usage
103
-
104
- ### 1. Import tokens (JS/TS)
48
+ Load the token object in JavaScript or TypeScript:
105
49
 
106
50
  ```ts
107
- import tokens, {
108
- tailwindTheme,
109
- tailwindPreset,
110
- generateCssVariables,
111
- } from "@phcdevworks/spectre-tokens";
112
-
113
- console.log(tokens.colors.brand["500"]); // "#8652ff"
114
- console.log(tokens.space["16"]); // "1rem"
115
- console.log(tokens.buttons.primary.bg); // "#8652ff"
116
- ```
117
-
118
- **Exports:**
119
-
120
- - `tokens` (default export): Complete token object with flattened structure for easy access
121
- - `tailwindTheme`: Ready-to-use Tailwind theme object
122
- - `tailwindPreset`: Preset for Tailwind config (includes theme)
123
- - `generateCssVariables()`: Generate custom `--sp-*` CSS variable strings with scoped selectors or prefixes
51
+ import tokens from '@phcdevworks/spectre-tokens'
124
52
 
125
- `generateCssVariables()` returns a CSS **string** (e.g. `:root { --sp-... }`) you can write to a file or inject into a page.
126
-
127
- **Token Structure (high-level namespaces):**
128
-
129
- The `tokens` object includes:
130
-
131
- - `colors`: Color palettes (brand, neutral, accent, success, warning, error, info, focus)
132
- - `space`: Spacing scale (0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80, 96)
133
- - `layout`: Semantic layout tokens (section, stack, container padding/gaps)
134
- - `radii`: Border radius values (none, sm, md, lg, pill)
135
- - `typography`: Font families and complete typography scale with metadata
136
- - `font`: Simplified font size/lineHeight/weight tokens for quick access
137
- - `shadows`: Box shadow tokens (none, sm, md, lg)
138
- - `breakpoints`: Responsive breakpoints (sm, md, lg, xl, 2xl)
139
- - `zIndex`: Z-index scale (base, dropdown, sticky, fixed, overlay, modal, popover, tooltip)
140
- - `transitions`: Duration and easing tokens
141
- - `animations`: Animation presets with duration, easing, and keyframe references
142
- - `buttons`: Button state tokens (primary, secondary, ghost, danger, success)
143
- - `forms`: Form state tokens (default, hover, focus, valid, invalid, disabled)
144
- - `accessibility`: WCAG compliance tokens (focus ring, touch targets, min text size)
145
- - `opacity`: Opacity scale (hover, active, disabled, focus, overlay, tooltip)
146
- - `borders`: Border color tokens
147
- - `surface`: Semantic surface backgrounds (page, card, input, overlay)
148
- - `text`: Semantic text colors (onPage, onSurface with default/muted/subtle/meta/**brand**)
149
- - `component`: Component-specific tokens (card, input, button, badge, iconBox)
150
- - `modes`: Theme mode definitions (default/light and dark)
151
-
152
- > Tokens may exist before UI primitives land in `@phcdevworks/spectre-ui`. Tokens define meaning; UI defines structure.
153
-
154
- ### 2. CSS variables
155
-
156
- ```css
157
- @import "@phcdevworks/spectre-tokens/dist/index.css";
158
-
159
- .button {
160
- color: var(--sp-text-on-page-default);
161
- padding-inline: var(--sp-space-16);
162
- border-radius: var(--sp-radius-pill);
163
- box-shadow: var(--sp-shadow-md);
53
+ const card = {
54
+ background: tokens.surface.page,
55
+ color: tokens.text.onPage.default,
56
+ padding: tokens.space['16'],
57
+ borderRadius: tokens.radii.md
164
58
  }
165
59
  ```
166
60
 
167
- To scope or re-prefix variables:
61
+ ### Tailwind preset usage
168
62
 
169
- ```ts
170
- const css = generateCssVariables(tokens, {
171
- selector: ".spectre-scope",
172
- prefix: "sp",
173
- });
174
- ```
175
-
176
- ### 3. Tailwind integration
63
+ Use the generated Tailwind preset when you want the package to populate theme
64
+ values from the token contract:
177
65
 
178
66
  ```ts
179
67
  // tailwind.config.ts
180
- import { tailwindPreset } from "@phcdevworks/spectre-tokens";
68
+ import { tailwindPreset } from '@phcdevworks/spectre-tokens'
181
69
 
182
70
  export default {
183
- presets: [tailwindPreset],
184
- };
185
- ```
186
-
187
- Works with Tailwind 3.x and 4.x through the classic config API; no Oxide lock-in required.
188
-
189
- ## Token Surfaces
190
-
191
- ### Conversion-ready button variants
192
-
193
- ```ts
194
- tokens.buttons.primary; // CTA baseline
195
- tokens.buttons.secondary; // Outlined
196
- tokens.buttons.ghost; // Low-emphasis
197
- tokens.buttons.danger; // Destructive
198
- tokens.buttons.success; // Confirmations
199
- ```
200
-
201
- Each variant ships with `bg`, `bgHover`, `bgActive`, `bgDisabled`, `text`, `textDisabled`, `border`, and `borderDisabled`, ensuring consistent CRO states.
202
-
203
- ```css
204
- .cta-button {
205
- background: var(--sp-button-primary-bg);
206
- color: var(--sp-button-primary-text);
207
- }
208
- .cta-button:hover {
209
- background: var(--sp-button-primary-bg-hover);
210
- }
211
- ```
212
-
213
- ### Form states (validation + UX)
214
-
215
- ```ts
216
- tokens.forms.default;
217
- tokens.forms.hover;
218
- tokens.forms.focus;
219
- tokens.forms.valid;
220
- tokens.forms.invalid;
221
- tokens.forms.disabled;
222
- ```
223
-
224
- ```css
225
- .input:focus {
226
- border-color: var(--sp-form-focus-border);
227
- outline: var(--sp-focus-ring-width) var(--sp-focus-ring-style)
228
- var(--sp-form-focus-ring);
229
- }
230
- .input.error {
231
- border-color: var(--sp-form-invalid-border);
232
- background: var(--sp-form-invalid-bg);
233
- }
234
- ```
235
-
236
- ### Accessibility helpers
237
-
238
- ```ts
239
- tokens.accessibility.focusRing.width; // 2px
240
- tokens.accessibility.focusRing.offset; // 2px
241
- tokens.accessibility.minTouchTarget; // 44px (WCAG 2.5.5)
242
- tokens.accessibility.minTextSize; // 16px
243
- tokens.colors.focus.primary; // Brand-aligned focus
244
- tokens.colors.focus.error; // Error state focus
245
- tokens.colors.focus.info; // Info state focus
246
- ```
247
-
248
- ### Motion & micro-interactions
249
-
250
- ```ts
251
- tokens.animations.fadeIn;
252
- tokens.animations.slideDown;
253
- tokens.animations.scaleIn;
254
- tokens.animations.bounce;
255
- tokens.animations.shake;
256
- tokens.animations.pulse;
257
- ```
258
-
259
- ```css
260
- .modal {
261
- animation: fade-in var(--sp-animation-fade-in-duration)
262
- var(--sp-animation-fade-in-easing);
263
- }
264
- ```
265
-
266
- ### Opacity scale
267
-
268
- ```ts
269
- tokens.opacity.hover; // "0.92"
270
- tokens.opacity.active; // "0.84"
271
- tokens.opacity.disabled; // "0.38"
272
- tokens.opacity.overlay; // "0.5"
273
- tokens.opacity.focus; // "1"
274
- tokens.opacity.tooltip; // "0.95"
275
- ```
276
-
277
- ### Typography: Font vs Typography
278
-
279
- Spectre provides **two ways** to access typography tokens:
280
-
281
- #### 1. `tokens.font.*` - Simplified Quick Access
282
-
283
- For simple use cases, use the `font` object which provides direct access to size, line height, and weight:
284
-
285
- ```ts
286
- tokens.font.xs; // { size: "0.75rem", lineHeight: "1.25rem", weight: 400 }
287
- tokens.font.sm; // { size: "0.875rem", lineHeight: "1.5rem", weight: 400 }
288
- tokens.font.md; // { size: "1rem", lineHeight: "1.75rem", weight: 500 }
289
- tokens.font.lg; // { size: "1.25rem", lineHeight: "2rem", weight: 500 }
290
- tokens.font.xl; // { size: "1.5rem", lineHeight: "2.125rem", weight: 600 }
291
- tokens.font["2xl"]; // { size: "1.875rem", lineHeight: "2.5rem", weight: 600 }
292
- ```
293
-
294
- #### 2. `tokens.typography.*` - Complete Typography System
295
-
296
- For advanced typography with font families and additional properties like letter spacing:
297
-
298
- ```ts
299
- // Font families
300
- tokens.typography.families.sans; // "'Inter', 'Helvetica Neue', Arial, sans-serif"
301
- tokens.typography.families.serif; // "'Spectre Serif', 'Georgia', serif"
302
- tokens.typography.families.mono; // "'JetBrains Mono', 'SFMono-Regular', Consolas, monospace"
303
-
304
- // Typography scale (includes all font properties plus letter spacing)
305
- tokens.typography.scale.xs; // { fontSize: "0.75rem", lineHeight: "1.25rem", fontWeight: 400, letterSpacing: "0.02em" }
306
- tokens.typography.scale.sm; // { fontSize: "0.875rem", lineHeight: "1.5rem", fontWeight: 400 }
307
- tokens.typography.scale.md; // { fontSize: "1rem", lineHeight: "1.75rem", fontWeight: 500 }
308
- tokens.typography.scale.lg; // { fontSize: "1.25rem", lineHeight: "2rem", fontWeight: 600 }
309
- tokens.typography.scale.xl; // { fontSize: "1.5rem", lineHeight: "2.125rem", fontWeight: 600 }
310
- tokens.typography.scale["2xl"]; // { fontSize: "1.875rem", lineHeight: "2.5rem", fontWeight: 700 }
311
- tokens.typography.scale["3xl"]; // { fontSize: "2.25rem", lineHeight: "2.75rem", fontWeight: 700 }
312
- ```
313
-
314
- **When to use which:**
315
-
316
- - Use `tokens.font.*` for quick, simple typography applications
317
- - Use `tokens.typography.*` when you need font families or letter spacing
318
- - Use `tokens.typography.scale.*` for complete typographic control
319
-
320
- ### Typography CSS Variables
321
-
322
- All typography tokens are available as CSS variables:
323
-
324
- ```css
325
- .heading {
326
- font-family: var(--sp-typography-family-sans);
327
- font-size: var(--sp-font-xl-size);
328
- line-height: var(--sp-font-xl-line-height);
329
- font-weight: var(--sp-font-xl-weight);
330
- }
331
-
332
- .body-text {
333
- font-size: var(--sp-font-md-size);
334
- line-height: var(--sp-font-md-line-height);
335
- font-weight: var(--sp-font-md-weight);
336
- }
337
-
338
- .code-block {
339
- font-family: var(--sp-typography-family-mono);
340
- font-size: var(--sp-font-sm-size);
341
- }
342
- ```
343
-
344
- ### Badge tokens
345
-
346
- Badges have background and text colors for five semantic variants:
347
-
348
- ```ts
349
- // Access via component namespace
350
- tokens.component.badge.neutralBg; // "#f1f5f9" (light) / "#334155" (dark)
351
- tokens.component.badge.neutralText; // "#334155" (light) / "#f1f5f9" (dark)
352
- tokens.component.badge.infoBg; // "#dbeafe" (light) / "#1e40af" (dark)
353
- tokens.component.badge.infoText; // "#1d4ed8" (light) / "#dbeafe" (dark)
354
- tokens.component.badge.successBg; // "#dcfce7" (light) / "#166534" (dark)
355
- tokens.component.badge.successText; // "#15803d" (light) / "#dcfce7" (dark)
356
- tokens.component.badge.warningBg; // "#fef3c7" (light) / "#92400e" (dark)
357
- tokens.component.badge.warningText; // "#b45309" (light) / "#fef3c7" (dark)
358
- tokens.component.badge.dangerBg; // "#fee2e2" (light) / "#991b1b" (dark)
359
- tokens.component.badge.dangerText; // "#b91c1c" (light) / "#fee2e2" (dark)
360
- ```
361
-
362
- **CSS Variables:**
363
-
364
- ```css
365
- .badge {
366
- background: var(--sp-badge-neutral-bg);
367
- color: var(--sp-badge-neutral-text);
368
- padding: 0.25rem 0.5rem;
369
- border-radius: var(--sp-radius-sm);
370
- font-size: var(--sp-font-xs-size);
371
- font-weight: var(--sp-font-xs-weight);
372
- }
373
-
374
- .badge--info {
375
- background: var(--sp-badge-info-bg);
376
- color: var(--sp-badge-info-text);
377
- }
378
-
379
- .badge--success {
380
- background: var(--sp-badge-success-bg);
381
- color: var(--sp-badge-success-text);
382
- }
383
-
384
- .badge--warning {
385
- background: var(--sp-badge-warning-bg);
386
- color: var(--sp-badge-warning-text);
387
- }
388
-
389
- .badge--danger {
390
- background: var(--sp-badge-danger-bg);
391
- color: var(--sp-badge-danger-text);
392
- }
393
- ```
394
-
395
- ### Icon Box tokens
396
-
397
- Icon boxes are decorative containers for icons with semantic color states:
398
-
399
- ```ts
400
- // Background and border
401
- tokens.component.iconBox.bg; // "#ffffff" (light) / "#1e293b" (dark)
402
- tokens.component.iconBox.border; // "#e2e8f0" (light) / "#334155" (dark)
403
-
404
- // Icon colors for different states
405
- tokens.component.iconBox.iconDefault; // "#6c32e6" (light) / "#a37aff" (dark)
406
- tokens.component.iconBox.iconSuccess; // "#16a34a" (light) / "#4ade80" (dark)
407
- tokens.component.iconBox.iconWarning; // "#d97706" (light) / "#fbbf24" (dark)
408
- tokens.component.iconBox.iconDanger; // "#dc2626" (light) / "#ef4444" (dark)
409
- ```
410
-
411
- **CSS Variables:**
412
-
413
- ```css
414
- .icon-box {
415
- background: var(--sp-icon-box-bg);
416
- border: 1px solid var(--sp-icon-box-border);
417
- border-radius: var(--sp-radius-md);
418
- padding: var(--sp-space-12);
419
- }
420
-
421
- .icon-box__icon {
422
- color: var(--sp-icon-box-icon-default);
423
- }
424
-
425
- .icon-box--success .icon-box__icon {
426
- color: var(--sp-icon-box-icon-success);
427
- }
428
-
429
- .icon-box--warning .icon-box__icon {
430
- color: var(--sp-icon-box-icon-warning);
431
- }
432
-
433
- .icon-box--danger .icon-box__icon {
434
- color: var(--sp-icon-box-icon-danger);
71
+ presets: [tailwindPreset]
435
72
  }
436
73
  ```
437
74
 
438
- ### Meta text
75
+ Prefer semantic tokens such as `surface`, `text`, `component`, `buttons`, and
76
+ `forms` for application UI. Raw palette values remain available when fixed color
77
+ access is appropriate.
439
78
 
440
- For secondary/metadata text styling:
79
+ ## What this package owns
441
80
 
442
- ```ts
443
- tokens.text.onPage.meta; // Metadata text on page backgrounds
444
- tokens.text.onSurface.meta; // Metadata text on card/surface backgrounds
445
- tokens.text.onPage.brand; // Brand accent text on page canvas
446
- ```
81
+ - Visual language expressed as token data in `tokens/`
82
+ - Semantic roles and token contracts consumed downstream
83
+ - Generated token outputs for JavaScript, TypeScript, CSS variables, and
84
+ Tailwind theme exports
85
+ - Theme and mode definitions used by downstream consumers
447
86
 
448
- ```css
449
- .timestamp,
450
- .byline {
451
- color: var(--sp-text-on-page-meta);
452
- font-size: var(--sp-font-sm-size);
453
- }
454
- ```
87
+ ### Token model
455
88
 
456
- ### WCAG targets
89
+ The generated token object includes these namespaces:
457
90
 
458
- Spectre encodes accessibility constraints (focus rings, touch targets, and contrast-minded semantic roles) at the token level. Always validate final UI implementations with tools like the WebAIM Contrast Checker.
91
+ - `colors`
92
+ - `space`
93
+ - `layout`
94
+ - `radii`
95
+ - `typography`
96
+ - `font`
97
+ - `shadows`
98
+ - `breakpoints`
99
+ - `zIndex`
100
+ - `transitions`
101
+ - `animations`
102
+ - `opacity`
103
+ - `border`
104
+ - `accessibility`
105
+ - `buttons`
106
+ - `forms`
107
+ - `surface`
108
+ - `text`
109
+ - `component`
110
+ - `modes`
459
111
 
460
- Always re-run final UI implementations through tools like [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/).
112
+ ### Themes and modes
461
113
 
462
- ## Spacing & Layout Tokens
114
+ The package includes mode-aware semantic tokens under `modes`, with `default`
115
+ and `dark` mode definitions in the generated output.
463
116
 
464
- - **8px grid**: `space.*` follows an 8px rhythm with a single 4px micro step for fine-grain alignment. Scale: 0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80, 96 (all in px, emitted as rem).
465
- - **Semantic layout**: `layout.section.padding.{sm,md,lg}` `space.24/32/48`; `layout.section.gap.{sm,md,lg}` `space.16/24/32`; `layout.stack.gap.{sm,md,lg}` → `space.8/12/16`; `layout.container.paddingInline.{sm,md,lg}` → `space.16/24/32`.
466
- - **Outputs**: CSS vars like `--sp-space-24` and `--sp-layout-section-padding-md`; Tailwind `theme.spacing` is sourced from `space.*`.
467
- - **Usage**: Use `layout.*` for consistent gutters/padding rather than ad-hoc numbers. Example: `gap: var(--sp-layout-stack-gap-md);` or `padding-inline: var(--sp-layout-container-padding-inline-lg);`.
468
- - **Responsiveness lives in Spectre UI**: apply breakpoint logic and component behavior in `@phcdevworks/spectre-ui` or consumers; keep tokens meaning-only.
117
+ Raw palette tokens are stable values. Semantic tokens are the preferred
118
+ interface for theme-aware usage because they can map across modes without
119
+ changing consumer code.
469
120
 
470
- ## Surface & Typography Roles
121
+ ## What this package does not own
471
122
 
472
- - `surface.page`, `surface.card`, `surface.input`, `surface.overlay`: semantic backgrounds for the app canvas, containers/tiles, form fields, and modal/dropdown layers.
473
- - `text.onPage.*` vs `text.onSurface.*`: use `onPage` for copy sitting directly on the page canvas; use `onSurface` for text inside cards, tiles, inputs, overlays, and other elevated surfaces.
474
- - `text.onPage.default`, `text.onPage.muted`, `text.onPage.subtle`, `text.onPage.meta`
475
- - `text.onSurface.default`, `text.onSurface.muted`, `text.onSurface.subtle`, `text.onSurface.meta`
476
- - `component.card.text`/`textMuted`, `component.input.text`/`placeholder`, `component.button.textDefault`/`textOnPrimary`, and `component.badge.*` alias the underlying semantic roles to keep component defaults aligned.
123
+ - Component structure or composition That belongs in downstream UI packages such
124
+ as [`@phcdevworks/spectre-ui`](https://github.com/phcdevworks/spectre-ui).
125
+ - Framework-specific delivery Adapter packages translate Spectre contracts for
126
+ specific frameworks and runtimes.
127
+ - Local redefinition of token meaning Downstream consumers should consume these
128
+ contracts rather than recreate them independently.
477
129
 
478
- ## Modes & Theme Switching
130
+ ## Package exports / API surface
479
131
 
480
- Spectre tokens ship with a comprehensive `modes` system for theme support:
132
+ ### Root package
481
133
 
482
- ```ts
483
- tokens.modes.default; // Light theme semantic tokens
484
- tokens.modes.dark; // Dark theme semantic tokens
485
- ```
134
+ `@phcdevworks/spectre-tokens` exports:
486
135
 
487
- ### Mode Structure
136
+ - `default` / `tokens`
137
+ - `tailwindTheme`
138
+ - `tailwindPreset`
139
+ - `generateCssVariables()`
140
+ - TypeScript types including `SpectreTokens`, `TailwindTheme`,
141
+ `SpectreModeTokens`, and `SpectreModeName`
488
142
 
489
- Each mode contains semantic tokens that adapt to the theme:
143
+ Example:
490
144
 
491
145
  ```ts
492
- // Light mode (tokens.modes.default)
493
- {
494
- surface: {
495
- page: { value: "#f8fafc" },
496
- card: { value: "#ffffff" },
497
- input: { value: "#ffffff" },
498
- overlay: { value: "rgba(15,23,42,0.6)" }
499
- },
500
- text: {
501
- onPage: {
502
- default: { value: "#0f172a" },
503
- muted: { value: "#475569" },
504
- subtle: { value: "#94a3b8" },
505
- meta: { value: "#94a3b8" }
506
- },
507
- onSurface: { /* ... */ }
508
- },
509
- component: {
510
- card: { text: { value: "#0f172a" }, textMuted: { value: "#6b7280" } },
511
- input: { text: { value: "#0f172a" }, placeholder: { value: "#94a3b8" } },
512
- button: { textDefault: { value: "#0f172a" }, textOnPrimary: { value: "#ffffff" } },
513
- badge: { /* ... */ },
514
- iconBox: { /* ... */ }
515
- }
516
- }
517
-
518
- // Dark mode (tokens.modes.dark) - same structure with dark-adapted values
519
- ```
520
-
521
- ### Accessing Semantic Tokens
522
-
523
- The TypeScript library flattens mode tokens to the root level for convenient access:
524
-
525
- > Note: The JS `tokens.*` values resolve to the default mode. Theme switching happens in CSS via `:root[data-spectre-theme="dark"]` and semantic CSS variables.
526
-
527
- ```ts
528
- // These are automatically resolved from modes.default
529
- tokens.surface.page; // "#f8fafc" in light mode
530
- tokens.text.onPage.default; // "#0f172a" in light mode
531
- tokens.component.card.text; // "#0f172a" in light mode
532
- ```
533
-
534
- ### CSS Variable Output
535
-
536
- The CSS generator outputs mode-specific variables:
537
-
538
- ```css
539
- /* Default (light) theme */
540
- :root {
541
- --sp-surface-page: #f8fafc;
542
- --sp-surface-card: #ffffff;
543
- --sp-text-on-page-default: #0f172a;
544
- /* ... */
545
- }
546
-
547
- /* Dark theme */
548
- :root[data-spectre-theme="dark"] {
549
- --sp-surface-page: #0f172a;
550
- --sp-surface-card: #1e293b;
551
- --sp-text-on-page-default: #f8fafc;
552
- /* ... */
553
- }
554
- ```
555
-
556
- ### Enabling Dark Mode
557
-
558
- Toggle themes by setting the `data-spectre-theme` attribute:
559
-
560
- ```html
561
- <!-- Light mode (default) -->
562
- <html>
563
- <!-- Your content -->
564
- </html>
565
-
566
- <!-- Dark mode -->
567
- <html data-spectre-theme="dark">
568
- <!-- Your content -->
569
- </html>
570
- ```
571
-
572
- ```js
573
- function setTheme(theme) {
574
- const html = document.documentElement;
575
- if (!theme || theme === "default") html.removeAttribute("data-spectre-theme");
576
- else html.setAttribute("data-spectre-theme", theme);
577
- }
578
-
579
- function toggleTheme() {
580
- const html = document.documentElement;
581
- const currentTheme = html.getAttribute("data-spectre-theme");
582
- setTheme(currentTheme === "dark" ? "default" : "dark");
583
- }
584
-
585
- // Set based on user preference
586
- if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
587
- setTheme("dark");
588
- }
589
- ```
590
-
591
- ### Semantic Token Benefits
592
-
593
- - **Automatic theme adaptation**: All semantic tokens (surface, text, component) automatically adapt when switching themes
594
- - **No manual color management**: Reference semantic tokens instead of raw colors
595
- - **Consistent UX**: Components using semantic tokens maintain proper contrast in both themes
596
- - **Type-safe**: TypeScript definitions ensure you only use valid semantic token paths
597
-
598
- ### Key CSS variables
599
-
600
- These variables are the contract consumed by `@phcdevworks/spectre-ui`; removing or renaming them will break downstream UI packages.
601
-
602
- - **Surface**: `--sp-surface-page`, `--sp-surface-card`, `--sp-surface-input`, `--sp-surface-overlay`
603
- - **Text**: `--sp-text-on-page-default`, `--sp-text-on-page-muted`, `--sp-text-on-page-subtle`, `--sp-text-on-page-meta`, `--sp-text-on-surface-default`, `--sp-text-on-surface-muted`, `--sp-text-on-surface-subtle`, `--sp-text-on-surface-meta`
604
- - **Components**: `--sp-component-card-text`, `--sp-component-card-text-muted`, `--sp-component-input-text`, `--sp-component-input-placeholder`, `--sp-badge-neutral-bg`, `--sp-badge-neutral-text`
605
- - **Buttons**: `--sp-button-primary-bg`, `--sp-button-primary-text`, `--sp-button-secondary-bg`, `--sp-button-secondary-text`, `--sp-button-ghost-bg`, `--sp-button-ghost-text`
606
- - **Typography**: `--sp-font-{xs,sm,md,lg,xl}-size`, `--sp-font-{xs,sm,md,lg,xl}-line-height`, `--sp-font-{xs,sm,md,lg,xl}-weight`
607
-
608
- ## Complete Token Reference
609
-
610
- ### Color Tokens
611
-
612
- ```ts
613
- // Brand colors (purple)
614
- tokens.colors.brand["50"]; // "#f5f0ff"
615
- tokens.colors.brand["500"]; // "#8652ff"
616
- tokens.colors.brand["900"]; // "#241147"
617
-
618
- // Neutral grays
619
- tokens.colors.neutral["50"]; // "#f8fafc"
620
- tokens.colors.neutral["500"]; // "#64748b"
621
- tokens.colors.neutral["900"]; // "#0f172a"
622
-
623
- // Accent (teal/cyan)
624
- tokens.colors.accent["500"]; // "#03e6b3"
625
-
626
- // Semantic colors
627
- tokens.colors.success["500"]; // "#22c55e"
628
- tokens.colors.warning["500"]; // "#f59e0b"
629
- tokens.colors.error["500"]; // "#ef4444"
630
- tokens.colors.info["500"]; // "#3b82f6"
631
-
632
- // Focus colors
633
- tokens.colors.focus.primary; // "#8652ff"
634
- tokens.colors.focus.error; // "#ef4444"
635
- tokens.colors.focus.info; // "#3b82f6"
636
- ```
637
-
638
- ### Spacing Tokens
639
-
640
- ```ts
641
- tokens.space["0"]; // "0rem"
642
- tokens.space["4"]; // "0.25rem" (4px)
643
- tokens.space["8"]; // "0.5rem" (8px)
644
- tokens.space["16"]; // "1rem" (16px)
645
- tokens.space["24"]; // "1.5rem" (24px)
646
- tokens.space["32"]; // "2rem" (32px)
647
- tokens.space["48"]; // "3rem" (48px)
648
- tokens.space["64"]; // "4rem" (64px)
649
- tokens.space["96"]; // "6rem" (96px)
650
- ```
651
-
652
- ### Layout Tokens
653
-
654
- ```ts
655
- // Section padding
656
- tokens.layout.section.padding.sm; // "1.5rem"
657
- tokens.layout.section.padding.md; // "2rem"
658
- tokens.layout.section.padding.lg; // "3rem"
659
-
660
- // Section gaps
661
- tokens.layout.section.gap.sm; // "1rem"
662
- tokens.layout.section.gap.md; // "1.5rem"
663
- tokens.layout.section.gap.lg; // "2rem"
664
-
665
- // Stack gaps (tighter spacing for related elements)
666
- tokens.layout.stack.gap.sm; // "0.5rem"
667
- tokens.layout.stack.gap.md; // "0.75rem"
668
- tokens.layout.stack.gap.lg; // "1rem"
669
-
670
- // Container inline padding
671
- tokens.layout.container.paddingInline.sm; // "1rem"
672
- tokens.layout.container.paddingInline.md; // "1.5rem"
673
- tokens.layout.container.paddingInline.lg; // "2rem"
674
- ```
675
-
676
- ### Border Radius Tokens
677
-
678
- ```ts
679
- tokens.radii.none; // "0"
680
- tokens.radii.sm; // "2px"
681
- tokens.radii.md; // "4px"
682
- tokens.radii.lg; // "8px"
683
- tokens.radii.pill; // "999px"
684
- ```
685
-
686
- ### Shadow Tokens
687
-
688
- ```ts
689
- tokens.shadows.none; // "none"
690
- tokens.shadows.sm; // "0 1px 2px 0 rgba(15, 23, 42, 0.08)"
691
- tokens.shadows.md; // "0 3px 8px -1px rgba(15, 23, 42, 0.1)"
692
- tokens.shadows.lg; // "0 8px 20px -4px rgba(15, 23, 42, 0.18)"
693
- ```
694
-
695
- ### Breakpoint Tokens
696
-
697
- ```ts
698
- tokens.breakpoints.sm; // "640px"
699
- tokens.breakpoints.md; // "768px"
700
- tokens.breakpoints.lg; // "1024px"
701
- tokens.breakpoints.xl; // "1280px"
702
- tokens.breakpoints["2xl"]; // "1536px"
703
- ```
704
-
705
- ### Z-Index Tokens
706
-
707
- ```ts
708
- tokens.zIndex.base; // "0"
709
- tokens.zIndex.dropdown; // "1000"
710
- tokens.zIndex.sticky; // "1100"
711
- tokens.zIndex.fixed; // "1200"
712
- tokens.zIndex.overlay; // "1300"
713
- tokens.zIndex.modal; // "1400"
714
- tokens.zIndex.popover; // "1500"
715
- tokens.zIndex.tooltip; // "1600"
716
- ```
717
-
718
- ### Transition Tokens
719
-
720
- ```ts
721
- // Duration
722
- tokens.transitions.duration.instant; // "75ms"
723
- tokens.transitions.duration.fast; // "150ms"
724
- tokens.transitions.duration.base; // "200ms"
725
- tokens.transitions.duration.moderate; // "300ms"
726
- tokens.transitions.duration.slow; // "500ms"
727
- tokens.transitions.duration.slower; // "700ms"
146
+ import tokens, {
147
+ generateCssVariables,
148
+ tailwindPreset,
149
+ tailwindTheme
150
+ } from '@phcdevworks/spectre-tokens'
728
151
 
729
- // Easing
730
- tokens.transitions.easing.linear; // "linear"
731
- tokens.transitions.easing.in; // "cubic-bezier(0.4, 0, 1, 1)"
732
- tokens.transitions.easing.out; // "cubic-bezier(0, 0, 0.2, 1)"
733
- tokens.transitions.easing.inOut; // "cubic-bezier(0.4, 0, 0.2, 1)"
734
- tokens.transitions.easing.spring; // "cubic-bezier(0.34, 1.56, 0.64, 1)"
152
+ const css = generateCssVariables(tokens, {
153
+ selector: ':root',
154
+ prefix: 'sp'
155
+ })
735
156
  ```
736
157
 
737
- ### Animation Tokens
158
+ ### CSS entry point
738
159
 
739
- ```ts
740
- tokens.animations.fadeIn; // { duration: "200ms", easing: "...", keyframes: "fade-in" }
741
- tokens.animations.fadeOut; // { duration: "150ms", easing: "...", keyframes: "fade-out" }
742
- tokens.animations.slideUp; // { duration: "300ms", easing: "...", keyframes: "slide-up" }
743
- tokens.animations.slideDown; // { duration: "300ms", easing: "...", keyframes: "slide-down" }
744
- tokens.animations.scaleIn; // { duration: "200ms", easing: "...", keyframes: "scale-in" }
745
- tokens.animations.bounce; // { duration: "500ms", easing: "...", keyframes: "bounce" }
746
- tokens.animations.shake; // { duration: "400ms", easing: "...", keyframes: "shake" }
747
- tokens.animations.pulse; // { duration: "1500ms", easing: "...", keyframes: "pulse" }
748
- ```
160
+ - `@phcdevworks/spectre-tokens/index.css`
749
161
 
750
- ### Border Tokens
162
+ ## Relationship to the rest of Spectre
751
163
 
752
- ```ts
753
- tokens.borders.card; // "#334155"
754
- tokens.borders.input; // "#cbd5f5"
755
- ```
164
+ Spectre keeps responsibilities separate:
756
165
 
757
- ## Repository Layout
166
+ - [`@phcdevworks/spectre-tokens`](https://github.com/phcdevworks/spectre-tokens)
167
+ defines visual language, semantic roles, and token contracts
168
+ - [`@phcdevworks/spectre-ui`](https://github.com/phcdevworks/spectre-ui) turns
169
+ those contracts into reusable CSS, Tailwind tooling, and shared styling
170
+ behavior
171
+ - Adapter packages translate Spectre contracts for framework-specific delivery
758
172
 
759
- | Folder | Responsibility |
760
- | ---------- | ------------------------------------------------------------------------------------------------------------- |
761
- | `tokens/` | Raw JSON token files (modularized as `palette.json`, `typography.json`, `components.json`, etc.). |
762
- | `src/` | TypeScript source that turns JSON into reusable formats (JS/TS exports, Tailwind theme, CSS helpers). |
763
- | `scripts/` | Build utilities and strictly-typed token merging engines. |
764
- | `dist/` | Generated artifacts: `index.js`, `index.cjs`, `index.d.ts`, and `index.css`. Regenerated via `npm run build`. |
173
+ That separation keeps token meaning centralized while letting the package system
174
+ expand by responsibility.
765
175
 
766
- Designers only touch the JSON under `tokens/`. Engineering evolves `src/` + `scripts/` when structure or transforms change.
176
+ ## Development
767
177
 
768
- ## Build & Release
178
+ Regenerate package outputs:
769
179
 
770
180
  ```bash
771
181
  npm run build
772
182
  ```
773
183
 
774
- `tsup` compiles the TypeScript library (ESM, CJS, `.d.ts`) and `scripts/build-css.js` emits `dist/index.css`. Because `dist/` is generated, releases are reproducible from `tokens/` + `src/`.
775
-
776
- > Do not hand-edit `dist/`. Always regenerate it via the build.
777
-
778
- For release history and version notes, see the **[Changelog](CHANGELOG.md)**.
779
-
780
- ## Migration & Comparison Guide
781
-
782
- ### Migrating from Other Token Systems
783
-
784
- #### From Style Dictionary / Design Tokens Format
785
-
786
- ```js
787
- // Old: Style Dictionary format
788
- {
789
- "color": {
790
- "brand": {
791
- "primary": { "value": "#8652ff" }
792
- }
793
- }
794
- }
795
-
796
- // New: Spectre Tokens
797
- tokens.colors.brand["500"] // "#8652ff"
798
- ```
799
-
800
- #### From Tailwind Default Theme
801
-
802
- ```js
803
- // Old: Tailwind's theme
804
- colors.purple[500]; // "#a855f7"
805
- spacing[4]; // "1rem"
806
-
807
- // New: Spectre Tokens
808
- tokens.colors.brand["500"]; // "#8652ff" (custom brand purple)
809
- tokens.space["16"]; // "1rem"
810
- ```
811
-
812
- #### From CSS Custom Properties Only
813
-
814
- ```css
815
- /* Old: Manual CSS variables */
816
- :root {
817
- --primary-color: #8652ff;
818
- --spacing-4: 1rem;
819
- }
820
-
821
- /* New: Spectre auto-generated */
822
- :root {
823
- --sp-color-brand-500: #8652ff;
824
- --sp-space-16: 1rem;
825
- /* Plus 300+ more tokens */
826
- }
827
- ```
828
-
829
- ### Key Differences from Other Systems
830
-
831
- | Feature | Spectre Tokens | Tailwind | Material UI | Chakra UI |
832
- | ----------------------- | ------------------------- | ------------------- | ------------------- | ----------------- |
833
- | **Format** | JSON → JS/TS/CSS | JS Config | Theme Object | Theme Object |
834
- | **CSS Variables** | ✅ Auto-generated | ⚠️ Opt-in (v4+) | ✅ Built-in | ✅ Built-in |
835
- | **Dark Mode** | ✅ Via `modes` | ✅ Via classes | ✅ Via palette mode | ✅ Via color mode |
836
- | **Type Safety** | ✅ Full TypeScript | ⚠️ Partial | ✅ Full | ✅ Full |
837
- | **Framework Agnostic** | ✅ Yes | ⚠️ Tailwind-focused | ❌ React-only | ❌ React-only |
838
- | **Semantic Tokens** | ✅ surface/text/component | ❌ Utility-first | ✅ Built-in | ✅ Built-in |
839
- | **Button States** | ✅ CRO-optimized | ❌ DIY | ✅ Built-in | ✅ Built-in |
840
- | **Form States** | ✅ Validation-aware | ❌ DIY | ✅ Built-in | ✅ Built-in |
841
- | **Tailwind Compatible** | ✅ Preset provided | N/A | ⚠️ Via plugin | ⚠️ Via plugin |
842
-
843
- ### Why Choose Spectre Tokens?
844
-
845
- 1. **True Single Source of Truth**: JSON tokens drive everything - JS, CSS, Tailwind, documentation
846
- 2. **CRO-First Design**: Button and form states optimized for conversion and UX
847
- 3. **Framework Agnostic**: Use with React, Vue, Svelte, WordPress, Astro, 11ty, or vanilla JS
848
- 4. **Semantic + Utility**: Get both semantic tokens (surface, text) and utility tokens (colors, space)
849
- 5. **WCAG Built-In**: Accessibility constraints enforced at the token level
850
- 6. **No Lock-In**: Export to CSS variables and use anywhere, or integrate deeply with Tailwind
851
- 7. **Type-Safe**: Complete TypeScript definitions with autocomplete
852
-
853
- ## Spectre Design Philosophy
854
-
855
- Spectre is a **specification-driven design system** built on three strict layers:
856
-
857
- ### 1. @phcdevworks/spectre-tokens (Foundation)
858
-
859
- **Purpose**: Single source of truth for design values (colors, surfaces, text roles, space, radii, shadows, etc.)
860
-
861
- **Exports**: CSS variables (`--sp-*`), TypeScript token object, Tailwind-compatible theme mappings
862
-
863
- **Rules**:
864
-
865
- - Tokens define semantic meaning, not UI behavior
866
- - UI must never invent new colors or values
867
- - Designers own `tokens/*.json`; engineers maintain `src/` transforms
868
- - Contrast targets and accessibility constraints are encoded at the token level
869
-
870
- **Status**: v2.0.0 released. Transitioned to modular JSON storage and full AI-readiness.
871
-
872
- ### 2. @phcdevworks/spectre-ui (Framework-Agnostic UI Layer)
873
-
874
- **Purpose**: Converts tokens into real CSS and class recipes
875
-
876
- **Ships**:
877
-
878
- - `index.css` (canonical CSS bundle: tokens + base + components + utilities)
879
- - `base.css` (resets + globals)
880
- - `components.css` (`.sp-btn`, `.sp-card`, `.sp-input`, etc.)
881
- - `utilities.css` (`.sp-stack`, `.sp-container`, etc.)
882
- - Type-safe recipes: `getButtonClasses`, `getCardClasses`, `getInputClasses`
883
-
884
- **Rules**:
885
-
886
- - UI must consume tokens, not redefine design values
887
- - Literal values in CSS are fallbacks only
888
- - Every CSS selector has a matching recipe where applicable
889
- - Tailwind preset is optional and non-authoritative
890
-
891
- **Status**: v1.0.0 hardened and aligned to tokens. v2.0.0 compatibility ready.
892
-
893
- ### 3. Adapters (WordPress, Astro, etc.)
894
-
895
- **Purpose**: Thin framework wrappers around spectre-ui; automatically sync and load the Spectre UI CSS bundle
896
-
897
- **Rules**:
898
-
899
- - Adapters never define styles, never duplicate CSS, never load tokens directly
900
- - All design values come from tokens, all CSS comes from spectre-ui
901
- - Adapters only translate and integrate
902
-
903
- ### Golden Rule (Non-Negotiable)
904
-
905
- **Tokens define meaning. UI defines structure. Adapters only translate.**
906
-
907
- - If it's a design token → belongs in `@phcdevworks/spectre-tokens`
908
- - If it's a CSS class or style → belongs in `@phcdevworks/spectre-ui`
909
- - If it's framework integration → belongs in an adapter
910
-
911
- ## Design Principles
912
-
913
- 1. **Single source of truth** – Tokens originate in JSON and flow into every runtime surface.
914
- 2. **Format-agnostic** – Consumers choose JS objects, CSS variables, Tailwind presets, or generated strings.
915
- 3. **Framework-neutral** – Works in WordPress, Astro, 11ty, React, or any environment that can ingest CSS/JS.
916
- 4. **Accessibility-first** – Focus rings, touch targets, and contrast targets are encoded directly in tokens.
917
- 5. **CRO-aware** – Buttons, forms, and state tokens are tuned for real-world conversion funnels.
918
-
919
- ## TypeScript Support
920
-
921
- Type definitions are bundled automatically with comprehensive interfaces for all token types:
922
-
923
- ```ts
924
- import type {
925
- Tokens,
926
- SpectreTokens,
927
- TailwindTheme,
928
- ColorScale,
929
- TokenScale,
930
- ButtonStateTokens,
931
- FormStateTokens,
932
- TypographyTokens,
933
- TypographyScaleEntry,
934
- FontScaleEntry,
935
- TransitionTokens,
936
- AnimationEntry,
937
- AccessibilityTokens,
938
- ComponentTokens,
939
- ComponentBadgeTokens,
940
- ComponentIconBoxTokens,
941
- LayoutTokens,
942
- SpectreModeTokens,
943
- SpectreModeName,
944
- SemanticTokenValue,
945
- CssVariableOptions,
946
- CssVariableMap,
947
- } from "@phcdevworks/spectre-tokens";
948
-
949
- const allTokens: SpectreTokens = tokens;
950
- ```
951
-
952
- ### Key Type Interfaces
953
-
954
- **Core Token Types:**
955
-
956
- ```ts
957
- // Color and token scales
958
- type ColorScale = Record<string, string>;
959
- type TokenScale = Record<string, string>;
960
-
961
- // Typography
962
- interface TypographyScaleEntry {
963
- fontSize: string;
964
- lineHeight: string;
965
- fontWeight?: number;
966
- letterSpacing?: string;
967
- }
968
-
969
- interface FontScaleEntry {
970
- size: string;
971
- lineHeight: string;
972
- weight: number;
973
- }
974
-
975
- // Component states
976
- interface ButtonStateTokens {
977
- bg: string;
978
- bgHover: string;
979
- bgActive: string;
980
- bgDisabled: string;
981
- text: string;
982
- textDisabled: string;
983
- border?: string;
984
- borderDisabled?: string;
985
- }
986
-
987
- interface FormStateTokens {
988
- bg?: string;
989
- border: string;
990
- text?: string;
991
- placeholder?: string;
992
- ring?: string;
993
- }
994
-
995
- // Component tokens
996
- interface ComponentBadgeTokens<Value = string> {
997
- neutralBg: Value;
998
- neutralText: Value;
999
- infoBg: Value;
1000
- infoText: Value;
1001
- successBg: Value;
1002
- successText: Value;
1003
- warningBg: Value;
1004
- warningText: Value;
1005
- dangerBg: Value;
1006
- dangerText: Value;
1007
- }
1008
-
1009
- interface ComponentIconBoxTokens<Value = string> {
1010
- bg: Value;
1011
- border: Value;
1012
- iconDefault: Value;
1013
- iconSuccess: Value;
1014
- iconWarning: Value;
1015
- iconDanger: Value;
1016
- }
1017
- ```
1018
-
1019
- **Semantic Token Types:**
1020
-
1021
- ```ts
1022
- // Semantic token values can be strings or objects with metadata
1023
- type SemanticTokenValue = string | { value: string; [key: string]: any };
1024
-
1025
- // Mode names
1026
- type SpectreModeName = "default" | "dark";
1027
-
1028
- // Mode structure
1029
- interface SpectreModeTokens {
1030
- surface: {
1031
- page: SemanticTokenValue;
1032
- card: SemanticTokenValue;
1033
- input: SemanticTokenValue;
1034
- overlay: SemanticTokenValue;
1035
- };
1036
- text: {
1037
- onPage: {
1038
- default: SemanticTokenValue;
1039
- muted: SemanticTokenValue;
1040
- subtle: SemanticTokenValue;
1041
- meta: SemanticTokenValue;
1042
- };
1043
- onSurface: {
1044
- /* same as onPage */
1045
- };
1046
- };
1047
- component: ComponentTokens<SemanticTokenValue>;
1048
- }
1049
- ```
1050
-
1051
- **CSS Variable Generation:**
1052
-
1053
- ```ts
1054
- interface CssVariableOptions {
1055
- prefix?: string; // Default: "sp"
1056
- selector?: string; // Default: ":root"
1057
- }
1058
-
1059
- type CssVariableMap = Record<string, string>;
1060
-
1061
- // Usage
1062
- const css: string = generateCssVariables(tokens, {
1063
- prefix: "spectre",
1064
- selector: ".my-theme",
1065
- });
1066
- ```
1067
-
1068
- ### Type-Safe Token Access
1069
-
1070
- TypeScript provides full autocomplete and type checking:
1071
-
1072
- ```ts
1073
- import tokens from "@phcdevworks/spectre-tokens";
1074
-
1075
- // Autocomplete for all color palettes and shades
1076
- const brandColor = tokens.colors.brand["500"]; // ✅ Type: string
1077
-
1078
- // Autocomplete for button states
1079
- const primaryBg = tokens.buttons.primary.bg; // ✅ Type: string
1080
- const primaryHover = tokens.buttons.primary.bgHover; // ✅ Type: string
1081
-
1082
- // Type checking prevents invalid access
1083
- // tokens.colors.invalid["500"]; // ❌ TypeScript error
1084
- // tokens.buttons.primary.invalidProp; // ❌ TypeScript error
1085
-
1086
- // Full type support for component tokens
1087
- const badgeBg: string = tokens.component.badge.successBg;
1088
- const iconColor: string = tokens.component.iconBox.iconDanger;
1089
- ```
1090
-
1091
- ## Part of the Spectre Suite
1092
-
1093
- - **Spectre Tokens** – Design-token foundation (this package)
1094
- - **Spectre UI** – Core styling layer
1095
- - **Spectre Blocks** – WordPress block library
1096
- - **Spectre Astro** – Astro integration
1097
- - **Spectre 11ty** – Eleventy integration
1098
-
1099
- ## Practical Examples
1100
-
1101
- ### Building a Button Component
1102
-
1103
- ```ts
1104
- import tokens from "@phcdevworks/spectre-tokens";
1105
-
1106
- // Create a type-safe button configuration
1107
- const buttonStyles = {
1108
- primary: {
1109
- background: tokens.buttons.primary.bg,
1110
- color: tokens.buttons.primary.text,
1111
- borderRadius: tokens.radii.md,
1112
- padding: `${tokens.space["12"]} ${tokens.space["24"]}`,
1113
- fontSize: tokens.font.md.size,
1114
- fontWeight: tokens.font.md.weight,
1115
- transition: `all ${tokens.transitions.duration.fast} ${tokens.transitions.easing.out}`,
1116
- "&:hover": {
1117
- background: tokens.buttons.primary.bgHover,
1118
- },
1119
- "&:active": {
1120
- background: tokens.buttons.primary.bgActive,
1121
- },
1122
- "&:disabled": {
1123
- background: tokens.buttons.primary.bgDisabled,
1124
- color: tokens.buttons.primary.textDisabled,
1125
- },
1126
- },
1127
- };
1128
- ```
1129
-
1130
- ### Creating a Card Layout
1131
-
1132
- ```css
1133
- .card {
1134
- background: var(--sp-surface-card);
1135
- border: 1px solid var(--sp-borders-card);
1136
- border-radius: var(--sp-radius-lg);
1137
- padding: var(--sp-space-24);
1138
- box-shadow: var(--sp-shadow-md);
1139
-
1140
- /* Semantic layout spacing */
1141
- display: flex;
1142
- flex-direction: column;
1143
- gap: var(--sp-layout-stack-gap-md);
1144
- }
1145
-
1146
- .card__title {
1147
- color: var(--sp-text-on-surface-default);
1148
- font-size: var(--sp-font-lg-size);
1149
- line-height: var(--sp-font-lg-line-height);
1150
- font-weight: var(--sp-font-lg-weight);
1151
- }
1152
-
1153
- .card__description {
1154
- color: var(--sp-text-on-surface-muted);
1155
- font-size: var(--sp-font-md-size);
1156
- line-height: var(--sp-font-md-line-height);
1157
- }
1158
-
1159
- .card__meta {
1160
- color: var(--sp-text-on-surface-meta);
1161
- font-size: var(--sp-font-sm-size);
1162
- }
1163
- ```
1164
-
1165
- ### Form with Validation States
1166
-
1167
- ```html
1168
- <!-- HTML structure -->
1169
- <div class="form-field">
1170
- <label class="form-label">Email Address</label>
1171
- <input type="email" class="form-input" />
1172
- <span class="form-hint">We'll never share your email.</span>
1173
- </div>
1174
-
1175
- <div class="form-field form-field--error">
1176
- <label class="form-label">Password</label>
1177
- <input type="password" class="form-input" />
1178
- <span class="form-error">Password must be at least 8 characters.</span>
1179
- </div>
1180
- ```
1181
-
1182
- ```css
1183
- /* CSS using Spectre tokens */
1184
- .form-field {
1185
- display: flex;
1186
- flex-direction: column;
1187
- gap: var(--sp-layout-stack-gap-sm);
1188
- }
1189
-
1190
- .form-label {
1191
- color: var(--sp-text-on-page-default);
1192
- font-size: var(--sp-font-sm-size);
1193
- font-weight: var(--sp-font-md-weight);
1194
- }
1195
-
1196
- .form-input {
1197
- background: var(--sp-form-default-bg);
1198
- border: 1px solid var(--sp-form-default-border);
1199
- border-radius: var(--sp-radius-md);
1200
- padding: var(--sp-space-12) var(--sp-space-16);
1201
- font-size: var(--sp-font-md-size);
1202
- color: var(--sp-form-default-text);
1203
- transition: border-color var(--sp-transition-duration-fast)
1204
- var(--sp-transition-easing-out);
1205
- min-height: var(--sp-accessibility-min-touch-target);
1206
- }
1207
-
1208
- .form-input::placeholder {
1209
- color: var(--sp-form-default-placeholder);
1210
- }
1211
-
1212
- .form-input:hover {
1213
- border-color: var(--sp-form-hover-border);
1214
- }
1215
-
1216
- .form-input:focus {
1217
- outline: none;
1218
- border-color: var(--sp-form-focus-border);
1219
- box-shadow: 0 0 0 var(--sp-accessibility-focus-ring-width)
1220
- var(--sp-form-focus-ring);
1221
- }
1222
-
1223
- .form-field--error .form-input {
1224
- border-color: var(--sp-form-invalid-border);
1225
- background: var(--sp-form-invalid-bg);
1226
- }
1227
-
1228
- .form-hint {
1229
- color: var(--sp-text-on-page-subtle);
1230
- font-size: var(--sp-font-xs-size);
1231
- }
1232
-
1233
- .form-error {
1234
- color: var(--sp-form-invalid-text);
1235
- font-size: var(--sp-font-xs-size);
1236
- }
1237
- ```
1238
-
1239
- ### Responsive Layout with Breakpoints
1240
-
1241
- ```ts
1242
- import { tailwindTheme } from "@phcdevworks/spectre-tokens";
1243
-
1244
- // Use in CSS-in-JS or styled-components
1245
- const Container = styled.div`
1246
- padding-inline: ${tokens.layout.container.paddingInline.sm};
1247
-
1248
- @media (min-width: ${tokens.breakpoints.md}) {
1249
- padding-inline: ${tokens.layout.container.paddingInline.md};
1250
- }
1251
-
1252
- @media (min-width: ${tokens.breakpoints.lg}) {
1253
- padding-inline: ${tokens.layout.container.paddingInline.lg};
1254
- }
1255
- `;
1256
- ```
1257
-
1258
- ### Animated Modal
1259
-
1260
- ```css
1261
- .modal {
1262
- position: fixed;
1263
- inset: 0;
1264
- z-index: var(--sp-z-index-modal);
1265
- display: flex;
1266
- align-items: center;
1267
- justify-content: center;
1268
- background: var(--sp-surface-overlay);
1269
- animation: fade-in var(--sp-animation-fade-in-duration)
1270
- var(--sp-animation-fade-in-easing);
1271
- }
1272
-
1273
- .modal__content {
1274
- background: var(--sp-surface-card);
1275
- border-radius: var(--sp-radius-lg);
1276
- padding: var(--sp-space-32);
1277
- box-shadow: var(--sp-shadow-lg);
1278
- max-width: 32rem;
1279
- animation: scale-in var(--sp-animation-scale-in-duration)
1280
- var(--sp-animation-scale-in-easing);
1281
- }
1282
-
1283
- @keyframes fade-in {
1284
- from {
1285
- opacity: 0;
1286
- }
1287
- to {
1288
- opacity: 1;
1289
- }
1290
- }
1291
-
1292
- @keyframes scale-in {
1293
- from {
1294
- opacity: 0;
1295
- transform: scale(0.95);
1296
- }
1297
- to {
1298
- opacity: 1;
1299
- transform: scale(1);
1300
- }
1301
- }
1302
- ```
1303
-
1304
- ### Badge Component System
1305
-
1306
- ```tsx
1307
- // React/TypeScript example
1308
- import tokens from "@phcdevworks/spectre-tokens";
1309
-
1310
- type BadgeVariant = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
1311
-
1312
- interface BadgeProps {
1313
- variant?: BadgeVariant;
1314
- children: React.ReactNode;
1315
- }
1316
-
1317
- const Badge: React.FC<BadgeProps> = ({ variant = 'neutral', children }) => {
1318
- const styles = {
1319
- background: tokens.component.badge[`${variant}Bg` as keyof typeof tokens.component.badge],
1320
- color: tokens.component.badge[`${variant}Text` as keyof typeof tokens.component.badge],
1321
- padding: `${tokens.space["4"]} ${tokens.space["8"]}`,
1322
- borderRadius: tokens.radii.sm,
1323
- fontSize: tokens.font.xs.size,
1324
- fontWeight: tokens.font.xs.weight,
1325
- display: 'inline-flex',
1326
- alignItems: 'center',
1327
- };
1328
-
1329
- return <span style={styles}>{children}</span>;
1330
- };
1331
-
1332
- // Usage
1333
- <Badge variant="success">Active</Badge>
1334
- <Badge variant="warning">Pending</Badge>
1335
- <Badge variant="danger">Error</Badge>
1336
- ```
1337
-
1338
- ### Custom Theme Scope
1339
-
1340
- ```ts
1341
- import { generateCssVariables } from "@phcdevworks/spectre-tokens";
184
+ Run the full validation and release gate:
1342
185
 
1343
- // Generate scoped CSS variables for a specific component
1344
- const customThemeCSS = generateCssVariables(tokens, {
1345
- selector: ".custom-widget",
1346
- prefix: "widget",
1347
- });
1348
-
1349
- // Output: Variables scoped to .custom-widget with --widget-* prefix
1350
- // .custom-widget {
1351
- // --widget-color-brand-500: #8652ff;
1352
- // --widget-space-16: 1rem;
1353
- // ...
1354
- // }
1355
- ```
1356
-
1357
- ## Troubleshooting & FAQ
1358
-
1359
- ### CSS Variables Not Working
1360
-
1361
- **Problem:** CSS variables like `var(--sp-color-brand-500)` are not applying.
1362
-
1363
- **Solution:**
1364
-
1365
- 1. Ensure you've imported the CSS file:
1366
- ```css
1367
- @import "@phcdevworks/spectre-tokens/dist/index.css";
1368
- ```
1369
- 2. Check that the file is being served correctly from `node_modules/`
1370
- 3. Verify CSS import order - token CSS should come before your custom styles
1371
-
1372
- ### TypeScript Types Not Found
1373
-
1374
- **Problem:** TypeScript can't find type definitions.
1375
-
1376
- **Solution:**
1377
-
1378
- 1. Check that `@phcdevworks/spectre-tokens` is in your dependencies (not devDependencies)
1379
- 2. Verify `dist/index.d.ts` exists in your `node_modules`
1380
- 3. Try clearing TypeScript cache: `rm -rf node_modules/.cache`
1381
- 4. Restart your TypeScript server/IDE
1382
-
1383
- ### Dark Mode Not Switching
1384
-
1385
- **Problem:** Setting `data-spectre-theme="dark"` doesn't change colors.
1386
-
1387
- **Solution:**
1388
-
1389
- 1. Apply the attribute to the `:root` or `<html>` element:
1390
- ```js
1391
- document.documentElement.setAttribute("data-spectre-theme", "dark");
1392
- ```
1393
- 2. Ensure you're using semantic tokens (surface, text, component) not raw colors
1394
- 3. Raw color tokens (e.g., `tokens.colors.brand[500]`) don't change with themes - use semantic tokens instead
1395
-
1396
- ### Tailwind Integration Issues
1397
-
1398
- **Problem:** Tailwind utilities not using Spectre tokens.
1399
-
1400
- **Solution:**
1401
-
1402
- 1. Verify preset is added to your config:
1403
-
1404
- ```ts
1405
- import { tailwindPreset } from "@phcdevworks/spectre-tokens";
1406
-
1407
- export default {
1408
- presets: [tailwindPreset], // Must be in presets, not theme.extend
1409
- };
1410
- ```
1411
-
1412
- 2. Clear Tailwind cache: `rm -rf .next/cache` or `rm -rf node_modules/.cache`
1413
- 3. Rebuild: `npm run build`
1414
-
1415
- ### Difference Between `font` and `typography`?
1416
-
1417
- **Question:** When should I use `tokens.font.*` vs `tokens.typography.*`?
1418
-
1419
- **Answer:**
1420
-
1421
- - **Use `tokens.font.*`** for quick access to size, line-height, and weight:
1422
- ```ts
1423
- tokens.font.md.size; // "1rem"
1424
- tokens.font.md.lineHeight; // "1.75rem"
1425
- tokens.font.md.weight; // 500
1426
- ```
1427
- - **Use `tokens.typography.*`** when you need:
1428
- - Font families: `tokens.typography.families.sans`
1429
- - Letter spacing: `tokens.typography.scale.xs.letterSpacing`
1430
- - Complete typographic object: `tokens.typography.scale.md`
1431
-
1432
- ### How to Create Custom Modes?
1433
-
1434
- **Question:** Can I add my own theme modes beyond default/dark?
1435
-
1436
- **Answer:**
1437
- Yes! Extend the `modes` object in your `tokens/modes.json`:
1438
-
1439
- ```json
1440
- {
1441
- "modes": {
1442
- "default": {
1443
- /* ... */
1444
- },
1445
- "dark": {
1446
- /* ... */
1447
- },
1448
- "highContrast": {
1449
- "surface": {
1450
- "page": { "value": "#ffffff" }
1451
- }
1452
- }
1453
- }
1454
- }
1455
- ```
1456
-
1457
- Then generate CSS with:
1458
-
1459
- ```css
1460
- :root[data-spectre-theme="highContrast"] {
1461
- /* Custom mode variables */
1462
- }
186
+ ```bash
187
+ npm run check
1463
188
  ```
1464
189
 
1465
- ### Can I Override Specific Tokens?
1466
-
1467
- **Question:** How do I override just a few tokens without forking?
1468
-
1469
- **Answer:**
1470
- Two approaches:
1471
-
1472
- 1. **CSS Override** (simplest):
1473
-
1474
- ```css
1475
- :root {
1476
- --sp-color-brand-500: #your-color !important;
1477
- }
1478
- ```
1479
-
1480
- 2. **JavaScript Override**:
1481
-
1482
- ```ts
1483
- import tokens from "@phcdevworks/spectre-tokens";
190
+ Key source areas:
1484
191
 
1485
- const customTokens = {
1486
- ...tokens,
1487
- colors: {
1488
- ...tokens.colors,
1489
- brand: {
1490
- ...tokens.colors.brand,
1491
- "500": "#your-color",
1492
- },
1493
- },
1494
- };
1495
- ```
192
+ - `tokens/` for source token data
193
+ - `src/generated/` for generated token output
194
+ - `src/` for package entry points, CSS generation, and types
195
+ - `scripts/` for build and validation scripts
196
+ - `example/` for usage examples
1496
197
 
1497
- ### Accessing Nested Token Values
1498
-
1499
- **Question:** Some tokens return `{ value: "..." }` instead of strings. Why?
1500
-
1501
- **Answer:**
1502
- Semantic tokens in `modes` use the `{ value: "..." }` format for metadata. The TypeScript library automatically resolves these:
1503
-
1504
- ```ts
1505
- // In tokens/*.json: { "value": "#f8fafc" }
1506
- tokens.surface.page; // Returns "#f8fafc" (string)
1507
-
1508
- // The library handles resolution automatically
1509
- ```
1510
-
1511
- If you're working with raw JSON, extract the value:
1512
-
1513
- ```ts
1514
- const rawValue = tokens.modes.default.surface.page.value;
1515
- ```
198
+ The files in `example/` are illustrative token demos only. They help explain
199
+ the token contract, but they are not the package contract itself and should not
200
+ be treated as downstream UI primitives.
1516
201
 
1517
202
  ## Contributing
1518
203
 
1519
- Issues and pull requests are welcome. If you are proposing token changes, update `tokens/` and include regenerated builds.
1520
-
1521
- For detailed contribution guidelines, see **[CONTRIBUTING.md](CONTRIBUTING.md)**.
1522
-
1523
- ---
1524
-
1525
- ## 🏛️ The Spectre Suite Hierarchy
1526
-
1527
- Spectre is built on a non-negotiable hierarchy to prevent style leakage and duplication:
204
+ PHCDevworks maintains this package as part of the Spectre system.
1528
205
 
1529
- 1. **Layer 1: DNA (This Package)** – The source of truth for all design values.
1530
- 2. **Layer 2: Blueprint (UI)** ([@phcdevworks/spectre-ui](https://github.com/phcdevworks/spectre-ui)) – Translates tokens into CSS structure and recipes.
1531
- 3. **Layer 3: Adapters** (WordPress, Astro, etc.) – Thin bridges that map Layer 2 to specific frameworks.
206
+ When contributing:
1532
207
 
1533
- > **The Golden Rule**: Tokens define *meaning*. UI defines *structure*. Adapters define *delivery*.
208
+ - treat `tokens/` as the source of truth
209
+ - keep generated outputs derived from source data
210
+ - avoid breaking token contracts without an intentional major-version change
211
+ - run `npm run build` to regenerate outputs when sources change
212
+ - run `npm run check` as the full validation gate before opening a pull request
213
+ - do not modify locked semantic color families without explicit approval
1534
214
 
1535
- ---
215
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full workflow.
1536
216
 
217
+ ## License
1537
218
 
219
+ MIT © PHCDevworks. See [LICENSE](LICENSE).