@phcdevworks/spectre-components 1.2.0 → 1.4.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 +359 -199
- package/dist/badge.cjs +450 -0
- package/dist/badge.cjs.map +1 -0
- package/dist/badge.d.cts +61 -0
- package/dist/badge.d.ts +61 -0
- package/dist/badge.js +445 -0
- package/dist/badge.js.map +1 -0
- package/dist/{base-B_9DJkh2.d.cts → base-BRQ7QAAr.d.cts} +3 -0
- package/dist/{base-B_9DJkh2.d.ts → base-BRQ7QAAr.d.ts} +3 -0
- package/dist/button.cjs +53 -4
- package/dist/button.cjs.map +1 -1
- package/dist/button.d.cts +12 -15
- package/dist/button.d.ts +12 -15
- package/dist/button.js +54 -5
- package/dist/button.js.map +1 -1
- package/dist/card.cjs +439 -0
- package/dist/card.cjs.map +1 -0
- package/dist/card.d.cts +67 -0
- package/dist/card.d.ts +67 -0
- package/dist/card.js +435 -0
- package/dist/card.js.map +1 -0
- package/dist/checkbox.cjs +48 -2
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.d.cts +11 -14
- package/dist/checkbox.d.ts +11 -14
- package/dist/checkbox.js +48 -2
- package/dist/checkbox.js.map +1 -1
- package/dist/fieldset.cjs +45 -0
- package/dist/fieldset.cjs.map +1 -1
- package/dist/fieldset.d.cts +11 -2
- package/dist/fieldset.d.ts +11 -2
- package/dist/fieldset.js +45 -0
- package/dist/fieldset.js.map +1 -1
- package/dist/form-Dg6kuOS7.d.cts +18 -0
- package/dist/form-Dg6kuOS7.d.ts +18 -0
- package/dist/icon-box.cjs +462 -0
- package/dist/icon-box.cjs.map +1 -0
- package/dist/icon-box.d.cts +73 -0
- package/dist/icon-box.d.ts +73 -0
- package/dist/icon-box.js +457 -0
- package/dist/icon-box.js.map +1 -0
- package/dist/index.cjs +754 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +739 -28
- package/dist/index.js.map +1 -1
- package/dist/input.cjs +55 -7
- package/dist/input.cjs.map +1 -1
- package/dist/input.d.cts +16 -27
- package/dist/input.d.ts +16 -27
- package/dist/input.js +55 -7
- package/dist/input.js.map +1 -1
- package/dist/label.cjs +29 -0
- package/dist/label.cjs.map +1 -1
- package/dist/label.d.cts +4 -3
- package/dist/label.d.ts +4 -3
- package/dist/label.js +29 -0
- package/dist/label.js.map +1 -1
- package/dist/{projectable-CIfxi7dv.d.cts → projectable-Bx-IVk0w.d.cts} +1 -1
- package/dist/{projectable-Ksv9eoIr.d.ts → projectable-D6IDeKfr.d.ts} +1 -1
- package/dist/radio.cjs +50 -4
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.d.cts +11 -14
- package/dist/radio.d.ts +11 -14
- package/dist/radio.js +50 -4
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +373 -0
- package/dist/rating.cjs.map +1 -0
- package/dist/rating.d.cts +67 -0
- package/dist/rating.d.ts +67 -0
- package/dist/rating.js +369 -0
- package/dist/rating.js.map +1 -0
- package/dist/select.cjs +65 -2
- package/dist/select.cjs.map +1 -1
- package/dist/select.d.cts +19 -16
- package/dist/select.d.ts +19 -16
- package/dist/select.js +65 -2
- package/dist/select.js.map +1 -1
- package/dist/testimonial.cjs +433 -0
- package/dist/testimonial.cjs.map +1 -0
- package/dist/testimonial.d.cts +61 -0
- package/dist/testimonial.d.ts +61 -0
- package/dist/testimonial.js +429 -0
- package/dist/testimonial.js.map +1 -0
- package/dist/textarea.cjs +55 -7
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.d.cts +15 -26
- package/dist/textarea.d.ts +15 -26
- package/dist/textarea.js +55 -7
- package/dist/textarea.js.map +1 -1
- package/package.json +45 -15
- package/dist/form-9cZG1E9P.d.cts +0 -10
- package/dist/form-9cZG1E9P.d.ts +0 -10
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
`@phcdevworks/spectre-components` is the **Layer 3** Lit-based web component
|
|
9
9
|
package of the Spectre design system. It turns Spectre tokens
|
|
10
10
|
(`@phcdevworks/spectre-tokens`) and Spectre UI styling contracts
|
|
11
|
-
(`@phcdevworks/spectre-ui`) into reusable, accessible, framework-agnostic
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
(`@phcdevworks/spectre-ui`) into reusable, accessible, framework-agnostic custom
|
|
12
|
+
elements — the canonical component implementation layer for Spectre, designed to
|
|
13
|
+
be consumed directly or wrapped by downstream adapter packages.
|
|
14
14
|
|
|
15
15
|
[Contributing](CONTRIBUTING.md) | [Code of Conduct](CODE_OF_CONDUCT.md) |
|
|
16
16
|
[Changelog](CHANGELOG.md) | [Roadmap](ROADMAP.md) |
|
|
@@ -22,23 +22,23 @@ designed to be consumed directly or wrapped by downstream adapter packages.
|
|
|
22
22
|
styling contract that maps Spectre tokens to visual output. It ships CSS rules
|
|
23
23
|
and JavaScript class-name helpers — nothing more.
|
|
24
24
|
|
|
25
|
-
This package sits above that. It owns **behavior**: the Lit element classes
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
This package sits above that. It owns **behavior**: the Lit element classes that
|
|
26
|
+
apply those CSS recipes, forward ARIA attributes to native elements, manage
|
|
27
|
+
focus delegation, handle content projection, validate properties, and expose a
|
|
28
|
+
stable TypeScript API surface for downstream adapters.
|
|
29
29
|
|
|
30
30
|
The separation keeps each layer focused:
|
|
31
31
|
|
|
32
|
-
| Layer
|
|
33
|
-
|
|
34
|
-
| L1
|
|
35
|
-
| L2
|
|
32
|
+
| Layer | Package | Owns |
|
|
33
|
+
| ------ | ------------------------------------- | -------------------------------------- |
|
|
34
|
+
| L1 | `@phcdevworks/spectre-tokens` | Design values and semantic meaning |
|
|
35
|
+
| L2 | `@phcdevworks/spectre-ui` | CSS recipes and styling contracts |
|
|
36
36
|
| **L3** | **`@phcdevworks/spectre-components`** | **Lit web component behavior and API** |
|
|
37
|
-
| L4
|
|
37
|
+
| L4 | Downstream adapters | Framework-specific delivery |
|
|
38
38
|
|
|
39
|
-
If you only need CSS class names, use `@phcdevworks/spectre-ui` directly. If
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
If you only need CSS class names, use `@phcdevworks/spectre-ui` directly. If you
|
|
40
|
+
need ready-to-use HTML elements with behavior, accessibility, and a typed API,
|
|
41
|
+
use this package.
|
|
42
42
|
|
|
43
43
|
## Key capabilities
|
|
44
44
|
|
|
@@ -57,16 +57,17 @@ API, use this package.
|
|
|
57
57
|
|
|
58
58
|
- You are building UI with the Spectre design system and want standards-based
|
|
59
59
|
custom elements with baked-in behavior and accessibility.
|
|
60
|
-
- You want typed form controls (`sp-button`, `sp-input`, `sp-select`, etc.)
|
|
61
|
-
|
|
60
|
+
- You want typed form controls (`sp-button`, `sp-input`, `sp-select`, etc.) and
|
|
61
|
+
display primitives (`sp-badge`, `sp-card`, `sp-rating`, etc.) that work in any
|
|
62
|
+
framework or in plain HTML.
|
|
62
63
|
- You are writing a framework adapter (React, Vue, Astro) and need a reliable,
|
|
63
64
|
stable element layer to wrap.
|
|
64
65
|
|
|
65
66
|
## When not to use this package
|
|
66
67
|
|
|
67
68
|
- You only need CSS class names — use `@phcdevworks/spectre-ui` directly.
|
|
68
|
-
- You are adding routing, shell logic, or app-startup orchestration — those
|
|
69
|
-
|
|
69
|
+
- You are adding routing, shell logic, or app-startup orchestration — those are
|
|
70
|
+
out of scope here.
|
|
70
71
|
- You need framework-specific component files (JSX, SFCs, Astro components) —
|
|
71
72
|
those belong in a downstream adapter package.
|
|
72
73
|
|
|
@@ -89,19 +90,30 @@ consumption.
|
|
|
89
90
|
<html lang="en">
|
|
90
91
|
<head>
|
|
91
92
|
<!-- Spectre CSS layers must load before any markup is rendered -->
|
|
92
|
-
<link
|
|
93
|
-
|
|
93
|
+
<link
|
|
94
|
+
rel="stylesheet"
|
|
95
|
+
href="/node_modules/@phcdevworks/spectre-tokens/index.css"
|
|
96
|
+
/>
|
|
97
|
+
<link
|
|
98
|
+
rel="stylesheet"
|
|
99
|
+
href="/node_modules/@phcdevworks/spectre-ui/index.css"
|
|
100
|
+
/>
|
|
94
101
|
</head>
|
|
95
102
|
<body>
|
|
96
103
|
<sp-label for="email">Email address</sp-label>
|
|
97
|
-
<sp-input
|
|
104
|
+
<sp-input
|
|
105
|
+
id="email"
|
|
106
|
+
name="email"
|
|
107
|
+
type="email"
|
|
108
|
+
placeholder="you@example.com"
|
|
109
|
+
></sp-input>
|
|
98
110
|
|
|
99
111
|
<sp-button variant="primary" type="submit">Send</sp-button>
|
|
100
112
|
<sp-button variant="ghost" type="button">Cancel</sp-button>
|
|
101
113
|
|
|
102
114
|
<script type="module">
|
|
103
|
-
import { defineSpectreComponents } from '/node_modules/@phcdevworks/spectre-components/dist/index.js'
|
|
104
|
-
defineSpectreComponents()
|
|
115
|
+
import { defineSpectreComponents } from '/node_modules/@phcdevworks/spectre-components/dist/index.js'
|
|
116
|
+
defineSpectreComponents()
|
|
105
117
|
</script>
|
|
106
118
|
</body>
|
|
107
119
|
</html>
|
|
@@ -110,18 +122,18 @@ consumption.
|
|
|
110
122
|
### JavaScript / TypeScript module
|
|
111
123
|
|
|
112
124
|
```ts
|
|
113
|
-
import '@phcdevworks/spectre-tokens/index.css'
|
|
114
|
-
import '@phcdevworks/spectre-ui/index.css'
|
|
125
|
+
import '@phcdevworks/spectre-tokens/index.css'
|
|
126
|
+
import '@phcdevworks/spectre-ui/index.css'
|
|
115
127
|
|
|
116
128
|
// Register everything at once
|
|
117
|
-
import { defineSpectreComponents } from '@phcdevworks/spectre-components'
|
|
118
|
-
defineSpectreComponents()
|
|
129
|
+
import { defineSpectreComponents } from '@phcdevworks/spectre-components'
|
|
130
|
+
defineSpectreComponents()
|
|
119
131
|
|
|
120
132
|
// Or register only what you use
|
|
121
|
-
import { defineSpectreButton } from '@phcdevworks/spectre-components/button'
|
|
122
|
-
import { defineSpectreInput } from '@phcdevworks/spectre-components/input'
|
|
123
|
-
defineSpectreButton()
|
|
124
|
-
defineSpectreInput()
|
|
133
|
+
import { defineSpectreButton } from '@phcdevworks/spectre-components/button'
|
|
134
|
+
import { defineSpectreInput } from '@phcdevworks/spectre-components/input'
|
|
135
|
+
defineSpectreButton()
|
|
136
|
+
defineSpectreInput()
|
|
125
137
|
```
|
|
126
138
|
|
|
127
139
|
### Full form example
|
|
@@ -154,10 +166,11 @@ defineSpectreInput();
|
|
|
154
166
|
|
|
155
167
|
### Framework integration note
|
|
156
168
|
|
|
157
|
-
These are standard HTML custom elements. They work in every major framework
|
|
158
|
-
|
|
169
|
+
These are standard HTML custom elements. They work in every major framework that
|
|
170
|
+
supports the Custom Elements standard:
|
|
159
171
|
|
|
160
172
|
**React 19+** — supports custom element properties and events natively:
|
|
173
|
+
|
|
161
174
|
```tsx
|
|
162
175
|
// React 19: properties and events work directly
|
|
163
176
|
<sp-input name="email" type="email" onInput={(e) => setValue(e.target.value)} />
|
|
@@ -165,27 +178,38 @@ that supports the Custom Elements standard:
|
|
|
165
178
|
|
|
166
179
|
**React 18 and below** — set attributes via `ref` for properties, listen for
|
|
167
180
|
native events on the element:
|
|
181
|
+
|
|
168
182
|
```tsx
|
|
169
|
-
const inputRef = useRef(null)
|
|
183
|
+
const inputRef = useRef(null)
|
|
170
184
|
useEffect(() => {
|
|
171
|
-
if (inputRef.current) inputRef.current.invalid = true
|
|
172
|
-
}, [])
|
|
173
|
-
|
|
185
|
+
if (inputRef.current) inputRef.current.invalid = true
|
|
186
|
+
}, [])
|
|
187
|
+
|
|
188
|
+
return <sp-input ref={inputRef} name="email" />
|
|
174
189
|
```
|
|
175
190
|
|
|
176
|
-
**Vue 3** — supports custom elements out of the box with `v-bind` and
|
|
177
|
-
|
|
178
|
-
|
|
191
|
+
**Vue 3** — supports custom elements out of the box with `v-bind` and `v-on`
|
|
192
|
+
directive compatibility. Mark the `sp-*` prefix in `compilerOptions` as a custom
|
|
193
|
+
element to suppress unknown-element warnings:
|
|
194
|
+
|
|
179
195
|
```ts
|
|
180
196
|
// vite.config.ts
|
|
181
|
-
plugins: [
|
|
197
|
+
plugins: [
|
|
198
|
+
vue({
|
|
199
|
+
template: {
|
|
200
|
+
compilerOptions: { isCustomElement: (tag) => tag.startsWith('sp-') }
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
]
|
|
182
204
|
```
|
|
205
|
+
|
|
183
206
|
```html
|
|
184
207
|
<sp-input name="email" :invalid="hasError" @change="handleChange" />
|
|
185
208
|
```
|
|
186
209
|
|
|
187
|
-
**Astro** — use components as static custom elements or with `client:load`
|
|
188
|
-
|
|
210
|
+
**Astro** — use components as static custom elements or with `client:load` when
|
|
211
|
+
JavaScript interactivity is needed:
|
|
212
|
+
|
|
189
213
|
```astro
|
|
190
214
|
---
|
|
191
215
|
import '@phcdevworks/spectre-tokens/index.css';
|
|
@@ -210,25 +234,25 @@ All components follow WCAG 2.1 AA baseline expectations by default.
|
|
|
210
234
|
inner native element so screen readers receive them on the correct target.
|
|
211
235
|
|
|
212
236
|
**Native element semantics** — every component renders a real native element
|
|
213
|
-
(`<button>`, `<input>`, `<textarea>`, `<select>`, `<label>`, `<fieldset>`)
|
|
214
|
-
|
|
237
|
+
(`<button>`, `<input>`, `<textarea>`, `<select>`, `<label>`, `<fieldset>`) or a
|
|
238
|
+
semantic light-DOM container with forwarded ARIA attributes, so browser
|
|
239
|
+
accessibility APIs work without customization.
|
|
215
240
|
|
|
216
241
|
**State communication**
|
|
217
242
|
|
|
218
|
-
| State
|
|
219
|
-
|
|
220
|
-
| `loading`
|
|
221
|
-
| `invalid`
|
|
243
|
+
| State | ARIA effect |
|
|
244
|
+
| ---------- | ---------------------------------------------------- |
|
|
245
|
+
| `loading` | `aria-busy="true"` on the native element |
|
|
246
|
+
| `invalid` | `aria-invalid="true"` on the native element |
|
|
222
247
|
| `disabled` | native `disabled` attribute (removes from tab order) |
|
|
223
|
-
| `required` | native `required` attribute
|
|
248
|
+
| `required` | native `required` attribute |
|
|
224
249
|
|
|
225
|
-
**Focus delegation** — `.focus()` and `.blur()` called on the host are
|
|
226
|
-
|
|
227
|
-
expected.
|
|
250
|
+
**Focus delegation** — `.focus()` and `.blur()` called on the host are delegated
|
|
251
|
+
to the inner native element so external `focus()` calls work as expected.
|
|
228
252
|
|
|
229
|
-
**Label association** — use `<sp-label for="id">` paired with `id` on the
|
|
230
|
-
|
|
231
|
-
|
|
253
|
+
**Label association** — use `<sp-label for="id">` paired with `id` on the target
|
|
254
|
+
control, or wrap controls inside a `<sp-fieldset>`. The `for` attribute forwards
|
|
255
|
+
to the native `<label>` element.
|
|
232
256
|
|
|
233
257
|
**Keyboard behavior** — provided entirely by the native element inside each
|
|
234
258
|
component. No custom keyboard handling is layered on top.
|
|
@@ -236,19 +260,23 @@ component. No custom keyboard handling is layered on top.
|
|
|
236
260
|
## Light DOM rendering
|
|
237
261
|
|
|
238
262
|
All components render in **light DOM** (`createRenderRoot() { return this; }`).
|
|
239
|
-
This is intentional: it allows `@phcdevworks/spectre-ui` global CSS to reach
|
|
240
|
-
|
|
263
|
+
This is intentional: it allows `@phcdevworks/spectre-ui` global CSS to reach the
|
|
264
|
+
native element directly without Shadow DOM piercing.
|
|
241
265
|
|
|
242
|
-
As a result, these components have no `::part()` exports — the native element
|
|
243
|
-
|
|
244
|
-
|
|
266
|
+
As a result, these components have no `::part()` exports — the native element is
|
|
267
|
+
directly selectable using standard CSS combinators or the stable internal data
|
|
268
|
+
attributes:
|
|
245
269
|
|
|
246
270
|
```css
|
|
247
271
|
/* Target the native input inside sp-input */
|
|
248
|
-
sp-input input {
|
|
272
|
+
sp-input input {
|
|
273
|
+
font-size: 0.875rem;
|
|
274
|
+
}
|
|
249
275
|
|
|
250
276
|
/* Stable internal hook — won't break if markup restructures */
|
|
251
|
-
sp-input [data-sp-input-native] {
|
|
277
|
+
sp-input [data-sp-input-native] {
|
|
278
|
+
font-size: 0.875rem;
|
|
279
|
+
}
|
|
252
280
|
```
|
|
253
281
|
|
|
254
282
|
Do not switch any component from light DOM to Shadow DOM without a
|
|
@@ -262,26 +290,26 @@ Renders a `<button>` with Spectre variant, size, loading, and pill support.
|
|
|
262
290
|
|
|
263
291
|
**Attributes**
|
|
264
292
|
|
|
265
|
-
| Attribute
|
|
266
|
-
|
|
267
|
-
| `variant`
|
|
268
|
-
| `size`
|
|
269
|
-
| `type`
|
|
270
|
-
| `label`
|
|
271
|
-
| `loading`
|
|
272
|
-
| `loading-label`
|
|
273
|
-
| `disabled`
|
|
274
|
-
| `full-width`
|
|
275
|
-
| `pill`
|
|
276
|
-
| `name`
|
|
277
|
-
| `value`
|
|
278
|
-
| `form`
|
|
279
|
-
| `autofocus`
|
|
280
|
-
| `id`
|
|
281
|
-
| `title`
|
|
282
|
-
| `aria-label`
|
|
283
|
-
| `aria-labelledby`
|
|
284
|
-
| `aria-describedby` | string
|
|
293
|
+
| Attribute | Type | Default | Description |
|
|
294
|
+
| ------------------ | --------------------------------------------------------------------- | --------- | -------------------------------------------------------- |
|
|
295
|
+
| `variant` | `primary \| secondary \| ghost \| danger \| success \| cta \| accent` | `primary` | Visual style |
|
|
296
|
+
| `size` | `sm \| md \| lg` | `md` | Control size |
|
|
297
|
+
| `type` | `button \| submit \| reset` | `button` | Native button type |
|
|
298
|
+
| `label` | string | — | Text label (overridden by content projection) |
|
|
299
|
+
| `loading` | boolean | `false` | Busy state — disables the button and shows loading label |
|
|
300
|
+
| `loading-label` | string | `Loading` | Accessible text shown during loading |
|
|
301
|
+
| `disabled` | boolean | `false` | Disables the button |
|
|
302
|
+
| `full-width` | boolean | `false` | Spans full container width |
|
|
303
|
+
| `pill` | boolean | `false` | Pill / fully-rounded corners |
|
|
304
|
+
| `name` | string | — | Form field name |
|
|
305
|
+
| `value` | string | `''` | Submitted value |
|
|
306
|
+
| `form` | string | — | Associates with a form by ID |
|
|
307
|
+
| `autofocus` | boolean | `false` | Autofocus on page load |
|
|
308
|
+
| `id` | string | — | Forwarded to the native `<button>` |
|
|
309
|
+
| `title` | string | — | Forwarded to the native `<button>` |
|
|
310
|
+
| `aria-label` | string | — | Forwarded to the native `<button>` |
|
|
311
|
+
| `aria-labelledby` | string | — | Forwarded to the native `<button>` |
|
|
312
|
+
| `aria-describedby` | string | — | Forwarded to the native `<button>` |
|
|
285
313
|
|
|
286
314
|
**Events** — native button events bubble normally (`click`, `focus`, `blur`).
|
|
287
315
|
|
|
@@ -305,28 +333,28 @@ Renders an `<input>` with state, size, and type support.
|
|
|
305
333
|
|
|
306
334
|
**Attributes**
|
|
307
335
|
|
|
308
|
-
| Attribute
|
|
309
|
-
|
|
310
|
-
| `type`
|
|
311
|
-
| `size`
|
|
312
|
-
| `name`
|
|
313
|
-
| `value`
|
|
314
|
-
| `placeholder`
|
|
315
|
-
| `disabled`
|
|
316
|
-
| `loading`
|
|
317
|
-
| `readonly`
|
|
318
|
-
| `required`
|
|
319
|
-
| `invalid`
|
|
320
|
-
| `success`
|
|
321
|
-
| `full-width`
|
|
322
|
-
| `pill`
|
|
323
|
-
| `autocomplete`
|
|
324
|
-
| `inputmode`
|
|
325
|
-
| `min` / `max` / `step`
|
|
326
|
-
| `minlength` / `maxlength` | number
|
|
327
|
-
| `form`
|
|
328
|
-
| `autofocus`
|
|
329
|
-
| `id` / `title` / `aria-*` | string
|
|
336
|
+
| Attribute | Type | Default | Description |
|
|
337
|
+
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------- |
|
|
338
|
+
| `type` | `text \| email \| password \| search \| tel \| url \| number \| date \| datetime-local \| month \| time \| week` | `text` | Native input type |
|
|
339
|
+
| `size` | `sm \| md \| lg` | `md` | Control size |
|
|
340
|
+
| `name` | string | — | Form field name |
|
|
341
|
+
| `value` | string | `''` | Current value |
|
|
342
|
+
| `placeholder` | string | — | Placeholder text |
|
|
343
|
+
| `disabled` | boolean | `false` | Disables the input |
|
|
344
|
+
| `loading` | boolean | `false` | Busy state |
|
|
345
|
+
| `readonly` | boolean | `false` | Read-only mode |
|
|
346
|
+
| `required` | boolean | `false` | Marks field as required |
|
|
347
|
+
| `invalid` | boolean | `false` | Error state (`aria-invalid`) |
|
|
348
|
+
| `success` | boolean | `false` | Success state |
|
|
349
|
+
| `full-width` | boolean | `false` | Spans full container width |
|
|
350
|
+
| `pill` | boolean | `false` | Pill / fully-rounded corners |
|
|
351
|
+
| `autocomplete` | string | — | Native autocomplete hint |
|
|
352
|
+
| `inputmode` | string | — | Virtual keyboard hint |
|
|
353
|
+
| `min` / `max` / `step` | string | — | Numeric/date range |
|
|
354
|
+
| `minlength` / `maxlength` | number | — | Character length constraints |
|
|
355
|
+
| `form` | string | — | Associates with a form by ID |
|
|
356
|
+
| `autofocus` | boolean | `false` | Autofocus on page load |
|
|
357
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to native `<input>` |
|
|
330
358
|
|
|
331
359
|
**Events** — `input` and `change` fire from the native `<input>` and bubble.
|
|
332
360
|
|
|
@@ -338,30 +366,32 @@ Renders an `<input>` with state, size, and type support.
|
|
|
338
366
|
|
|
339
367
|
Renders a `<textarea>` with row control and resize support.
|
|
340
368
|
|
|
341
|
-
**Attributes** — same as `sp-input` except no `type`, `min`, `max`, `step`, and
|
|
369
|
+
**Attributes** — same as `sp-input` except no `type`, `min`, `max`, `step`, and
|
|
370
|
+
adds:
|
|
342
371
|
|
|
343
|
-
| Attribute | Type
|
|
344
|
-
|
|
345
|
-
| `rows`
|
|
372
|
+
| Attribute | Type | Default | Description |
|
|
373
|
+
| --------- | ------ | ------- | ------------------ |
|
|
374
|
+
| `rows` | number | `2` | Visible row height |
|
|
346
375
|
|
|
347
376
|
**Events** — `input` and `change` fire from the native `<textarea>`.
|
|
348
377
|
|
|
349
|
-
**Internal target** — `[data-sp-textarea-native]` selects the native
|
|
378
|
+
**Internal target** — `[data-sp-textarea-native]` selects the native
|
|
379
|
+
`<textarea>`.
|
|
350
380
|
|
|
351
381
|
---
|
|
352
382
|
|
|
353
383
|
### sp-select
|
|
354
384
|
|
|
355
|
-
Renders a `<select>`. Pass `<option>` elements as children — they are
|
|
356
|
-
|
|
385
|
+
Renders a `<select>`. Pass `<option>` elements as children — they are projected
|
|
386
|
+
into the native select element.
|
|
357
387
|
|
|
358
388
|
**Attributes** — same as `sp-input` minus `type`, `placeholder`, `readonly`,
|
|
359
389
|
`inputmode`, `min`, `max`, `step`, `minlength`, `maxlength`.
|
|
360
390
|
|
|
361
391
|
**Events** — `input` and `change` fire from the native `<select>`.
|
|
362
392
|
|
|
363
|
-
**Content projection** — `<option>` and `<optgroup>` children are moved into
|
|
364
|
-
|
|
393
|
+
**Content projection** — `<option>` and `<optgroup>` children are moved into the
|
|
394
|
+
native `<select>`:
|
|
365
395
|
|
|
366
396
|
```html
|
|
367
397
|
<sp-select name="country" required>
|
|
@@ -383,18 +413,18 @@ Renders a `<label>` wrapping an `<input type="checkbox">` with indicator.
|
|
|
383
413
|
|
|
384
414
|
**Attributes**
|
|
385
415
|
|
|
386
|
-
| Attribute
|
|
387
|
-
|
|
388
|
-
| `name`
|
|
389
|
-
| `value`
|
|
390
|
-
| `checked`
|
|
391
|
-
| `label`
|
|
392
|
-
| `disabled`
|
|
393
|
-
| `loading`
|
|
394
|
-
| `required`
|
|
395
|
-
| `invalid`
|
|
396
|
-
| `success`
|
|
397
|
-
| `form` / `autofocus` / `id` / `title` / `aria-*` | —
|
|
416
|
+
| Attribute | Type | Default | Description |
|
|
417
|
+
| ------------------------------------------------ | ------- | ------- | --------------------------------------------- |
|
|
418
|
+
| `name` | string | — | Form field name |
|
|
419
|
+
| `value` | string | `on` | Submitted value when checked |
|
|
420
|
+
| `checked` | boolean | `false` | Checked state |
|
|
421
|
+
| `label` | string | — | Text label (overridden by content projection) |
|
|
422
|
+
| `disabled` | boolean | `false` | Disables the checkbox |
|
|
423
|
+
| `loading` | boolean | `false` | Busy state |
|
|
424
|
+
| `required` | boolean | `false` | Marks field as required |
|
|
425
|
+
| `invalid` | boolean | `false` | Error state |
|
|
426
|
+
| `success` | boolean | `false` | Success state |
|
|
427
|
+
| `form` / `autofocus` / `id` / `title` / `aria-*` | — | — | Forwarded to native `<input>` |
|
|
398
428
|
|
|
399
429
|
**Events** — `input` and `change` fire from the native checkbox input.
|
|
400
430
|
|
|
@@ -413,8 +443,8 @@ markup):
|
|
|
413
443
|
|
|
414
444
|
### sp-radio
|
|
415
445
|
|
|
416
|
-
Renders a `<label>` wrapping an `<input type="radio">` with indicator.
|
|
417
|
-
|
|
446
|
+
Renders a `<label>` wrapping an `<input type="radio">` with indicator. Group
|
|
447
|
+
multiple `sp-radio` elements by giving them the same `name`.
|
|
418
448
|
|
|
419
449
|
**Attributes** — same as `sp-checkbox`. `value` defaults to `on`.
|
|
420
450
|
|
|
@@ -433,15 +463,15 @@ Group multiple `sp-radio` elements by giving them the same `name`.
|
|
|
433
463
|
|
|
434
464
|
### sp-label
|
|
435
465
|
|
|
436
|
-
Renders a `<label>` with `for` forwarding. Use to associate a visible label
|
|
437
|
-
|
|
466
|
+
Renders a `<label>` with `for` forwarding. Use to associate a visible label with
|
|
467
|
+
any form control.
|
|
438
468
|
|
|
439
469
|
**Attributes**
|
|
440
470
|
|
|
441
|
-
| Attribute
|
|
442
|
-
|
|
443
|
-
| `for`
|
|
444
|
-
| `id` / `title` / `aria-*` | string | —
|
|
471
|
+
| Attribute | Type | Default | Description |
|
|
472
|
+
| ------------------------- | ------ | ------- | ------------------------------------------------------------ |
|
|
473
|
+
| `for` | string | — | ID of the associated control (forwarded to native `<label>`) |
|
|
474
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to native `<label>` |
|
|
445
475
|
|
|
446
476
|
**Content projection** — children become the label text (supports rich markup):
|
|
447
477
|
|
|
@@ -461,14 +491,14 @@ Renders a `<fieldset>` with optional legend and group-level state.
|
|
|
461
491
|
|
|
462
492
|
**Attributes**
|
|
463
493
|
|
|
464
|
-
| Attribute
|
|
465
|
-
|
|
466
|
-
| `legend`
|
|
467
|
-
| `disabled`
|
|
468
|
-
| `loading`
|
|
469
|
-
| `invalid`
|
|
470
|
-
| `success`
|
|
471
|
-
| `form` / `name` / `id` / `title` / `aria-*` | string
|
|
494
|
+
| Attribute | Type | Default | Description |
|
|
495
|
+
| ------------------------------------------- | ------- | ------- | ---------------------------------- |
|
|
496
|
+
| `legend` | string | — | Text for the `<legend>` element |
|
|
497
|
+
| `disabled` | boolean | `false` | Disables all controls in the group |
|
|
498
|
+
| `loading` | boolean | `false` | Busy state |
|
|
499
|
+
| `invalid` | boolean | `false` | Group-level error state |
|
|
500
|
+
| `success` | boolean | `false` | Group-level success state |
|
|
501
|
+
| `form` / `name` / `id` / `title` / `aria-*` | string | — | Forwarded to native `<fieldset>` |
|
|
472
502
|
|
|
473
503
|
**Content projection** — children are placed inside the native `<fieldset>`
|
|
474
504
|
alongside the legend:
|
|
@@ -483,7 +513,119 @@ alongside the legend:
|
|
|
483
513
|
</sp-fieldset>
|
|
484
514
|
```
|
|
485
515
|
|
|
486
|
-
**Internal target** — `[data-sp-fieldset-native]` selects the native
|
|
516
|
+
**Internal target** — `[data-sp-fieldset-native]` selects the native
|
|
517
|
+
`<fieldset>`.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
### sp-badge
|
|
522
|
+
|
|
523
|
+
Renders a `<span>` display primitive backed by the Spectre badge recipe.
|
|
524
|
+
|
|
525
|
+
**Attributes**
|
|
526
|
+
|
|
527
|
+
| Attribute | Type | Default | Description |
|
|
528
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------------- | --------- | -------------------------------- |
|
|
529
|
+
| `variant` | `primary \| secondary \| ghost \| danger \| success \| warning \| info \| accent \| cta \| neutral \| outline` | `primary` | Visual style |
|
|
530
|
+
| `size` | `sm \| md \| lg` | `md` | Badge size |
|
|
531
|
+
| `disabled` | boolean | `false` | Disabled visual state |
|
|
532
|
+
| `loading` | boolean | `false` | Busy visual state |
|
|
533
|
+
| `full-width` | boolean | `false` | Spans full container width |
|
|
534
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to the native `<span>` |
|
|
535
|
+
|
|
536
|
+
**Content projection** — children become the badge content.
|
|
537
|
+
|
|
538
|
+
**Internal target** — `[data-sp-badge-native]` selects the native `<span>`.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
### sp-card
|
|
543
|
+
|
|
544
|
+
Renders a `<div>` container backed by the Spectre card recipe.
|
|
545
|
+
|
|
546
|
+
**Attributes**
|
|
547
|
+
|
|
548
|
+
| Attribute | Type | Default | Description |
|
|
549
|
+
| ------------------------- | -------------------------------------- | ---------- | ------------------------------- |
|
|
550
|
+
| `variant` | `elevated \| flat \| outline \| ghost` | `elevated` | Visual style |
|
|
551
|
+
| `padded` | boolean | `true` | Applies card padding |
|
|
552
|
+
| `full-height` | boolean | `false` | Spans full container height |
|
|
553
|
+
| `interactive` | boolean | `false` | Applies interactive styling |
|
|
554
|
+
| `disabled` | boolean | `false` | Disabled visual state |
|
|
555
|
+
| `loading` | boolean | `false` | Busy visual state |
|
|
556
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to the native `<div>` |
|
|
557
|
+
|
|
558
|
+
**Content projection** — children become the card content.
|
|
559
|
+
|
|
560
|
+
**Internal target** — `[data-sp-card-native]` selects the native `<div>`.
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
### sp-icon-box
|
|
565
|
+
|
|
566
|
+
Renders a `<div>` icon container backed by the Spectre icon-box recipe.
|
|
567
|
+
|
|
568
|
+
**Attributes**
|
|
569
|
+
|
|
570
|
+
| Attribute | Type | Default | Description |
|
|
571
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------- |
|
|
572
|
+
| `variant` | `primary \| secondary \| ghost \| danger \| success \| warning \| info \| accent \| cta \| neutral \| outline` | `primary` | Visual style |
|
|
573
|
+
| `size` | `sm \| md \| lg` | `md` | Icon-box size |
|
|
574
|
+
| `disabled` | boolean | `false` | Disabled visual state |
|
|
575
|
+
| `loading` | boolean | `false` | Busy visual state |
|
|
576
|
+
| `interactive` | boolean | `false` | Applies interactive styling |
|
|
577
|
+
| `pill` | boolean | `false` | Pill / fully-rounded corners |
|
|
578
|
+
| `full-width` | boolean | `false` | Spans full container width |
|
|
579
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to the native `<div>` |
|
|
580
|
+
|
|
581
|
+
**Content projection** — children become the icon-box content.
|
|
582
|
+
|
|
583
|
+
**Internal target** — `[data-sp-icon-box-native]` selects the native `<div>`.
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
### sp-rating
|
|
588
|
+
|
|
589
|
+
Renders a read-only rating visualization with generated star spans.
|
|
590
|
+
|
|
591
|
+
**Attributes**
|
|
592
|
+
|
|
593
|
+
| Attribute | Type | Default | Description |
|
|
594
|
+
| ------------------------- | ---------------- | ------- | -------------------------------------- |
|
|
595
|
+
| `value` | number | `0` | Filled star count |
|
|
596
|
+
| `max` | number | `5` | Total star count |
|
|
597
|
+
| `size` | `sm \| md \| lg` | `md` | Rating size |
|
|
598
|
+
| `label` | string | — | Optional visible text beside the stars |
|
|
599
|
+
| `disabled` | boolean | `false` | Disabled visual state |
|
|
600
|
+
| `loading` | boolean | `false` | Busy visual state |
|
|
601
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to the rating container |
|
|
602
|
+
|
|
603
|
+
**Accessibility** — renders `role="img"` and computes an accessible label like
|
|
604
|
+
`Rating: 4 out of 5` unless `aria-label` is provided.
|
|
605
|
+
|
|
606
|
+
**Internal target** — `[data-sp-rating-native]` selects the rating container.
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
### sp-testimonial
|
|
611
|
+
|
|
612
|
+
Renders a `<div>` testimonial container backed by the Spectre testimonial
|
|
613
|
+
recipe.
|
|
614
|
+
|
|
615
|
+
**Attributes**
|
|
616
|
+
|
|
617
|
+
| Attribute | Type | Default | Description |
|
|
618
|
+
| ------------------------- | -------------------------------------- | ---------- | ------------------------------- |
|
|
619
|
+
| `variant` | `elevated \| flat \| outline \| ghost` | `elevated` | Visual style |
|
|
620
|
+
| `full-height` | boolean | `false` | Spans full container height |
|
|
621
|
+
| `interactive` | boolean | `false` | Applies interactive styling |
|
|
622
|
+
| `disabled` | boolean | `false` | Disabled visual state |
|
|
623
|
+
| `loading` | boolean | `false` | Busy visual state |
|
|
624
|
+
| `id` / `title` / `aria-*` | string | — | Forwarded to the native `<div>` |
|
|
625
|
+
|
|
626
|
+
**Content projection** — children become the testimonial content.
|
|
627
|
+
|
|
628
|
+
**Internal target** — `[data-sp-testimonial-native]` selects the native `<div>`.
|
|
487
629
|
|
|
488
630
|
## Package exports / API surface
|
|
489
631
|
|
|
@@ -495,52 +637,66 @@ helper.
|
|
|
495
637
|
**Bulk registration**
|
|
496
638
|
|
|
497
639
|
```ts
|
|
498
|
-
import { defineSpectreComponents } from '@phcdevworks/spectre-components'
|
|
499
|
-
defineSpectreComponents()
|
|
640
|
+
import { defineSpectreComponents } from '@phcdevworks/spectre-components'
|
|
641
|
+
defineSpectreComponents() // registers all sp-* elements
|
|
500
642
|
```
|
|
501
643
|
|
|
502
644
|
**Per-component helpers** (same as individual entry points):
|
|
503
645
|
`defineSpectreButton`, `defineSpectreInput`, `defineSpectreTextarea`,
|
|
504
646
|
`defineSpectreSelect`, `defineSpectreCheckbox`, `defineSpectreRadio`,
|
|
505
|
-
`defineSpectreLabel`, `defineSpectreFieldset`
|
|
647
|
+
`defineSpectreLabel`, `defineSpectreFieldset`, `defineSpectreBadge`,
|
|
648
|
+
`defineSpectreCard`, `defineSpectreIconBox`, `defineSpectreRating`,
|
|
649
|
+
`defineSpectreTestimonial`
|
|
506
650
|
|
|
507
|
-
**Element classes**:
|
|
508
|
-
`
|
|
509
|
-
`
|
|
510
|
-
`
|
|
651
|
+
**Element classes**: `SpectreButtonElement`, `SpectreInputElement`,
|
|
652
|
+
`SpectreTextareaElement`, `SpectreSelectElement`, `SpectreCheckboxElement`,
|
|
653
|
+
`SpectreRadioElement`, `SpectreLabelElement`, `SpectreFieldsetElement`,
|
|
654
|
+
`SpectreBadgeElement`, `SpectreCardElement`, `SpectreIconBoxElement`,
|
|
655
|
+
`SpectreRatingElement`, `SpectreTestimonialElement`
|
|
511
656
|
|
|
512
|
-
**Button constants and types**:
|
|
513
|
-
`
|
|
514
|
-
`
|
|
515
|
-
`SpectreButtonProps`
|
|
657
|
+
**Button constants and types**: `spectreButtonVariants`, `spectreButtonSizes`,
|
|
658
|
+
`spectreButtonTypes`, `SpectreButtonVariant`, `SpectreButtonSize`,
|
|
659
|
+
`SpectreButtonType`, `SpectreButtonProps`
|
|
516
660
|
|
|
517
|
-
**Input / textarea / select constants and types**:
|
|
518
|
-
`
|
|
519
|
-
`
|
|
520
|
-
`SpectreSelectProps`
|
|
661
|
+
**Input / textarea / select constants and types**: `spectreInputSizes`,
|
|
662
|
+
`spectreInputTypes`, `SpectreInputSize`, `SpectreInputType`,
|
|
663
|
+
`SpectreInputProps`, `SpectreTextareaProps`, `SpectreSelectProps`
|
|
521
664
|
|
|
522
665
|
**Props interfaces** (checkbox / radio / label / fieldset):
|
|
523
666
|
`SpectreCheckboxProps`, `SpectreRadioProps`, `SpectreLabelProps`,
|
|
524
667
|
`SpectreFieldsetProps`
|
|
525
668
|
|
|
669
|
+
**Display constants and types**: `spectreBadgeVariants`, `spectreBadgeSizes`,
|
|
670
|
+
`spectreCardVariants`, `spectreIconBoxVariants`, `spectreIconBoxSizes`,
|
|
671
|
+
`spectreRatingSizes`, `spectreTestimonialVariants`, `SpectreBadgeVariant`,
|
|
672
|
+
`SpectreBadgeSize`, `SpectreCardVariant`, `SpectreIconBoxVariant`,
|
|
673
|
+
`SpectreIconBoxSize`, `SpectreRatingSize`, `SpectreTestimonialVariant`,
|
|
674
|
+
`SpectreBadgeProps`, `SpectreCardProps`, `SpectreIconBoxProps`,
|
|
675
|
+
`SpectreRatingProps`, `SpectreTestimonialProps`
|
|
676
|
+
|
|
526
677
|
### Subpath entry points
|
|
527
678
|
|
|
528
679
|
Each entry point registers only that component and exports only its surface:
|
|
529
680
|
|
|
530
|
-
| Entry point
|
|
531
|
-
|
|
532
|
-
| `.../button`
|
|
533
|
-
| `.../input`
|
|
534
|
-
| `.../textarea`
|
|
535
|
-
| `.../select`
|
|
536
|
-
| `.../checkbox`
|
|
537
|
-
| `.../radio`
|
|
538
|
-
| `.../label`
|
|
539
|
-
| `.../fieldset`
|
|
681
|
+
| Entry point | Registers | Key exports |
|
|
682
|
+
| ----------------- | ---------------- | ---------------------------------------------------------------------------------------- |
|
|
683
|
+
| `.../button` | `sp-button` | `defineSpectreButton`, `SpectreButtonElement`, button constants and types |
|
|
684
|
+
| `.../input` | `sp-input` | `defineSpectreInput`, `SpectreInputElement`, input constants and types |
|
|
685
|
+
| `.../textarea` | `sp-textarea` | `defineSpectreTextarea`, `SpectreTextareaElement`, `SpectreTextareaProps` |
|
|
686
|
+
| `.../select` | `sp-select` | `defineSpectreSelect`, `SpectreSelectElement`, `SpectreSelectProps` |
|
|
687
|
+
| `.../checkbox` | `sp-checkbox` | `defineSpectreCheckbox`, `SpectreCheckboxElement`, `SpectreCheckboxProps` |
|
|
688
|
+
| `.../radio` | `sp-radio` | `defineSpectreRadio`, `SpectreRadioElement`, `SpectreRadioProps` |
|
|
689
|
+
| `.../label` | `sp-label` | `defineSpectreLabel`, `SpectreLabelElement`, `SpectreLabelProps` |
|
|
690
|
+
| `.../fieldset` | `sp-fieldset` | `defineSpectreFieldset`, `SpectreFieldsetElement`, `SpectreFieldsetProps` |
|
|
691
|
+
| `.../badge` | `sp-badge` | `defineSpectreBadge`, `SpectreBadgeElement`, badge constants and types |
|
|
692
|
+
| `.../card` | `sp-card` | `defineSpectreCard`, `SpectreCardElement`, card constants and types |
|
|
693
|
+
| `.../icon-box` | `sp-icon-box` | `defineSpectreIconBox`, `SpectreIconBoxElement`, icon-box constants and types |
|
|
694
|
+
| `.../rating` | `sp-rating` | `defineSpectreRating`, `SpectreRatingElement`, rating constants and types |
|
|
695
|
+
| `.../testimonial` | `sp-testimonial` | `defineSpectreTestimonial`, `SpectreTestimonialElement`, testimonial constants and types |
|
|
540
696
|
|
|
541
697
|
Size constants are shared between input, textarea, and select. Import
|
|
542
|
-
`spectreInputSizes` / `SpectreInputSize` from `.../input` when needed
|
|
543
|
-
|
|
698
|
+
`spectreInputSizes` / `SpectreInputSize` from `.../input` when needed alongside
|
|
699
|
+
textarea or select.
|
|
544
700
|
|
|
545
701
|
## Relationship to the rest of Spectre
|
|
546
702
|
|
|
@@ -552,8 +708,8 @@ spectre-components → Lit web component behavior ← you are here
|
|
|
552
708
|
```
|
|
553
709
|
|
|
554
710
|
The Golden Rule: **tokens define meaning, UI defines structure, components
|
|
555
|
-
define behavior, adapters define delivery.** This package only owns the
|
|
556
|
-
|
|
711
|
+
define behavior, adapters define delivery.** This package only owns the behavior
|
|
712
|
+
layer.
|
|
557
713
|
|
|
558
714
|
## Development
|
|
559
715
|
|
|
@@ -561,20 +717,23 @@ behavior layer.
|
|
|
561
717
|
git clone https://github.com/phcdevworks/spectre-components.git
|
|
562
718
|
cd spectre-components
|
|
563
719
|
npm install
|
|
564
|
-
npm run check #
|
|
720
|
+
npm run check # full release validation gate
|
|
565
721
|
```
|
|
566
722
|
|
|
567
|
-
Requires Node.js `^22.
|
|
568
|
-
|
|
569
|
-
| Command
|
|
570
|
-
|
|
571
|
-
| `npm run check`
|
|
572
|
-
| `npm run build`
|
|
573
|
-
| `npm test`
|
|
574
|
-
| `npm run lint`
|
|
575
|
-
| `npm run check:exports`
|
|
576
|
-
| `npm run
|
|
577
|
-
| `npm run
|
|
723
|
+
Requires Node.js `^22.13.0 || >=24.0.0` and npm `11.16.0`.
|
|
724
|
+
|
|
725
|
+
| Command | Purpose |
|
|
726
|
+
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
727
|
+
| `npm run check` | Full validation (lint → typecheck → test → build → export, contract, invariant, and ecosystem checks) |
|
|
728
|
+
| `npm run build` | Compile ESM + CJS with declarations into `dist/` |
|
|
729
|
+
| `npm test` | Run Vitest suite under happy-dom |
|
|
730
|
+
| `npm run lint` | ESLint |
|
|
731
|
+
| `npm run check:exports` | Verify built subpath exports resolve correctly |
|
|
732
|
+
| `npm run check:contract` | Verify built exports match `components.contract.json` |
|
|
733
|
+
| `npm run check:invariants` | Verify light-DOM and no-hardcoded-visual invariants |
|
|
734
|
+
| `npm run check:ecosystem` | Validate `spectre.manifest.json` |
|
|
735
|
+
| `npm run dev` | tsup watch mode |
|
|
736
|
+
| `npm run clean` | Remove `dist/` and `coverage/` |
|
|
578
737
|
|
|
579
738
|
Key source areas:
|
|
580
739
|
|
|
@@ -582,27 +741,27 @@ Key source areas:
|
|
|
582
741
|
- `src/utils/` — `base.ts`, `projectable.ts`, `form.ts`, `dom.ts`
|
|
583
742
|
- `src/index.ts` — root public API and bulk registration helper
|
|
584
743
|
- `tests/` — component behavior coverage (Vitest + happy-dom)
|
|
585
|
-
- `scripts/check-exports.
|
|
744
|
+
- `scripts/check-exports.ts` — post-build export resolution check
|
|
586
745
|
|
|
587
746
|
## Troubleshooting
|
|
588
747
|
|
|
589
|
-
**Build fails with type errors** — TypeScript 6 is required. Run
|
|
590
|
-
|
|
748
|
+
**Build fails with type errors** — TypeScript 6 is required. Run `npm install`,
|
|
749
|
+
then `npm run build`.
|
|
591
750
|
|
|
592
751
|
**Tests fail in CI but pass locally** — Tests run under happy-dom. Confirm you
|
|
593
|
-
are on Node `^22.
|
|
752
|
+
are on Node `^22.13.0 || >=24.0.0`. CI tests both versions.
|
|
594
753
|
|
|
595
754
|
**Custom element already defined** — Each `defineSpectre*()` helper is
|
|
596
755
|
idempotent; calling it twice is safe. If you see conflicts, two different
|
|
597
756
|
versions of this package may be loaded in the same page.
|
|
598
757
|
|
|
599
|
-
**Styles are not applying** — The Spectre CSS layers must load before
|
|
600
|
-
|
|
758
|
+
**Styles are not applying** — The Spectre CSS layers must load before components
|
|
759
|
+
are registered. Import `@phcdevworks/spectre-tokens/index.css` and
|
|
601
760
|
`@phcdevworks/spectre-ui/index.css` at the top of your entry module.
|
|
602
761
|
|
|
603
762
|
**Properties not reflecting in React 18** — React 18 sets custom element
|
|
604
|
-
properties as attributes. Use a `ref` to set properties imperatively, or
|
|
605
|
-
|
|
763
|
+
properties as attributes. Use a `ref` to set properties imperatively, or upgrade
|
|
764
|
+
to React 19 which supports custom elements fully.
|
|
606
765
|
|
|
607
766
|
## Validation
|
|
608
767
|
|
|
@@ -612,8 +771,9 @@ Run the full validation gate before any pull request:
|
|
|
612
771
|
npm run check
|
|
613
772
|
```
|
|
614
773
|
|
|
615
|
-
This runs: lint → typecheck → tests → build → export validation
|
|
616
|
-
|
|
774
|
+
This runs: lint → typecheck → tests → build → export validation → contract
|
|
775
|
+
validation → invariant checks → ecosystem manifest validation. All steps must
|
|
776
|
+
pass.
|
|
617
777
|
|
|
618
778
|
## AI and automation boundaries
|
|
619
779
|
|
|
@@ -640,10 +800,10 @@ Contribution boundaries:
|
|
|
640
800
|
|
|
641
801
|
- Components must consume `@phcdevworks/spectre-ui` class helpers — do not
|
|
642
802
|
recreate CSS locally.
|
|
643
|
-
- Design values must come from `@phcdevworks/spectre-tokens` — do not
|
|
644
|
-
|
|
645
|
-
- Component tags, properties, events, slots, and ARIA behavior are stable API
|
|
646
|
-
|
|
803
|
+
- Design values must come from `@phcdevworks/spectre-tokens` — do not hardcode
|
|
804
|
+
colors, spacing, or other visual primitives.
|
|
805
|
+
- Component tags, properties, events, slots, and ARIA behavior are stable API —
|
|
806
|
+
breaking changes require a semver major bump.
|
|
647
807
|
- Render in light DOM only — Shadow DOM changes require design-system approval.
|
|
648
808
|
- No framework-specific code — no JSX, SFCs, or Astro components in this
|
|
649
809
|
package.
|