@phcdevworks/spectre-ui 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Framework-agnostic styling layer that powers Spectre Blocks, Spectre Astro, Spectre 11ty, and every Spectre integration.
4
4
 
5
- > 📋 **[View Roadmap](https://github.com/phcdevworks/spectre-ui/blob/main/ROADMAP.md)** | 🤝 **[Contributing Guide](CONTRIBUTING.md)** | 📝 **[Changelog](CHANGELOG.md)**
5
+ > 🤝 **[Contributing Guide](CONTRIBUTING.md)** | 📝 **[Changelog](CHANGELOG.md)**
6
6
 
7
7
  ## Overview
8
8
 
@@ -24,7 +24,16 @@ npm install @phcdevworks/spectre-ui
24
24
 
25
25
  ### 1. Import Spectre CSS
26
26
 
27
- Import the CSS bundles anywhere in your app, layout, or build pipeline.
27
+ Import the canonical bundle anywhere in your app, layout, or build pipeline.
28
+
29
+ ```ts
30
+ // Recommended: one-line Spectre UI bundle (tokens + base + components + utilities)
31
+ import "@phcdevworks/spectre-ui/index.css";
32
+ ```
33
+
34
+ `index.css` automatically loads the Spectre Tokens CSS, so you don't need to import `@phcdevworks/spectre-tokens` separately in most apps.
35
+
36
+ **Advanced layer control:** If you prefer to manage the layers individually, you can still import each file directly.
28
37
 
29
38
  ```css
30
39
  @import "@phcdevworks/spectre-ui/base.css";
@@ -48,6 +57,31 @@ export default {
48
57
 
49
58
  Works with Tailwind 3.x and 4.x through the classic config API.
50
59
 
60
+ Need a plain theme object without presets? Generate it from the first-party tokens.
61
+
62
+ ```ts
63
+ // tailwind.config.ts
64
+ import {
65
+ createSpectreTailwindTheme,
66
+ spectreTokens,
67
+ } from "@phcdevworks/spectre-ui";
68
+
69
+ const { theme } = createSpectreTailwindTheme({
70
+ tokens: spectreTokens,
71
+ overrides: {
72
+ spacing: {
73
+ ...spectreTokens.spacing,
74
+ gutter: "1.125rem",
75
+ },
76
+ },
77
+ });
78
+
79
+ export default {
80
+ content: ["./src/**/*.{js,ts,jsx,tsx,astro}"],
81
+ theme,
82
+ };
83
+ ```
84
+
51
85
  ### 3. Use Spectre recipes
52
86
 
53
87
  Recipes wrap Spectre's class combinations so every framework composes styles consistently.
@@ -86,9 +120,10 @@ getButtonClasses({ variant: "primary" }); // CTA baseline
86
120
  getButtonClasses({ variant: "secondary" }); // Outlined
87
121
  getButtonClasses({ variant: "ghost" }); // Low-emphasis
88
122
  getButtonClasses({ variant: "danger" }); // Destructive
123
+ getButtonClasses({ variant: "success" }); // Positive actions
89
124
  ```
90
125
 
91
- Each variant ships with full state coverage: `default`, `hover`, `active`, `disabled`, and tone modifiers (`success`, `warning`, `danger`).
126
+ Each variant ships with full state coverage: `default`, `hover`, `active`, `disabled`.
92
127
 
93
128
  ```css
94
129
  .cta-button {
@@ -111,8 +146,7 @@ getInputClasses({ state: "success" });
111
146
  ```css
112
147
  .input:focus {
113
148
  border-color: var(--sp-component-input-border-focus);
114
- outline: var(--sp-focus-ring-width) var(--sp-focus-ring-style)
115
- var(--sp-component-input-ring-focus);
149
+ outline: var(--sp-focus-ring-width) var(--sp-focus-ring-style) var(--sp-component-input-ring-focus);
116
150
  }
117
151
  .input.error {
118
152
  border-color: var(--sp-component-input-border-error);
@@ -208,10 +242,10 @@ import {
208
242
 
209
243
  ## Repository Layout
210
244
 
211
- | Folder | Responsibility |
212
- | ------------- | ------------------------------------------------------------------------------------------------------------- |
213
- | `src/` | TypeScript source: recipes, Tailwind preset, token re-exports, CSS constants. |
214
- | `src/styles/` | Raw CSS files (`base.css`, `components.css`, `utilities.css`) copied to `dist/` during build. |
245
+ | Folder | Responsibility |
246
+ | ------------- | ----------------------------------------------------------------------------------------------------------- |
247
+ | `src/` | TypeScript source: recipes, Tailwind preset, token re-exports, CSS constants. |
248
+ | `src/styles/` | Raw CSS files (`base.css`, `components.css`, `utilities.css`) copied to `dist/` during build. |
215
249
  | `dist/` | Generated artifacts: `index.js`, `index.cjs`, `index.d.ts`, and CSS files. Regenerated via `npm run build`. |
216
250
 
217
251
  Designers update tokens in `@phcdevworks/spectre-tokens`. Engineering evolves recipes, presets, and CSS in this package.
@@ -243,7 +277,9 @@ import type {
243
277
  SpectreTokens,
244
278
  SpectreTailwindTheme,
245
279
  ButtonVariant,
280
+ ButtonSize,
246
281
  InputState,
282
+ InputSize,
247
283
  CardVariant,
248
284
  ButtonRecipeOptions,
249
285
  CardRecipeOptions,
@@ -259,8 +295,6 @@ import type {
259
295
  - **Spectre Astro** – Astro integration
260
296
  - **Spectre 11ty** – Eleventy integration
261
297
 
262
- For the project's future direction, see the **[Roadmap](https://github.com/phcdevworks/spectre-ui/blob/main/ROADMAP.md)**.
263
-
264
298
  ## Contributing
265
299
 
266
300
  Issues and pull requests are welcome. If you are proposing style or recipe changes, update `src/` and include regenerated builds.
package/dist/base.css CHANGED
@@ -1,12 +1,5 @@
1
1
  @layer base {
2
2
 
3
- :root {
4
- --sp-surface-page: var(--sp-color-neutral-50, #f8fafc);
5
- --sp-surface-card: #ffffff;
6
- --sp-surface-input: var(--sp-form-default-bg, #ffffff);
7
- --sp-surface-overlay: rgba(15, 23, 42, var(--sp-opacity-overlay, 0.5));
8
- }
9
-
10
3
  *,
11
4
  *::before,
12
5
  *::after {
@@ -52,7 +45,7 @@
52
45
  }
53
46
 
54
47
  a {
55
- color: var(--sp-color-primary, inherit);
48
+ color: var(--sp-color-brand-600, inherit);
56
49
  text-decoration: none;
57
50
  }
58
51
 
@@ -61,12 +54,13 @@
61
54
  }
62
55
 
63
56
  :focus-visible {
64
- outline: 2px solid var(--sp-color-primary, currentColor);
65
- outline-offset: 2px;
57
+ outline: var(--sp-focus-ring-width, 2px) solid var(--sp-color-focus-primary,
58
+ var(--sp-color-brand-600, currentColor));
59
+ outline-offset: var(--sp-focus-ring-offset, 2px);
66
60
  }
67
61
 
68
62
  ::selection {
69
- background-color: var(--sp-color-primary-soft, rgba(79, 70, 229, 0.15));
63
+ background-color: var(--sp-color-brand-100, #ebe2ff);
70
64
  color: inherit;
71
65
  }
72
66
  }
@@ -54,21 +54,36 @@
54
54
  --sp-component-card-ghost-border: var(--sp-component-card-ghost-bg);
55
55
 
56
56
  /* input roles */
57
- --sp-component-input-border: var(--sp-form-default-border);
58
- --sp-component-input-bg: var(--sp-surface-input, var(--sp-form-default-bg));
59
- --sp-component-input-text: var(--sp-text-on-surface-default, var(--sp-form-default-text));
60
- --sp-component-input-placeholder: var(--sp-text-on-surface-muted, var(--sp-form-default-placeholder));
61
- --sp-component-input-border-focus: var(--sp-form-focus-border);
62
- --sp-component-input-ring: var(--sp-form-focus-ring, var(--sp-color-focus-primary));
63
- --sp-component-input-border-invalid: var(--sp-form-invalid-border);
64
- --sp-component-input-bg-invalid: var(--sp-form-invalid-bg);
65
- --sp-component-input-text-invalid: var(--sp-form-invalid-text);
66
- --sp-component-input-border-valid: var(--sp-form-valid-border);
67
- --sp-component-input-bg-valid: var(--sp-form-valid-bg);
68
- --sp-component-input-text-valid: var(--sp-form-valid-text);
69
- --sp-component-input-bg-disabled: var(--sp-form-disabled-bg);
70
- --sp-component-input-text-disabled: var(--sp-form-disabled-text);
71
- --sp-component-input-border-disabled: var(--sp-form-disabled-border);
57
+ --sp-component-input-role-border: var(--sp-component-input-border,
58
+ var(--sp-surface-input, var(--sp-color-neutral-200, #e2e8f0)));
59
+ --sp-component-input-role-bg: var(--sp-component-input-bg,
60
+ var(--sp-surface-input, var(--sp-color-neutral-50, #f8fafc)));
61
+ --sp-component-input-role-text: var(--sp-component-input-text,
62
+ var(--sp-text-on-surface-default, var(--sp-color-neutral-900, #0f172a)));
63
+ --sp-component-input-role-placeholder: var(--sp-component-input-placeholder,
64
+ var(--sp-text-on-surface-muted, var(--sp-color-neutral-500, #64748b)));
65
+ --sp-component-input-role-border-focus: var(--sp-component-input-border-focus,
66
+ var(--sp-color-brand-500, #8652ff));
67
+ --sp-component-input-role-ring: var(--sp-component-input-ring,
68
+ var(--sp-color-brand-500, #8652ff));
69
+ --sp-component-input-role-border-error: var(--sp-component-input-border-error,
70
+ var(--sp-color-error-500, #ef4444));
71
+ --sp-component-input-role-bg-error: var(--sp-component-input-bg-error,
72
+ var(--sp-color-error-50, #fef2f2));
73
+ --sp-component-input-role-text-error: var(--sp-component-input-text-error,
74
+ var(--sp-color-error-700, #b91c1c));
75
+ --sp-component-input-role-border-success: var(--sp-component-input-border-success,
76
+ var(--sp-color-success-500, #22c55e));
77
+ --sp-component-input-role-bg-success: var(--sp-component-input-bg-success,
78
+ var(--sp-color-success-50, #f0fdf4));
79
+ --sp-component-input-role-text-success: var(--sp-component-input-text-success,
80
+ var(--sp-color-success-700, #15803d));
81
+ --sp-component-input-role-bg-disabled: var(--sp-component-input-bg-disabled,
82
+ var(--sp-surface-input, var(--sp-color-neutral-50, #f8fafc)));
83
+ --sp-component-input-role-text-disabled: var(--sp-component-input-text-disabled,
84
+ var(--sp-text-on-surface-muted, var(--sp-color-neutral-400, #94a3b8)));
85
+ --sp-component-input-role-border-disabled: var(--sp-component-input-border-disabled,
86
+ var(--sp-surface-input, var(--sp-color-neutral-200, #e2e8f0)));
72
87
  }
73
88
 
74
89
  /* BUTTONS -------------------------------------------------------------- */
@@ -79,10 +94,8 @@
79
94
  gap: var(--sp-space-2xs, 0.25rem);
80
95
  padding: var(--sp-space-2xs, 0.25rem) var(--sp-space-md, 1rem);
81
96
  border-radius: var(--sp-radius-md, 4px);
82
- border: 1px solid var(
83
- --sp-component-button-border-base,
84
- var(--sp-component-button-ghost-bg, var(--sp-button-ghost-bg))
85
- );
97
+ border: 1px solid var(--sp-component-button-border-base,
98
+ var(--sp-component-button-ghost-bg, var(--sp-button-ghost-bg)));
86
99
  font-family: var(--sp-font-family-sans, system-ui);
87
100
  font-size: var(--sp-font-md-size, 1rem);
88
101
  line-height: 1;
@@ -143,10 +156,8 @@
143
156
  .sp-btn--primary {
144
157
  background-color: var(--sp-component-button-primary-bg);
145
158
  color: var(--sp-component-button-primary-text);
146
- box-shadow: var(
147
- --sp-component-button-shadow,
148
- var(--sp-shadow-sm)
149
- );
159
+ box-shadow: var(--sp-component-button-shadow,
160
+ var(--sp-shadow-sm));
150
161
  }
151
162
 
152
163
  .sp-btn--primary.sp-btn--hover,
@@ -265,7 +276,7 @@
265
276
  }
266
277
 
267
278
  .sp-label {
268
- color: var(--sp-component-input-text, var(--sp-text-on-surface-default, var(--sp-form-default-text)));
279
+ color: var(--sp-component-input-role-text);
269
280
  font-size: var(--sp-font-sm-size, 0.875rem);
270
281
  font-weight: var(--sp-font-sm-weight, 500);
271
282
  line-height: var(--sp-font-sm-line-height, 1.25rem);
@@ -278,7 +289,7 @@
278
289
  }
279
290
 
280
291
  .sp-error-message {
281
- color: var(--sp-color-danger, #dc2626);
292
+ color: var(--sp-color-error-600, #dc2626);
282
293
  font-size: var(--sp-font-xs-size, 0.75rem);
283
294
  line-height: var(--sp-font-xs-line-height, 1rem);
284
295
  }
@@ -289,9 +300,9 @@
289
300
  appearance: none;
290
301
  padding: var(--sp-space-2xs, 0.25rem) var(--sp-space-md, 1rem);
291
302
  border-radius: var(--sp-radius-md, 4px);
292
- border: 1px solid var(--sp-component-input-border, var(--sp-form-default-border));
293
- background-color: var(--sp-component-input-bg, var(--sp-surface-input, var(--sp-form-default-bg)));
294
- color: var(--sp-component-input-text, var(--sp-text-on-surface-default, var(--sp-form-default-text)));
303
+ border: 1px solid var(--sp-component-input-role-border);
304
+ background-color: var(--sp-component-input-role-bg);
305
+ color: var(--sp-component-input-role-text);
295
306
  font-family: var(--sp-font-family-sans, system-ui);
296
307
  font-size: var(--sp-font-md-size, 1rem);
297
308
  line-height: var(--sp-font-md-line-height, 1.5rem);
@@ -302,7 +313,7 @@
302
313
  }
303
314
 
304
315
  .sp-input::placeholder {
305
- color: var(--sp-component-input-placeholder, var(--sp-text-on-surface-muted, var(--sp-form-default-placeholder)));
316
+ color: var(--sp-component-input-role-placeholder);
306
317
  }
307
318
 
308
319
  .sp-input--sm {
@@ -329,33 +340,27 @@
329
340
 
330
341
  .sp-input:focus,
331
342
  .sp-input--focus {
332
- border-color: var(
333
- --sp-component-input-border-focus,
334
- var(--sp-component-input-border, var(--sp-form-focus-border))
335
- );
336
- box-shadow: 0 0 0 var(--sp-focus-ring-width, 2px) var(
337
- --sp-component-input-ring,
338
- var(--sp-form-focus-ring, var(--sp-color-focus-primary))
339
- );
343
+ border-color: var(--sp-component-input-role-border-focus);
344
+ box-shadow: 0 0 0 var(--sp-focus-ring-width, 2px) var(--sp-component-input-role-ring);
340
345
  outline: none;
341
346
  }
342
347
 
343
348
  .sp-input--error {
344
- border-color: var(--sp-component-input-border-invalid, var(--sp-form-invalid-border));
345
- background-color: var(--sp-component-input-bg-invalid, var(--sp-form-invalid-bg));
346
- color: var(--sp-component-input-text-invalid, var(--sp-form-invalid-text));
349
+ border-color: var(--sp-component-input-role-border-error);
350
+ background-color: var(--sp-component-input-role-bg-error);
351
+ color: var(--sp-component-input-role-text-error);
347
352
  }
348
353
 
349
354
  .sp-input--success {
350
- border-color: var(--sp-component-input-border-valid, var(--sp-form-valid-border));
351
- background-color: var(--sp-component-input-bg-valid, var(--sp-form-valid-bg));
352
- color: var(--sp-component-input-text-valid, var(--sp-form-valid-text));
355
+ border-color: var(--sp-component-input-role-border-success);
356
+ background-color: var(--sp-component-input-role-bg-success);
357
+ color: var(--sp-component-input-role-text-success);
353
358
  }
354
359
 
355
360
  .sp-input--disabled {
356
- background-color: var(--sp-component-input-bg-disabled, var(--sp-form-disabled-bg));
357
- color: var(--sp-component-input-text-disabled, var(--sp-form-disabled-text));
358
- border-color: var(--sp-component-input-border-disabled, var(--sp-form-disabled-border));
361
+ background-color: var(--sp-component-input-role-bg-disabled);
362
+ color: var(--sp-component-input-role-text-disabled);
363
+ border-color: var(--sp-component-input-role-border-disabled);
359
364
  cursor: not-allowed;
360
365
  }
361
366
 
@@ -372,10 +377,8 @@
372
377
  border-radius: var(--sp-radius-lg, 8px);
373
378
  padding: var(--sp-space-lg, 1.5rem);
374
379
  box-shadow: var(--sp-component-card-shadow, var(--sp-shadow-sm));
375
- border: 1px solid var(
376
- --sp-component-card-border-base,
377
- var(--sp-component-card-ghost-border, var(--sp-component-card-bg))
378
- );
380
+ border: 1px solid var(--sp-component-card-border-base,
381
+ var(--sp-component-card-ghost-border, var(--sp-component-card-bg)));
379
382
  }
380
383
 
381
384
  .sp-card p,
package/dist/index.cjs CHANGED
@@ -2,11 +2,17 @@
2
2
 
3
3
  var spectreTokens = require('@phcdevworks/spectre-tokens');
4
4
 
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var spectreTokens__default = /*#__PURE__*/_interopDefault(spectreTokens);
8
+
5
9
  // src/css-constants.ts
6
- var spectreBaseStylesPath = "@phcdevworks/spectre-ui/dist/base.css";
7
- var spectreComponentsStylesPath = "@phcdevworks/spectre-ui/dist/components.css";
8
- var spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/dist/utilities.css";
10
+ var spectreBaseStylesPath = "@phcdevworks/spectre-ui/base.css";
11
+ var spectreComponentsStylesPath = "@phcdevworks/spectre-ui/components.css";
12
+ var spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/utilities.css";
13
+ var spectreIndexStylesPath = "@phcdevworks/spectre-ui/index.css";
9
14
  var spectreStyles = {
15
+ index: spectreIndexStylesPath,
10
16
  base: spectreBaseStylesPath,
11
17
  components: spectreComponentsStylesPath,
12
18
  utilities: spectreUtilitiesStylesPath
@@ -14,43 +20,34 @@ var spectreStyles = {
14
20
 
15
21
  // src/tailwind/theme.ts
16
22
  function createSpectreTailwindTheme(options) {
17
- const { tokens, overrides } = options;
18
23
  const mergedTokens = {
19
- ...tokens,
20
- ...overrides
21
- };
22
- const colors = {
23
- page: mergedTokens.surface?.page,
24
- card: mergedTokens.surface?.card,
25
- input: mergedTokens.surface?.input,
26
- text: {
27
- page: mergedTokens.text?.onPage?.default,
28
- "page-muted": mergedTokens.text?.onPage?.muted,
29
- surface: mergedTokens.text?.onSurface?.default,
30
- "surface-muted": mergedTokens.text?.onSurface?.muted
31
- },
32
- primary: mergedTokens.buttons?.primary?.bg ?? mergedTokens.colors?.primary
24
+ ...options.tokens ?? {},
25
+ ...options.overrides ?? {}
33
26
  };
34
- const spacing = mergedTokens.spacing ?? {};
35
- const borderRadius = mergedTokens.radii ?? {};
36
- const boxShadow = mergedTokens.shadows ?? {};
37
- const fontFamily = mergedTokens.typography?.families ?? {};
27
+ const t = mergedTokens;
28
+ const colors = t.colors ?? t.color ?? t.palette ?? {};
29
+ const spacing = t.spacing ?? t.space ?? {};
30
+ const borderRadius = t.radii ?? t.radius ?? {};
31
+ const boxShadow = t.shadows ?? t.shadow ?? {};
32
+ const fontFamily = t.typography?.families ?? t.fonts ?? {};
33
+ const fontSize = t.typography?.scale ?? t.fontSize ?? {};
38
34
  const theme2 = {
39
35
  colors,
40
36
  spacing,
41
37
  borderRadius,
42
38
  boxShadow,
43
- fontFamily
39
+ fontFamily,
40
+ fontSize
44
41
  };
45
42
  return { theme: theme2 };
46
43
  }
47
44
 
48
45
  // src/tailwind/preset.ts
49
- var { theme } = createSpectreTailwindTheme({ tokens: spectreTokens.tokens });
46
+ var { theme } = createSpectreTailwindTheme({ tokens: spectreTokens__default.default });
50
47
  var spectrePreset = {
51
48
  content: [],
52
- theme: theme ?? {}
53
- // ensure theme is never undefined
49
+ theme
50
+ // theme is guaranteed non-undefined now
54
51
  };
55
52
 
56
53
  // src/recipes/button.ts
@@ -58,19 +55,18 @@ function getButtonClasses(opts = {}) {
58
55
  const {
59
56
  variant = "primary",
60
57
  size = "md",
61
- tone = "default",
62
58
  fullWidth = false,
63
59
  loading = false,
64
60
  disabled = false,
65
61
  iconOnly = false
66
62
  } = opts;
67
- const classes = [];
68
- classes.push("sp-btn");
63
+ const classes = ["sp-btn"];
69
64
  const variantMap = {
70
65
  primary: "sp-btn--primary",
71
66
  secondary: "sp-btn--secondary",
72
67
  ghost: "sp-btn--ghost",
73
- danger: "sp-btn--danger"
68
+ danger: "sp-btn--danger",
69
+ success: "sp-btn--success"
74
70
  };
75
71
  classes.push(variantMap[variant]);
76
72
  const sizeMap = {
@@ -79,19 +75,11 @@ function getButtonClasses(opts = {}) {
79
75
  lg: "sp-btn--lg"
80
76
  };
81
77
  classes.push(sizeMap[size]);
82
- if (tone !== "default") {
83
- const toneMap = {
84
- success: "sp-btn--tone-success",
85
- warning: "sp-btn--tone-warning",
86
- danger: "sp-btn--tone-danger"
87
- };
88
- classes.push(toneMap[tone]);
89
- }
90
78
  if (fullWidth) classes.push("sp-btn--full");
91
79
  if (loading) classes.push("sp-btn--loading");
92
80
  if (disabled) classes.push("sp-btn--disabled");
93
81
  if (iconOnly) classes.push("sp-btn--icon");
94
- return classes.filter(Boolean).join(" ").trim();
82
+ return classes.join(" ").trim();
95
83
  }
96
84
 
97
85
  // src/recipes/card.ts
@@ -102,10 +90,10 @@ function getCardClasses(opts = {}) {
102
90
  padded = false,
103
91
  fullHeight = false
104
92
  } = opts;
105
- const classes = [];
106
- classes.push("sp-card");
93
+ const classes = ["sp-card"];
107
94
  const variantMap = {
108
95
  elevated: "sp-card--elevated",
96
+ flat: "sp-card--flat",
109
97
  outline: "sp-card--outline",
110
98
  ghost: "sp-card--ghost"
111
99
  };
@@ -113,38 +101,29 @@ function getCardClasses(opts = {}) {
113
101
  if (interactive) classes.push("sp-card--interactive");
114
102
  if (padded) classes.push("sp-card--padded");
115
103
  if (fullHeight) classes.push("sp-card--full");
116
- return classes.filter(Boolean).join(" ").trim();
104
+ return classes.join(" ").trim();
117
105
  }
118
106
 
119
107
  // src/recipes/input.ts
120
108
  function getInputClasses(opts = {}) {
121
- const {
122
- state = "default",
123
- size = "md",
124
- fullWidth = false
125
- } = opts;
126
- const classes = [];
127
- classes.push("sp-input");
128
- if (state === "error") {
129
- classes.push("sp-input--error");
130
- } else if (state === "success") {
131
- classes.push("sp-input--success");
132
- }
109
+ const { state = "default", size = "md", fullWidth = false } = opts;
110
+ const classes = ["sp-input"];
133
111
  const sizeMap = {
134
112
  sm: "sp-input--sm",
135
113
  md: "sp-input--md",
136
114
  lg: "sp-input--lg"
137
115
  };
138
116
  classes.push(sizeMap[size]);
139
- if (fullWidth) {
140
- classes.push("sp-input--full");
141
- }
142
- return classes.filter(Boolean).join(" ").trim();
117
+ if (state === "error") classes.push("sp-input--error");
118
+ if (state === "success") classes.push("sp-input--success");
119
+ if (state === "disabled") classes.push("sp-input--disabled");
120
+ if (fullWidth) classes.push("sp-input--full");
121
+ return classes.join(" ").trim();
143
122
  }
144
123
 
145
124
  Object.defineProperty(exports, "spectreTokens", {
146
125
  enumerable: true,
147
- get: function () { return spectreTokens.tokens; }
126
+ get: function () { return spectreTokens__default.default; }
148
127
  });
149
128
  exports.createSpectreTailwindTheme = createSpectreTailwindTheme;
150
129
  exports.getButtonClasses = getButtonClasses;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/css-constants.ts","../src/tailwind/theme.ts","../src/tailwind/preset.ts","../src/recipes/button.ts","../src/recipes/card.ts","../src/recipes/input.ts"],"names":["theme","spectreTokens"],"mappings":"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","file":"index.cjs","sourcesContent":["export const spectreBaseStylesPath = \"@phcdevworks/spectre-ui/dist/base.css\";\nexport const spectreComponentsStylesPath = \"@phcdevworks/spectre-ui/dist/components.css\";\nexport const spectreUtilitiesStylesPath = \"@phcdevworks/spectre-ui/dist/utilities.css\";\n\nexport const spectreStyles = {\n base: spectreBaseStylesPath,\n components: spectreComponentsStylesPath,\n utilities: spectreUtilitiesStylesPath,\n};\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport type { SpectreTokens } from \"../tokens\";\n\nexport interface SpectreTailwindTheme {\n theme: TailwindConfig[\"theme\"];\n}\n\nexport interface CreateSpectreTailwindThemeOptions {\n tokens: SpectreTokens;\n overrides?: Partial<SpectreTokens>;\n}\n\nexport function createSpectreTailwindTheme(\n options: CreateSpectreTailwindThemeOptions,\n): SpectreTailwindTheme {\n const { tokens, overrides } = options;\n\n // Shallow merge overrides into tokens\n const mergedTokens: SpectreTokens = {\n ...(tokens as SpectreTokens),\n ...(overrides as Partial<SpectreTokens> | undefined),\n };\n\n // Minimal, semantic color mapping\n const colors: Record<string, unknown> = {\n page: mergedTokens.surface?.page,\n card: mergedTokens.surface?.card,\n input: mergedTokens.surface?.input,\n text: {\n page: mergedTokens.text?.onPage?.default,\n \"page-muted\": mergedTokens.text?.onPage?.muted,\n surface: mergedTokens.text?.onSurface?.default,\n \"surface-muted\": mergedTokens.text?.onSurface?.muted,\n },\n primary:\n (mergedTokens as any).buttons?.primary?.bg ??\n (mergedTokens as any).colors?.primary,\n };\n\n const spacing: Record<string, unknown> =\n (mergedTokens as any).spacing ?? {};\n\n const borderRadius: Record<string, unknown> =\n (mergedTokens as any).radii ?? {};\n\n const boxShadow: Record<string, unknown> =\n (mergedTokens as any).shadows ?? {};\n\n const fontFamily: Record<string, unknown> =\n (mergedTokens as any).typography?.families ?? {};\n\n const theme: TailwindConfig[\"theme\"] = {\n colors: colors as any,\n spacing: spacing as any,\n borderRadius: borderRadius as any,\n boxShadow: boxShadow as any,\n fontFamily: fontFamily as any,\n };\n\n return { theme };\n}\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport { spectreTokens } from \"../tokens\";\nimport { createSpectreTailwindTheme } from \"./theme\";\n\nconst { theme } = createSpectreTailwindTheme({ tokens: spectreTokens });\n\nexport const spectrePreset: TailwindConfig = {\n content: [],\n theme: theme ?? {}, // ensure theme is never undefined\n};\n\nexport default spectrePreset;\n","export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonTone = 'default' | 'success' | 'warning' | 'danger';\n\nexport interface ButtonRecipeOptions {\n variant?: ButtonVariant;\n size?: ButtonSize;\n tone?: ButtonTone;\n fullWidth?: boolean;\n loading?: boolean;\n disabled?: boolean;\n iconOnly?: boolean;\n}\n\n/**\n * Generate Spectre button classes.\n *\n * Rules:\n * - Base: \"sp-btn\"\n * - Variant: \"sp-btn--primary\" / \"sp-btn--secondary\" / \"sp-btn--ghost\" / \"sp-btn--danger\"\n * - default variant is \"primary\"\n * - Size: \"sp-btn--sm\" / \"sp-btn--md\" / \"sp-btn--lg\"\n * - default size is \"md\"\n * - Tone: \"sp-btn--tone-success\" / \"sp-btn--tone-warning\" / \"sp-btn--tone-danger\"\n * - default tone is \"default\" (no tone class)\n * - fullWidth: add \"sp-btn--full\"\n * - loading: add \"sp-btn--loading\"\n * - disabled: add \"sp-btn--disabled\"\n * - iconOnly: add \"sp-btn--icon\"\n *\n * Must return a single space-joined, trimmed class string.\n */\nexport function getButtonClasses(opts: ButtonRecipeOptions = {}): string {\n const {\n variant = 'primary',\n size = 'md',\n tone = 'default',\n fullWidth = false,\n loading = false,\n disabled = false,\n iconOnly = false,\n } = opts;\n\n const classes: string[] = [];\n\n // Base\n classes.push('sp-btn');\n\n // Variant\n const variantMap: Record<ButtonVariant, string> = {\n primary: 'sp-btn--primary',\n secondary: 'sp-btn--secondary',\n ghost: 'sp-btn--ghost',\n danger: 'sp-btn--danger',\n };\n classes.push(variantMap[variant]);\n\n // Size\n const sizeMap: Record<ButtonSize, string> = {\n sm: 'sp-btn--sm',\n md: 'sp-btn--md',\n lg: 'sp-btn--lg',\n };\n classes.push(sizeMap[size]);\n\n // Tone (optional)\n if (tone !== 'default') {\n const toneMap: Record<Exclude<ButtonTone, 'default'>, string> = {\n success: 'sp-btn--tone-success',\n warning: 'sp-btn--tone-warning',\n danger: 'sp-btn--tone-danger',\n };\n classes.push(toneMap[tone as Exclude<ButtonTone, 'default'>]);\n }\n\n // Flags\n if (fullWidth) classes.push('sp-btn--full');\n if (loading) classes.push('sp-btn--loading');\n if (disabled) classes.push('sp-btn--disabled');\n if (iconOnly) classes.push('sp-btn--icon');\n\n // Final class string\n return classes.filter(Boolean).join(' ').trim();\n}\n","export type CardVariant = 'elevated' | 'outline' | 'ghost';\n\nexport interface CardRecipeOptions {\n variant?: CardVariant;\n interactive?: boolean; // hover/focus styles\n padded?: boolean; // apply default padding\n fullHeight?: boolean;\n}\n\n/**\n * Generate Spectre card classes.\n *\n * Rules:\n * - Base class: \"sp-card\"\n * - Variant (default: elevated):\n * - \"sp-card--elevated\"\n * - \"sp-card--outline\"\n * - \"sp-card--ghost\"\n * - interactive: add \"sp-card--interactive\"\n * - padded: add \"sp-card--padded\"\n * - fullHeight: add \"sp-card--full\"\n */\nexport function getCardClasses(opts: CardRecipeOptions = {}): string {\n const {\n variant = 'elevated',\n interactive = false,\n padded = false,\n fullHeight = false,\n } = opts;\n\n const classes: string[] = [];\n\n // Base\n classes.push('sp-card');\n\n // Variant\n const variantMap: Record<CardVariant, string> = {\n elevated: 'sp-card--elevated',\n outline: 'sp-card--outline',\n ghost: 'sp-card--ghost',\n };\n classes.push(variantMap[variant]);\n\n // Flags\n if (interactive) classes.push('sp-card--interactive');\n if (padded) classes.push('sp-card--padded');\n if (fullHeight) classes.push('sp-card--full');\n\n return classes.filter(Boolean).join(' ').trim();\n}\n","export type InputState = 'default' | 'error' | 'success';\nexport type InputSize = 'sm' | 'md' | 'lg';\n\nexport interface InputRecipeOptions {\n state?: InputState;\n size?: InputSize;\n fullWidth?: boolean;\n}\n\n/**\n * Generate Spectre input classes.\n *\n * Rules:\n * - Base class: \"sp-input\"\n * - State:\n * - \"default\" => no state modifier\n * - \"error\" => \"sp-input--error\"\n * - \"success\" => \"sp-input--success\"\n * - Size (default: md):\n * - \"sp-input--sm\"\n * - \"sp-input--md\"\n * - \"sp-input--lg\"\n * - fullWidth: add \"sp-input--full\"\n */\nexport function getInputClasses(opts: InputRecipeOptions = {}): string {\n const {\n state = 'default',\n size = 'md',\n fullWidth = false,\n } = opts;\n\n const classes: string[] = [];\n\n // Base\n classes.push('sp-input');\n\n // State\n if (state === 'error') {\n classes.push('sp-input--error');\n } else if (state === 'success') {\n classes.push('sp-input--success');\n }\n\n // Size\n const sizeMap: Record<InputSize, string> = {\n sm: 'sp-input--sm',\n md: 'sp-input--md',\n lg: 'sp-input--lg',\n };\n classes.push(sizeMap[size]);\n\n // Flags\n if (fullWidth) {\n classes.push('sp-input--full');\n }\n\n return classes.filter(Boolean).join(' ').trim();\n}\n"]}
1
+ {"version":3,"sources":["../src/css-constants.ts","../src/tailwind/theme.ts","../src/tailwind/preset.ts","../src/recipes/button.ts","../src/recipes/card.ts","../src/recipes/input.ts"],"names":["theme","spectreTokens"],"mappings":"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","file":"index.cjs","sourcesContent":["export const spectreBaseStylesPath = \"@phcdevworks/spectre-ui/base.css\";\nexport const spectreComponentsStylesPath = \"@phcdevworks/spectre-ui/components.css\";\nexport const spectreUtilitiesStylesPath = \"@phcdevworks/spectre-ui/utilities.css\";\nexport const spectreIndexStylesPath = \"@phcdevworks/spectre-ui/index.css\";\n\nexport const spectreStyles = {\n index: spectreIndexStylesPath,\n base: spectreBaseStylesPath,\n components: spectreComponentsStylesPath,\n utilities: spectreUtilitiesStylesPath,\n};\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport type { SpectreTokens } from \"../tokens\";\n\ntype TailwindTheme = NonNullable<TailwindConfig[\"theme\"]>;\n\nexport interface SpectreTailwindTheme {\n theme: TailwindTheme;\n}\n\nexport interface CreateSpectreTailwindThemeOptions {\n tokens: SpectreTokens;\n overrides?: Partial<SpectreTokens>;\n}\n\n/**\n * Minimal, type-safe theme mapper.\n * Important: theme is NEVER undefined (fixes exactOptionalPropertyTypes + DTS).\n */\nexport function createSpectreTailwindTheme(\n options: CreateSpectreTailwindThemeOptions\n): SpectreTailwindTheme {\n const mergedTokens = {\n ...(options.tokens ?? ({} as SpectreTokens)),\n ...(options.overrides ?? {}),\n } as SpectreTokens;\n\n // We keep mapping shallow + permissive because token shapes will evolve.\n // Tailwind accepts nested objects of strings for colors.\n const t: any = mergedTokens;\n\n const colors = (t.colors ?? t.color ?? t.palette ?? {}) as Record<string, any>;\n const spacing = (t.spacing ?? t.space ?? {}) as Record<string, any>;\n const borderRadius = (t.radii ?? t.radius ?? {}) as Record<string, any>;\n const boxShadow = (t.shadows ?? t.shadow ?? {}) as Record<string, any>;\n const fontFamily = (t.typography?.families ?? t.fonts ?? {}) as Record<string, any>;\n const fontSize = (t.typography?.scale ?? t.fontSize ?? {}) as Record<string, any>;\n\n const theme: TailwindTheme = {\n colors: colors as any,\n spacing: spacing as any,\n borderRadius: borderRadius as any,\n boxShadow: boxShadow as any,\n fontFamily: fontFamily as any,\n fontSize: fontSize as any,\n };\n\n return { theme };\n}\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport { spectreTokens } from \"../tokens\";\nimport { createSpectreTailwindTheme } from \"./theme\";\n\nconst { theme } = createSpectreTailwindTheme({ tokens: spectreTokens });\n\nexport const spectrePreset: TailwindConfig = {\n content: [],\n theme, // theme is guaranteed non-undefined now\n};\n\nexport const spectreTailwindPreset: TailwindConfig = spectrePreset;\n","export type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"danger\" | \"success\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonRecipeOptions {\n variant?: ButtonVariant;\n size?: ButtonSize;\n fullWidth?: boolean;\n loading?: boolean;\n disabled?: boolean;\n iconOnly?: boolean;\n}\n\nexport function getButtonClasses(opts: ButtonRecipeOptions = {}): string {\n const {\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n disabled = false,\n iconOnly = false,\n } = opts;\n\n const classes: string[] = [\"sp-btn\"];\n\n const variantMap: Record<ButtonVariant, string> = {\n primary: \"sp-btn--primary\",\n secondary: \"sp-btn--secondary\",\n ghost: \"sp-btn--ghost\",\n danger: \"sp-btn--danger\",\n success: \"sp-btn--success\",\n };\n classes.push(variantMap[variant]);\n\n const sizeMap: Record<ButtonSize, string> = {\n sm: \"sp-btn--sm\",\n md: \"sp-btn--md\",\n lg: \"sp-btn--lg\",\n };\n classes.push(sizeMap[size]);\n\n if (fullWidth) classes.push(\"sp-btn--full\");\n if (loading) classes.push(\"sp-btn--loading\");\n if (disabled) classes.push(\"sp-btn--disabled\");\n if (iconOnly) classes.push(\"sp-btn--icon\");\n\n return classes.join(\" \").trim();\n}\n","export type CardVariant = \"elevated\" | \"flat\" | \"outline\" | \"ghost\";\n\nexport interface CardRecipeOptions {\n variant?: CardVariant;\n interactive?: boolean; // hover/focus styles\n padded?: boolean; // apply default padding\n fullHeight?: boolean;\n}\n\nexport function getCardClasses(opts: CardRecipeOptions = {}): string {\n const {\n variant = \"elevated\",\n interactive = false,\n padded = false,\n fullHeight = false,\n } = opts;\n\n const classes: string[] = [\"sp-card\"];\n\n const variantMap: Record<CardVariant, string> = {\n elevated: \"sp-card--elevated\",\n flat: \"sp-card--flat\",\n outline: \"sp-card--outline\",\n ghost: \"sp-card--ghost\",\n };\n classes.push(variantMap[variant]);\n\n if (interactive) classes.push(\"sp-card--interactive\");\n if (padded) classes.push(\"sp-card--padded\");\n if (fullHeight) classes.push(\"sp-card--full\");\n\n return classes.join(\" \").trim();\n}\n","export type InputState = \"default\" | \"error\" | \"success\" | \"disabled\";\nexport type InputSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface InputRecipeOptions {\n state?: InputState;\n size?: InputSize;\n fullWidth?: boolean;\n}\n\nexport function getInputClasses(opts: InputRecipeOptions = {}): string {\n const { state = \"default\", size = \"md\", fullWidth = false } = opts;\n\n const classes: string[] = [\"sp-input\"];\n\n const sizeMap: Record<InputSize, string> = {\n sm: \"sp-input--sm\",\n md: \"sp-input--md\",\n lg: \"sp-input--lg\",\n };\n classes.push(sizeMap[size]);\n\n // State\n if (state === \"error\") classes.push(\"sp-input--error\");\n if (state === \"success\") classes.push(\"sp-input--success\");\n if (state === \"disabled\") classes.push(\"sp-input--disabled\");\n\n if (fullWidth) classes.push(\"sp-input--full\");\n\n return classes.join(\" \").trim();\n}\n"]}
package/dist/index.css ADDED
@@ -0,0 +1,8 @@
1
+ /* Spectre UI canonical bundle:
2
+ - Tokens first (defines --sp-* vars)
3
+ - Then UI base, components, utilities
4
+ */
5
+ @import "@phcdevworks/spectre-tokens/index.css";
6
+ @import "./base.css";
7
+ @import "./components.css";
8
+ @import "./utilities.css";
package/dist/index.d.cts CHANGED
@@ -1,103 +1,61 @@
1
1
  import { Config } from 'tailwindcss';
2
2
  import { SpectreTokens } from '@phcdevworks/spectre-tokens';
3
- export { SpectreTokens, tokens as spectreTokens } from '@phcdevworks/spectre-tokens';
3
+ export { SpectreTokens, default as spectreTokens } from '@phcdevworks/spectre-tokens';
4
4
 
5
- declare const spectreBaseStylesPath = "@phcdevworks/spectre-ui/dist/base.css";
6
- declare const spectreComponentsStylesPath = "@phcdevworks/spectre-ui/dist/components.css";
7
- declare const spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/dist/utilities.css";
5
+ declare const spectreBaseStylesPath = "@phcdevworks/spectre-ui/base.css";
6
+ declare const spectreComponentsStylesPath = "@phcdevworks/spectre-ui/components.css";
7
+ declare const spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/utilities.css";
8
8
  declare const spectreStyles: {
9
+ index: string;
9
10
  base: string;
10
11
  components: string;
11
12
  utilities: string;
12
13
  };
13
14
 
14
- declare const spectrePreset: Config;
15
-
15
+ type TailwindTheme = NonNullable<Config["theme"]>;
16
16
  interface SpectreTailwindTheme {
17
- theme: Config["theme"];
17
+ theme: TailwindTheme;
18
18
  }
19
19
  interface CreateSpectreTailwindThemeOptions {
20
20
  tokens: SpectreTokens;
21
21
  overrides?: Partial<SpectreTokens>;
22
22
  }
23
+ /**
24
+ * Minimal, type-safe theme mapper.
25
+ * Important: theme is NEVER undefined (fixes exactOptionalPropertyTypes + DTS).
26
+ */
23
27
  declare function createSpectreTailwindTheme(options: CreateSpectreTailwindThemeOptions): SpectreTailwindTheme;
24
28
 
25
- type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
26
- type ButtonSize = 'sm' | 'md' | 'lg';
27
- type ButtonTone = 'default' | 'success' | 'warning' | 'danger';
29
+ declare const spectrePreset: Config;
30
+
31
+ type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "success";
32
+ type ButtonSize = "sm" | "md" | "lg";
28
33
  interface ButtonRecipeOptions {
29
34
  variant?: ButtonVariant;
30
35
  size?: ButtonSize;
31
- tone?: ButtonTone;
32
36
  fullWidth?: boolean;
33
37
  loading?: boolean;
34
38
  disabled?: boolean;
35
39
  iconOnly?: boolean;
36
40
  }
37
- /**
38
- * Generate Spectre button classes.
39
- *
40
- * Rules:
41
- * - Base: "sp-btn"
42
- * - Variant: "sp-btn--primary" / "sp-btn--secondary" / "sp-btn--ghost" / "sp-btn--danger"
43
- * - default variant is "primary"
44
- * - Size: "sp-btn--sm" / "sp-btn--md" / "sp-btn--lg"
45
- * - default size is "md"
46
- * - Tone: "sp-btn--tone-success" / "sp-btn--tone-warning" / "sp-btn--tone-danger"
47
- * - default tone is "default" (no tone class)
48
- * - fullWidth: add "sp-btn--full"
49
- * - loading: add "sp-btn--loading"
50
- * - disabled: add "sp-btn--disabled"
51
- * - iconOnly: add "sp-btn--icon"
52
- *
53
- * Must return a single space-joined, trimmed class string.
54
- */
55
41
  declare function getButtonClasses(opts?: ButtonRecipeOptions): string;
56
42
 
57
- type CardVariant = 'elevated' | 'outline' | 'ghost';
43
+ type CardVariant = "elevated" | "flat" | "outline" | "ghost";
58
44
  interface CardRecipeOptions {
59
45
  variant?: CardVariant;
60
46
  interactive?: boolean;
61
47
  padded?: boolean;
62
48
  fullHeight?: boolean;
63
49
  }
64
- /**
65
- * Generate Spectre card classes.
66
- *
67
- * Rules:
68
- * - Base class: "sp-card"
69
- * - Variant (default: elevated):
70
- * - "sp-card--elevated"
71
- * - "sp-card--outline"
72
- * - "sp-card--ghost"
73
- * - interactive: add "sp-card--interactive"
74
- * - padded: add "sp-card--padded"
75
- * - fullHeight: add "sp-card--full"
76
- */
77
50
  declare function getCardClasses(opts?: CardRecipeOptions): string;
78
51
 
79
- type InputState = 'default' | 'error' | 'success';
80
- type InputSize = 'sm' | 'md' | 'lg';
52
+ type InputState = "default" | "error" | "success" | "disabled";
53
+ type InputSize = "sm" | "md" | "lg";
81
54
  interface InputRecipeOptions {
82
55
  state?: InputState;
83
56
  size?: InputSize;
84
57
  fullWidth?: boolean;
85
58
  }
86
- /**
87
- * Generate Spectre input classes.
88
- *
89
- * Rules:
90
- * - Base class: "sp-input"
91
- * - State:
92
- * - "default" => no state modifier
93
- * - "error" => "sp-input--error"
94
- * - "success" => "sp-input--success"
95
- * - Size (default: md):
96
- * - "sp-input--sm"
97
- * - "sp-input--md"
98
- * - "sp-input--lg"
99
- * - fullWidth: add "sp-input--full"
100
- */
101
59
  declare function getInputClasses(opts?: InputRecipeOptions): string;
102
60
 
103
- export { type ButtonRecipeOptions, type ButtonSize, type ButtonTone, type ButtonVariant, type CardRecipeOptions, type CardVariant, type CreateSpectreTailwindThemeOptions, type InputRecipeOptions, type InputSize, type InputState, type SpectreTailwindTheme, createSpectreTailwindTheme, getButtonClasses, getCardClasses, getInputClasses, spectreBaseStylesPath, spectreComponentsStylesPath, spectrePreset, spectreStyles, spectreUtilitiesStylesPath };
61
+ export { type ButtonRecipeOptions, type ButtonSize, type ButtonVariant, type CardRecipeOptions, type CardVariant, type CreateSpectreTailwindThemeOptions, type InputRecipeOptions, type InputSize, type InputState, type SpectreTailwindTheme, createSpectreTailwindTheme, getButtonClasses, getCardClasses, getInputClasses, spectreBaseStylesPath, spectreComponentsStylesPath, spectrePreset, spectreStyles, spectreUtilitiesStylesPath };
package/dist/index.d.ts CHANGED
@@ -1,103 +1,61 @@
1
1
  import { Config } from 'tailwindcss';
2
2
  import { SpectreTokens } from '@phcdevworks/spectre-tokens';
3
- export { SpectreTokens, tokens as spectreTokens } from '@phcdevworks/spectre-tokens';
3
+ export { SpectreTokens, default as spectreTokens } from '@phcdevworks/spectre-tokens';
4
4
 
5
- declare const spectreBaseStylesPath = "@phcdevworks/spectre-ui/dist/base.css";
6
- declare const spectreComponentsStylesPath = "@phcdevworks/spectre-ui/dist/components.css";
7
- declare const spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/dist/utilities.css";
5
+ declare const spectreBaseStylesPath = "@phcdevworks/spectre-ui/base.css";
6
+ declare const spectreComponentsStylesPath = "@phcdevworks/spectre-ui/components.css";
7
+ declare const spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/utilities.css";
8
8
  declare const spectreStyles: {
9
+ index: string;
9
10
  base: string;
10
11
  components: string;
11
12
  utilities: string;
12
13
  };
13
14
 
14
- declare const spectrePreset: Config;
15
-
15
+ type TailwindTheme = NonNullable<Config["theme"]>;
16
16
  interface SpectreTailwindTheme {
17
- theme: Config["theme"];
17
+ theme: TailwindTheme;
18
18
  }
19
19
  interface CreateSpectreTailwindThemeOptions {
20
20
  tokens: SpectreTokens;
21
21
  overrides?: Partial<SpectreTokens>;
22
22
  }
23
+ /**
24
+ * Minimal, type-safe theme mapper.
25
+ * Important: theme is NEVER undefined (fixes exactOptionalPropertyTypes + DTS).
26
+ */
23
27
  declare function createSpectreTailwindTheme(options: CreateSpectreTailwindThemeOptions): SpectreTailwindTheme;
24
28
 
25
- type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
26
- type ButtonSize = 'sm' | 'md' | 'lg';
27
- type ButtonTone = 'default' | 'success' | 'warning' | 'danger';
29
+ declare const spectrePreset: Config;
30
+
31
+ type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "success";
32
+ type ButtonSize = "sm" | "md" | "lg";
28
33
  interface ButtonRecipeOptions {
29
34
  variant?: ButtonVariant;
30
35
  size?: ButtonSize;
31
- tone?: ButtonTone;
32
36
  fullWidth?: boolean;
33
37
  loading?: boolean;
34
38
  disabled?: boolean;
35
39
  iconOnly?: boolean;
36
40
  }
37
- /**
38
- * Generate Spectre button classes.
39
- *
40
- * Rules:
41
- * - Base: "sp-btn"
42
- * - Variant: "sp-btn--primary" / "sp-btn--secondary" / "sp-btn--ghost" / "sp-btn--danger"
43
- * - default variant is "primary"
44
- * - Size: "sp-btn--sm" / "sp-btn--md" / "sp-btn--lg"
45
- * - default size is "md"
46
- * - Tone: "sp-btn--tone-success" / "sp-btn--tone-warning" / "sp-btn--tone-danger"
47
- * - default tone is "default" (no tone class)
48
- * - fullWidth: add "sp-btn--full"
49
- * - loading: add "sp-btn--loading"
50
- * - disabled: add "sp-btn--disabled"
51
- * - iconOnly: add "sp-btn--icon"
52
- *
53
- * Must return a single space-joined, trimmed class string.
54
- */
55
41
  declare function getButtonClasses(opts?: ButtonRecipeOptions): string;
56
42
 
57
- type CardVariant = 'elevated' | 'outline' | 'ghost';
43
+ type CardVariant = "elevated" | "flat" | "outline" | "ghost";
58
44
  interface CardRecipeOptions {
59
45
  variant?: CardVariant;
60
46
  interactive?: boolean;
61
47
  padded?: boolean;
62
48
  fullHeight?: boolean;
63
49
  }
64
- /**
65
- * Generate Spectre card classes.
66
- *
67
- * Rules:
68
- * - Base class: "sp-card"
69
- * - Variant (default: elevated):
70
- * - "sp-card--elevated"
71
- * - "sp-card--outline"
72
- * - "sp-card--ghost"
73
- * - interactive: add "sp-card--interactive"
74
- * - padded: add "sp-card--padded"
75
- * - fullHeight: add "sp-card--full"
76
- */
77
50
  declare function getCardClasses(opts?: CardRecipeOptions): string;
78
51
 
79
- type InputState = 'default' | 'error' | 'success';
80
- type InputSize = 'sm' | 'md' | 'lg';
52
+ type InputState = "default" | "error" | "success" | "disabled";
53
+ type InputSize = "sm" | "md" | "lg";
81
54
  interface InputRecipeOptions {
82
55
  state?: InputState;
83
56
  size?: InputSize;
84
57
  fullWidth?: boolean;
85
58
  }
86
- /**
87
- * Generate Spectre input classes.
88
- *
89
- * Rules:
90
- * - Base class: "sp-input"
91
- * - State:
92
- * - "default" => no state modifier
93
- * - "error" => "sp-input--error"
94
- * - "success" => "sp-input--success"
95
- * - Size (default: md):
96
- * - "sp-input--sm"
97
- * - "sp-input--md"
98
- * - "sp-input--lg"
99
- * - fullWidth: add "sp-input--full"
100
- */
101
59
  declare function getInputClasses(opts?: InputRecipeOptions): string;
102
60
 
103
- export { type ButtonRecipeOptions, type ButtonSize, type ButtonTone, type ButtonVariant, type CardRecipeOptions, type CardVariant, type CreateSpectreTailwindThemeOptions, type InputRecipeOptions, type InputSize, type InputState, type SpectreTailwindTheme, createSpectreTailwindTheme, getButtonClasses, getCardClasses, getInputClasses, spectreBaseStylesPath, spectreComponentsStylesPath, spectrePreset, spectreStyles, spectreUtilitiesStylesPath };
61
+ export { type ButtonRecipeOptions, type ButtonSize, type ButtonVariant, type CardRecipeOptions, type CardVariant, type CreateSpectreTailwindThemeOptions, type InputRecipeOptions, type InputSize, type InputState, type SpectreTailwindTheme, createSpectreTailwindTheme, getButtonClasses, getCardClasses, getInputClasses, spectreBaseStylesPath, spectreComponentsStylesPath, spectrePreset, spectreStyles, spectreUtilitiesStylesPath };
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
- import { tokens } from '@phcdevworks/spectre-tokens';
2
- export { tokens as spectreTokens } from '@phcdevworks/spectre-tokens';
1
+ import spectreTokens from '@phcdevworks/spectre-tokens';
2
+ export { default as spectreTokens } from '@phcdevworks/spectre-tokens';
3
3
 
4
4
  // src/css-constants.ts
5
- var spectreBaseStylesPath = "@phcdevworks/spectre-ui/dist/base.css";
6
- var spectreComponentsStylesPath = "@phcdevworks/spectre-ui/dist/components.css";
7
- var spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/dist/utilities.css";
5
+ var spectreBaseStylesPath = "@phcdevworks/spectre-ui/base.css";
6
+ var spectreComponentsStylesPath = "@phcdevworks/spectre-ui/components.css";
7
+ var spectreUtilitiesStylesPath = "@phcdevworks/spectre-ui/utilities.css";
8
+ var spectreIndexStylesPath = "@phcdevworks/spectre-ui/index.css";
8
9
  var spectreStyles = {
10
+ index: spectreIndexStylesPath,
9
11
  base: spectreBaseStylesPath,
10
12
  components: spectreComponentsStylesPath,
11
13
  utilities: spectreUtilitiesStylesPath
@@ -13,43 +15,34 @@ var spectreStyles = {
13
15
 
14
16
  // src/tailwind/theme.ts
15
17
  function createSpectreTailwindTheme(options) {
16
- const { tokens, overrides } = options;
17
18
  const mergedTokens = {
18
- ...tokens,
19
- ...overrides
19
+ ...options.tokens ?? {},
20
+ ...options.overrides ?? {}
20
21
  };
21
- const colors = {
22
- page: mergedTokens.surface?.page,
23
- card: mergedTokens.surface?.card,
24
- input: mergedTokens.surface?.input,
25
- text: {
26
- page: mergedTokens.text?.onPage?.default,
27
- "page-muted": mergedTokens.text?.onPage?.muted,
28
- surface: mergedTokens.text?.onSurface?.default,
29
- "surface-muted": mergedTokens.text?.onSurface?.muted
30
- },
31
- primary: mergedTokens.buttons?.primary?.bg ?? mergedTokens.colors?.primary
32
- };
33
- const spacing = mergedTokens.spacing ?? {};
34
- const borderRadius = mergedTokens.radii ?? {};
35
- const boxShadow = mergedTokens.shadows ?? {};
36
- const fontFamily = mergedTokens.typography?.families ?? {};
22
+ const t = mergedTokens;
23
+ const colors = t.colors ?? t.color ?? t.palette ?? {};
24
+ const spacing = t.spacing ?? t.space ?? {};
25
+ const borderRadius = t.radii ?? t.radius ?? {};
26
+ const boxShadow = t.shadows ?? t.shadow ?? {};
27
+ const fontFamily = t.typography?.families ?? t.fonts ?? {};
28
+ const fontSize = t.typography?.scale ?? t.fontSize ?? {};
37
29
  const theme2 = {
38
30
  colors,
39
31
  spacing,
40
32
  borderRadius,
41
33
  boxShadow,
42
- fontFamily
34
+ fontFamily,
35
+ fontSize
43
36
  };
44
37
  return { theme: theme2 };
45
38
  }
46
39
 
47
40
  // src/tailwind/preset.ts
48
- var { theme } = createSpectreTailwindTheme({ tokens: tokens });
41
+ var { theme } = createSpectreTailwindTheme({ tokens: spectreTokens });
49
42
  var spectrePreset = {
50
43
  content: [],
51
- theme: theme ?? {}
52
- // ensure theme is never undefined
44
+ theme
45
+ // theme is guaranteed non-undefined now
53
46
  };
54
47
 
55
48
  // src/recipes/button.ts
@@ -57,19 +50,18 @@ function getButtonClasses(opts = {}) {
57
50
  const {
58
51
  variant = "primary",
59
52
  size = "md",
60
- tone = "default",
61
53
  fullWidth = false,
62
54
  loading = false,
63
55
  disabled = false,
64
56
  iconOnly = false
65
57
  } = opts;
66
- const classes = [];
67
- classes.push("sp-btn");
58
+ const classes = ["sp-btn"];
68
59
  const variantMap = {
69
60
  primary: "sp-btn--primary",
70
61
  secondary: "sp-btn--secondary",
71
62
  ghost: "sp-btn--ghost",
72
- danger: "sp-btn--danger"
63
+ danger: "sp-btn--danger",
64
+ success: "sp-btn--success"
73
65
  };
74
66
  classes.push(variantMap[variant]);
75
67
  const sizeMap = {
@@ -78,19 +70,11 @@ function getButtonClasses(opts = {}) {
78
70
  lg: "sp-btn--lg"
79
71
  };
80
72
  classes.push(sizeMap[size]);
81
- if (tone !== "default") {
82
- const toneMap = {
83
- success: "sp-btn--tone-success",
84
- warning: "sp-btn--tone-warning",
85
- danger: "sp-btn--tone-danger"
86
- };
87
- classes.push(toneMap[tone]);
88
- }
89
73
  if (fullWidth) classes.push("sp-btn--full");
90
74
  if (loading) classes.push("sp-btn--loading");
91
75
  if (disabled) classes.push("sp-btn--disabled");
92
76
  if (iconOnly) classes.push("sp-btn--icon");
93
- return classes.filter(Boolean).join(" ").trim();
77
+ return classes.join(" ").trim();
94
78
  }
95
79
 
96
80
  // src/recipes/card.ts
@@ -101,10 +85,10 @@ function getCardClasses(opts = {}) {
101
85
  padded = false,
102
86
  fullHeight = false
103
87
  } = opts;
104
- const classes = [];
105
- classes.push("sp-card");
88
+ const classes = ["sp-card"];
106
89
  const variantMap = {
107
90
  elevated: "sp-card--elevated",
91
+ flat: "sp-card--flat",
108
92
  outline: "sp-card--outline",
109
93
  ghost: "sp-card--ghost"
110
94
  };
@@ -112,33 +96,24 @@ function getCardClasses(opts = {}) {
112
96
  if (interactive) classes.push("sp-card--interactive");
113
97
  if (padded) classes.push("sp-card--padded");
114
98
  if (fullHeight) classes.push("sp-card--full");
115
- return classes.filter(Boolean).join(" ").trim();
99
+ return classes.join(" ").trim();
116
100
  }
117
101
 
118
102
  // src/recipes/input.ts
119
103
  function getInputClasses(opts = {}) {
120
- const {
121
- state = "default",
122
- size = "md",
123
- fullWidth = false
124
- } = opts;
125
- const classes = [];
126
- classes.push("sp-input");
127
- if (state === "error") {
128
- classes.push("sp-input--error");
129
- } else if (state === "success") {
130
- classes.push("sp-input--success");
131
- }
104
+ const { state = "default", size = "md", fullWidth = false } = opts;
105
+ const classes = ["sp-input"];
132
106
  const sizeMap = {
133
107
  sm: "sp-input--sm",
134
108
  md: "sp-input--md",
135
109
  lg: "sp-input--lg"
136
110
  };
137
111
  classes.push(sizeMap[size]);
138
- if (fullWidth) {
139
- classes.push("sp-input--full");
140
- }
141
- return classes.filter(Boolean).join(" ").trim();
112
+ if (state === "error") classes.push("sp-input--error");
113
+ if (state === "success") classes.push("sp-input--success");
114
+ if (state === "disabled") classes.push("sp-input--disabled");
115
+ if (fullWidth) classes.push("sp-input--full");
116
+ return classes.join(" ").trim();
142
117
  }
143
118
 
144
119
  export { createSpectreTailwindTheme, getButtonClasses, getCardClasses, getInputClasses, spectreBaseStylesPath, spectreComponentsStylesPath, spectrePreset, spectreStyles, spectreUtilitiesStylesPath };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/css-constants.ts","../src/tailwind/theme.ts","../src/tailwind/preset.ts","../src/recipes/button.ts","../src/recipes/card.ts","../src/recipes/input.ts"],"names":["theme","spectreTokens"],"mappings":"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","file":"index.js","sourcesContent":["export const spectreBaseStylesPath = \"@phcdevworks/spectre-ui/dist/base.css\";\nexport const spectreComponentsStylesPath = \"@phcdevworks/spectre-ui/dist/components.css\";\nexport const spectreUtilitiesStylesPath = \"@phcdevworks/spectre-ui/dist/utilities.css\";\n\nexport const spectreStyles = {\n base: spectreBaseStylesPath,\n components: spectreComponentsStylesPath,\n utilities: spectreUtilitiesStylesPath,\n};\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport type { SpectreTokens } from \"../tokens\";\n\nexport interface SpectreTailwindTheme {\n theme: TailwindConfig[\"theme\"];\n}\n\nexport interface CreateSpectreTailwindThemeOptions {\n tokens: SpectreTokens;\n overrides?: Partial<SpectreTokens>;\n}\n\nexport function createSpectreTailwindTheme(\n options: CreateSpectreTailwindThemeOptions,\n): SpectreTailwindTheme {\n const { tokens, overrides } = options;\n\n // Shallow merge overrides into tokens\n const mergedTokens: SpectreTokens = {\n ...(tokens as SpectreTokens),\n ...(overrides as Partial<SpectreTokens> | undefined),\n };\n\n // Minimal, semantic color mapping\n const colors: Record<string, unknown> = {\n page: mergedTokens.surface?.page,\n card: mergedTokens.surface?.card,\n input: mergedTokens.surface?.input,\n text: {\n page: mergedTokens.text?.onPage?.default,\n \"page-muted\": mergedTokens.text?.onPage?.muted,\n surface: mergedTokens.text?.onSurface?.default,\n \"surface-muted\": mergedTokens.text?.onSurface?.muted,\n },\n primary:\n (mergedTokens as any).buttons?.primary?.bg ??\n (mergedTokens as any).colors?.primary,\n };\n\n const spacing: Record<string, unknown> =\n (mergedTokens as any).spacing ?? {};\n\n const borderRadius: Record<string, unknown> =\n (mergedTokens as any).radii ?? {};\n\n const boxShadow: Record<string, unknown> =\n (mergedTokens as any).shadows ?? {};\n\n const fontFamily: Record<string, unknown> =\n (mergedTokens as any).typography?.families ?? {};\n\n const theme: TailwindConfig[\"theme\"] = {\n colors: colors as any,\n spacing: spacing as any,\n borderRadius: borderRadius as any,\n boxShadow: boxShadow as any,\n fontFamily: fontFamily as any,\n };\n\n return { theme };\n}\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport { spectreTokens } from \"../tokens\";\nimport { createSpectreTailwindTheme } from \"./theme\";\n\nconst { theme } = createSpectreTailwindTheme({ tokens: spectreTokens });\n\nexport const spectrePreset: TailwindConfig = {\n content: [],\n theme: theme ?? {}, // ensure theme is never undefined\n};\n\nexport default spectrePreset;\n","export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonTone = 'default' | 'success' | 'warning' | 'danger';\n\nexport interface ButtonRecipeOptions {\n variant?: ButtonVariant;\n size?: ButtonSize;\n tone?: ButtonTone;\n fullWidth?: boolean;\n loading?: boolean;\n disabled?: boolean;\n iconOnly?: boolean;\n}\n\n/**\n * Generate Spectre button classes.\n *\n * Rules:\n * - Base: \"sp-btn\"\n * - Variant: \"sp-btn--primary\" / \"sp-btn--secondary\" / \"sp-btn--ghost\" / \"sp-btn--danger\"\n * - default variant is \"primary\"\n * - Size: \"sp-btn--sm\" / \"sp-btn--md\" / \"sp-btn--lg\"\n * - default size is \"md\"\n * - Tone: \"sp-btn--tone-success\" / \"sp-btn--tone-warning\" / \"sp-btn--tone-danger\"\n * - default tone is \"default\" (no tone class)\n * - fullWidth: add \"sp-btn--full\"\n * - loading: add \"sp-btn--loading\"\n * - disabled: add \"sp-btn--disabled\"\n * - iconOnly: add \"sp-btn--icon\"\n *\n * Must return a single space-joined, trimmed class string.\n */\nexport function getButtonClasses(opts: ButtonRecipeOptions = {}): string {\n const {\n variant = 'primary',\n size = 'md',\n tone = 'default',\n fullWidth = false,\n loading = false,\n disabled = false,\n iconOnly = false,\n } = opts;\n\n const classes: string[] = [];\n\n // Base\n classes.push('sp-btn');\n\n // Variant\n const variantMap: Record<ButtonVariant, string> = {\n primary: 'sp-btn--primary',\n secondary: 'sp-btn--secondary',\n ghost: 'sp-btn--ghost',\n danger: 'sp-btn--danger',\n };\n classes.push(variantMap[variant]);\n\n // Size\n const sizeMap: Record<ButtonSize, string> = {\n sm: 'sp-btn--sm',\n md: 'sp-btn--md',\n lg: 'sp-btn--lg',\n };\n classes.push(sizeMap[size]);\n\n // Tone (optional)\n if (tone !== 'default') {\n const toneMap: Record<Exclude<ButtonTone, 'default'>, string> = {\n success: 'sp-btn--tone-success',\n warning: 'sp-btn--tone-warning',\n danger: 'sp-btn--tone-danger',\n };\n classes.push(toneMap[tone as Exclude<ButtonTone, 'default'>]);\n }\n\n // Flags\n if (fullWidth) classes.push('sp-btn--full');\n if (loading) classes.push('sp-btn--loading');\n if (disabled) classes.push('sp-btn--disabled');\n if (iconOnly) classes.push('sp-btn--icon');\n\n // Final class string\n return classes.filter(Boolean).join(' ').trim();\n}\n","export type CardVariant = 'elevated' | 'outline' | 'ghost';\n\nexport interface CardRecipeOptions {\n variant?: CardVariant;\n interactive?: boolean; // hover/focus styles\n padded?: boolean; // apply default padding\n fullHeight?: boolean;\n}\n\n/**\n * Generate Spectre card classes.\n *\n * Rules:\n * - Base class: \"sp-card\"\n * - Variant (default: elevated):\n * - \"sp-card--elevated\"\n * - \"sp-card--outline\"\n * - \"sp-card--ghost\"\n * - interactive: add \"sp-card--interactive\"\n * - padded: add \"sp-card--padded\"\n * - fullHeight: add \"sp-card--full\"\n */\nexport function getCardClasses(opts: CardRecipeOptions = {}): string {\n const {\n variant = 'elevated',\n interactive = false,\n padded = false,\n fullHeight = false,\n } = opts;\n\n const classes: string[] = [];\n\n // Base\n classes.push('sp-card');\n\n // Variant\n const variantMap: Record<CardVariant, string> = {\n elevated: 'sp-card--elevated',\n outline: 'sp-card--outline',\n ghost: 'sp-card--ghost',\n };\n classes.push(variantMap[variant]);\n\n // Flags\n if (interactive) classes.push('sp-card--interactive');\n if (padded) classes.push('sp-card--padded');\n if (fullHeight) classes.push('sp-card--full');\n\n return classes.filter(Boolean).join(' ').trim();\n}\n","export type InputState = 'default' | 'error' | 'success';\nexport type InputSize = 'sm' | 'md' | 'lg';\n\nexport interface InputRecipeOptions {\n state?: InputState;\n size?: InputSize;\n fullWidth?: boolean;\n}\n\n/**\n * Generate Spectre input classes.\n *\n * Rules:\n * - Base class: \"sp-input\"\n * - State:\n * - \"default\" => no state modifier\n * - \"error\" => \"sp-input--error\"\n * - \"success\" => \"sp-input--success\"\n * - Size (default: md):\n * - \"sp-input--sm\"\n * - \"sp-input--md\"\n * - \"sp-input--lg\"\n * - fullWidth: add \"sp-input--full\"\n */\nexport function getInputClasses(opts: InputRecipeOptions = {}): string {\n const {\n state = 'default',\n size = 'md',\n fullWidth = false,\n } = opts;\n\n const classes: string[] = [];\n\n // Base\n classes.push('sp-input');\n\n // State\n if (state === 'error') {\n classes.push('sp-input--error');\n } else if (state === 'success') {\n classes.push('sp-input--success');\n }\n\n // Size\n const sizeMap: Record<InputSize, string> = {\n sm: 'sp-input--sm',\n md: 'sp-input--md',\n lg: 'sp-input--lg',\n };\n classes.push(sizeMap[size]);\n\n // Flags\n if (fullWidth) {\n classes.push('sp-input--full');\n }\n\n return classes.filter(Boolean).join(' ').trim();\n}\n"]}
1
+ {"version":3,"sources":["../src/css-constants.ts","../src/tailwind/theme.ts","../src/tailwind/preset.ts","../src/recipes/button.ts","../src/recipes/card.ts","../src/recipes/input.ts"],"names":["theme"],"mappings":";;;;AAAO,IAAM,qBAAA,GAAwB;AAC9B,IAAM,2BAAA,GAA8B;AACpC,IAAM,0BAAA,GAA6B;AACnC,IAAM,sBAAA,GAAyB,mCAAA;AAE/B,IAAM,aAAA,GAAgB;AAAA,EAC3B,KAAA,EAAO,sBAAA;AAAA,EACP,IAAA,EAAM,qBAAA;AAAA,EACN,UAAA,EAAY,2BAAA;AAAA,EACZ,SAAA,EAAW;AACb;;;ACQO,SAAS,2BACd,OAAA,EACsB;AACtB,EAAA,MAAM,YAAA,GAAe;AAAA,IACnB,GAAI,OAAA,CAAQ,MAAA,IAAW,EAAC;AAAA,IACxB,GAAI,OAAA,CAAQ,SAAA,IAAa;AAAC,GAC5B;AAIA,EAAA,MAAM,CAAA,GAAS,YAAA;AAEf,EAAA,MAAM,SAAU,CAAA,CAAE,MAAA,IAAU,EAAE,KAAA,IAAS,CAAA,CAAE,WAAW,EAAC;AACrD,EAAA,MAAM,OAAA,GAAW,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,SAAS,EAAC;AAC1C,EAAA,MAAM,YAAA,GAAgB,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,UAAU,EAAC;AAC9C,EAAA,MAAM,SAAA,GAAa,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,UAAU,EAAC;AAC7C,EAAA,MAAM,aAAc,CAAA,CAAE,UAAA,EAAY,QAAA,IAAY,CAAA,CAAE,SAAS,EAAC;AAC1D,EAAA,MAAM,WAAY,CAAA,CAAE,UAAA,EAAY,KAAA,IAAS,CAAA,CAAE,YAAY,EAAC;AAExD,EAAA,MAAMA,MAAAA,GAAuB;AAAA,IAC3B,MAAA;AAAA,IACA,OAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,EAAE,OAAAA,MAAAA,EAAM;AACjB;;;AC3CA,IAAM,EAAE,KAAA,EAAM,GAAI,2BAA2B,EAAE,MAAA,EAAQ,eAAe,CAAA;AAE/D,IAAM,aAAA,GAAgC;AAAA,EAC3C,SAAS,EAAC;AAAA,EACV;AAAA;AACF;;;ACGO,SAAS,gBAAA,CAAiB,IAAA,GAA4B,EAAC,EAAW;AACvE,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,SAAA;AAAA,IACV,IAAA,GAAO,IAAA;AAAA,IACP,SAAA,GAAY,KAAA;AAAA,IACZ,OAAA,GAAU,KAAA;AAAA,IACV,QAAA,GAAW,KAAA;AAAA,IACX,QAAA,GAAW;AAAA,GACb,GAAI,IAAA;AAEJ,EAAA,MAAM,OAAA,GAAoB,CAAC,QAAQ,CAAA;AAEnC,EAAA,MAAM,UAAA,GAA4C;AAAA,IAChD,OAAA,EAAS,iBAAA;AAAA,IACT,SAAA,EAAW,mBAAA;AAAA,IACX,KAAA,EAAO,eAAA;AAAA,IACP,MAAA,EAAQ,gBAAA;AAAA,IACR,OAAA,EAAS;AAAA,GACX;AACA,EAAA,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,OAAO,CAAC,CAAA;AAEhC,EAAA,MAAM,OAAA,GAAsC;AAAA,IAC1C,EAAA,EAAI,YAAA;AAAA,IACJ,EAAA,EAAI,YAAA;AAAA,IACJ,EAAA,EAAI;AAAA,GACN;AACA,EAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAC,CAAA;AAE1B,EAAA,IAAI,SAAA,EAAW,OAAA,CAAQ,IAAA,CAAK,cAAc,CAAA;AAC1C,EAAA,IAAI,OAAA,EAAS,OAAA,CAAQ,IAAA,CAAK,iBAAiB,CAAA;AAC3C,EAAA,IAAI,QAAA,EAAU,OAAA,CAAQ,IAAA,CAAK,kBAAkB,CAAA;AAC7C,EAAA,IAAI,QAAA,EAAU,OAAA,CAAQ,IAAA,CAAK,cAAc,CAAA;AAEzC,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,EAAK;AAChC;;;ACrCO,SAAS,cAAA,CAAe,IAAA,GAA0B,EAAC,EAAW;AACnE,EAAA,MAAM;AAAA,IACJ,OAAA,GAAU,UAAA;AAAA,IACV,WAAA,GAAc,KAAA;AAAA,IACd,MAAA,GAAS,KAAA;AAAA,IACT,UAAA,GAAa;AAAA,GACf,GAAI,IAAA;AAEJ,EAAA,MAAM,OAAA,GAAoB,CAAC,SAAS,CAAA;AAEpC,EAAA,MAAM,UAAA,GAA0C;AAAA,IAC9C,QAAA,EAAU,mBAAA;AAAA,IACV,IAAA,EAAM,eAAA;AAAA,IACN,OAAA,EAAS,kBAAA;AAAA,IACT,KAAA,EAAO;AAAA,GACT;AACA,EAAA,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,OAAO,CAAC,CAAA;AAEhC,EAAA,IAAI,WAAA,EAAa,OAAA,CAAQ,IAAA,CAAK,sBAAsB,CAAA;AACpD,EAAA,IAAI,MAAA,EAAQ,OAAA,CAAQ,IAAA,CAAK,iBAAiB,CAAA;AAC1C,EAAA,IAAI,UAAA,EAAY,OAAA,CAAQ,IAAA,CAAK,eAAe,CAAA;AAE5C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,EAAK;AAChC;;;ACvBO,SAAS,eAAA,CAAgB,IAAA,GAA2B,EAAC,EAAW;AACrE,EAAA,MAAM,EAAE,KAAA,GAAQ,SAAA,EAAW,OAAO,IAAA,EAAM,SAAA,GAAY,OAAM,GAAI,IAAA;AAE9D,EAAA,MAAM,OAAA,GAAoB,CAAC,UAAU,CAAA;AAErC,EAAA,MAAM,OAAA,GAAqC;AAAA,IACzC,EAAA,EAAI,cAAA;AAAA,IACJ,EAAA,EAAI,cAAA;AAAA,IACJ,EAAA,EAAI;AAAA,GACN;AACA,EAAA,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAC,CAAA;AAG1B,EAAA,IAAI,KAAA,KAAU,OAAA,EAAS,OAAA,CAAQ,IAAA,CAAK,iBAAiB,CAAA;AACrD,EAAA,IAAI,KAAA,KAAU,SAAA,EAAW,OAAA,CAAQ,IAAA,CAAK,mBAAmB,CAAA;AACzD,EAAA,IAAI,KAAA,KAAU,UAAA,EAAY,OAAA,CAAQ,IAAA,CAAK,oBAAoB,CAAA;AAE3D,EAAA,IAAI,SAAA,EAAW,OAAA,CAAQ,IAAA,CAAK,gBAAgB,CAAA;AAE5C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,EAAK;AAChC","file":"index.js","sourcesContent":["export const spectreBaseStylesPath = \"@phcdevworks/spectre-ui/base.css\";\nexport const spectreComponentsStylesPath = \"@phcdevworks/spectre-ui/components.css\";\nexport const spectreUtilitiesStylesPath = \"@phcdevworks/spectre-ui/utilities.css\";\nexport const spectreIndexStylesPath = \"@phcdevworks/spectre-ui/index.css\";\n\nexport const spectreStyles = {\n index: spectreIndexStylesPath,\n base: spectreBaseStylesPath,\n components: spectreComponentsStylesPath,\n utilities: spectreUtilitiesStylesPath,\n};\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport type { SpectreTokens } from \"../tokens\";\n\ntype TailwindTheme = NonNullable<TailwindConfig[\"theme\"]>;\n\nexport interface SpectreTailwindTheme {\n theme: TailwindTheme;\n}\n\nexport interface CreateSpectreTailwindThemeOptions {\n tokens: SpectreTokens;\n overrides?: Partial<SpectreTokens>;\n}\n\n/**\n * Minimal, type-safe theme mapper.\n * Important: theme is NEVER undefined (fixes exactOptionalPropertyTypes + DTS).\n */\nexport function createSpectreTailwindTheme(\n options: CreateSpectreTailwindThemeOptions\n): SpectreTailwindTheme {\n const mergedTokens = {\n ...(options.tokens ?? ({} as SpectreTokens)),\n ...(options.overrides ?? {}),\n } as SpectreTokens;\n\n // We keep mapping shallow + permissive because token shapes will evolve.\n // Tailwind accepts nested objects of strings for colors.\n const t: any = mergedTokens;\n\n const colors = (t.colors ?? t.color ?? t.palette ?? {}) as Record<string, any>;\n const spacing = (t.spacing ?? t.space ?? {}) as Record<string, any>;\n const borderRadius = (t.radii ?? t.radius ?? {}) as Record<string, any>;\n const boxShadow = (t.shadows ?? t.shadow ?? {}) as Record<string, any>;\n const fontFamily = (t.typography?.families ?? t.fonts ?? {}) as Record<string, any>;\n const fontSize = (t.typography?.scale ?? t.fontSize ?? {}) as Record<string, any>;\n\n const theme: TailwindTheme = {\n colors: colors as any,\n spacing: spacing as any,\n borderRadius: borderRadius as any,\n boxShadow: boxShadow as any,\n fontFamily: fontFamily as any,\n fontSize: fontSize as any,\n };\n\n return { theme };\n}\n","import type { Config as TailwindConfig } from \"tailwindcss\";\nimport { spectreTokens } from \"../tokens\";\nimport { createSpectreTailwindTheme } from \"./theme\";\n\nconst { theme } = createSpectreTailwindTheme({ tokens: spectreTokens });\n\nexport const spectrePreset: TailwindConfig = {\n content: [],\n theme, // theme is guaranteed non-undefined now\n};\n\nexport const spectreTailwindPreset: TailwindConfig = spectrePreset;\n","export type ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"danger\" | \"success\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonRecipeOptions {\n variant?: ButtonVariant;\n size?: ButtonSize;\n fullWidth?: boolean;\n loading?: boolean;\n disabled?: boolean;\n iconOnly?: boolean;\n}\n\nexport function getButtonClasses(opts: ButtonRecipeOptions = {}): string {\n const {\n variant = \"primary\",\n size = \"md\",\n fullWidth = false,\n loading = false,\n disabled = false,\n iconOnly = false,\n } = opts;\n\n const classes: string[] = [\"sp-btn\"];\n\n const variantMap: Record<ButtonVariant, string> = {\n primary: \"sp-btn--primary\",\n secondary: \"sp-btn--secondary\",\n ghost: \"sp-btn--ghost\",\n danger: \"sp-btn--danger\",\n success: \"sp-btn--success\",\n };\n classes.push(variantMap[variant]);\n\n const sizeMap: Record<ButtonSize, string> = {\n sm: \"sp-btn--sm\",\n md: \"sp-btn--md\",\n lg: \"sp-btn--lg\",\n };\n classes.push(sizeMap[size]);\n\n if (fullWidth) classes.push(\"sp-btn--full\");\n if (loading) classes.push(\"sp-btn--loading\");\n if (disabled) classes.push(\"sp-btn--disabled\");\n if (iconOnly) classes.push(\"sp-btn--icon\");\n\n return classes.join(\" \").trim();\n}\n","export type CardVariant = \"elevated\" | \"flat\" | \"outline\" | \"ghost\";\n\nexport interface CardRecipeOptions {\n variant?: CardVariant;\n interactive?: boolean; // hover/focus styles\n padded?: boolean; // apply default padding\n fullHeight?: boolean;\n}\n\nexport function getCardClasses(opts: CardRecipeOptions = {}): string {\n const {\n variant = \"elevated\",\n interactive = false,\n padded = false,\n fullHeight = false,\n } = opts;\n\n const classes: string[] = [\"sp-card\"];\n\n const variantMap: Record<CardVariant, string> = {\n elevated: \"sp-card--elevated\",\n flat: \"sp-card--flat\",\n outline: \"sp-card--outline\",\n ghost: \"sp-card--ghost\",\n };\n classes.push(variantMap[variant]);\n\n if (interactive) classes.push(\"sp-card--interactive\");\n if (padded) classes.push(\"sp-card--padded\");\n if (fullHeight) classes.push(\"sp-card--full\");\n\n return classes.join(\" \").trim();\n}\n","export type InputState = \"default\" | \"error\" | \"success\" | \"disabled\";\nexport type InputSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface InputRecipeOptions {\n state?: InputState;\n size?: InputSize;\n fullWidth?: boolean;\n}\n\nexport function getInputClasses(opts: InputRecipeOptions = {}): string {\n const { state = \"default\", size = \"md\", fullWidth = false } = opts;\n\n const classes: string[] = [\"sp-input\"];\n\n const sizeMap: Record<InputSize, string> = {\n sm: \"sp-input--sm\",\n md: \"sp-input--md\",\n lg: \"sp-input--lg\",\n };\n classes.push(sizeMap[size]);\n\n // State\n if (state === \"error\") classes.push(\"sp-input--error\");\n if (state === \"success\") classes.push(\"sp-input--success\");\n if (state === \"disabled\") classes.push(\"sp-input--disabled\");\n\n if (fullWidth) classes.push(\"sp-input--full\");\n\n return classes.join(\" \").trim();\n}\n"]}
@@ -18,7 +18,8 @@
18
18
  margin-right: auto;
19
19
  padding-left: var(--sp-space-md, 1rem);
20
20
  padding-right: var(--sp-space-md, 1rem);
21
- max-width: var(--sp-container-max-width, 72rem);
21
+ max-width: var(--sp-container-max-width,
22
+ var(--sp-breakpoint-2xl, 72rem));
22
23
  }
23
24
 
24
25
  .sp-section {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phcdevworks/spectre-ui",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "description": "Framework-agnostic UI layer for the Spectre design system. Provides base CSS, component classes, utilities, and a Tailwind preset powered by @phcdevworks/spectre-tokens.",
5
5
  "keywords": [
6
6
  "phcdevworks",
@@ -47,6 +47,7 @@
47
47
  "import": "./dist/index.js",
48
48
  "require": "./dist/index.cjs"
49
49
  },
50
+ "./index.css": "./dist/index.css",
50
51
  "./base.css": "./dist/base.css",
51
52
  "./components.css": "./dist/components.css",
52
53
  "./utilities.css": "./dist/utilities.css"
@@ -70,7 +71,7 @@
70
71
  "tailwindcss": "^3.4.0 || ^4.0.0"
71
72
  },
72
73
  "dependencies": {
73
- "@phcdevworks/spectre-tokens": "^0.0.4"
74
+ "@phcdevworks/spectre-tokens": "^0.1.0"
74
75
  },
75
76
  "devDependencies": {
76
77
  "autoprefixer": "^10.4.20",
@@ -78,6 +79,6 @@
78
79
  "tailwindcss": "^3.4.15",
79
80
  "tsup": "^8.5.1",
80
81
  "typescript": "^5.9.3",
81
- "vitest": "^2.1.4"
82
+ "vitest": "^4.0.15"
82
83
  }
83
84
  }