@phcdevworks/spectre-tokens 2.0.0 → 2.1.1

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