@pithyjs/pidl-catalog 0.1.0-beta.3 → 0.1.0-beta.7
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/dist/specs/atoms/actions/button.pidl.json +8 -7
- package/dist/specs/atoms/actions/icon-group.pidl.json +1 -0
- package/dist/specs/atoms/actions/segmented-control.pidl.json +1 -0
- package/dist/specs/atoms/content/action-tile.pidl.json +2 -1
- package/dist/specs/atoms/content/badge.pidl.json +2 -0
- package/dist/specs/atoms/content/callout.pidl.json +1 -0
- package/dist/specs/atoms/content/code-block.pidl.json +1 -0
- package/dist/specs/atoms/content/heading.pidl.json +3 -2
- package/dist/specs/atoms/content/icon.pidl.json +7 -6
- package/dist/specs/atoms/content/image.pidl.json +9 -8
- package/dist/specs/atoms/content/prose.pidl.json +1 -0
- package/dist/specs/atoms/content/stat.pidl.json +1 -0
- package/dist/specs/atoms/content/tabs.pidl.json +1 -0
- package/dist/specs/atoms/content/text.pidl.json +1 -0
- package/dist/specs/atoms/content/tile.pidl.json +10 -3
- package/dist/specs/atoms/data/bars.pidl.json +1 -0
- package/dist/specs/atoms/data/donut.pidl.json +1 -0
- package/dist/specs/atoms/data/gauge.pidl.json +1 -0
- package/dist/specs/atoms/data/line-area.pidl.json +1 -0
- package/dist/specs/atoms/data/line.pidl.json +3 -2
- package/dist/specs/atoms/data/stacked-bar.pidl.json +1 -0
- package/dist/specs/atoms/forms/checkbox-group.pidl.json +1 -0
- package/dist/specs/atoms/forms/checkbox.pidl.json +1 -0
- package/dist/specs/atoms/forms/file-picker.pidl.json +1 -0
- package/dist/specs/atoms/forms/form-field.pidl.json +1 -0
- package/dist/specs/atoms/forms/input.pidl.json +1 -0
- package/dist/specs/atoms/forms/radio-group.pidl.json +1 -0
- package/dist/specs/atoms/forms/radio.pidl.json +1 -0
- package/dist/specs/atoms/forms/search-input.pidl.json +1 -0
- package/dist/specs/atoms/forms/select.pidl.json +1 -0
- package/dist/specs/atoms/forms/slider.pidl.json +298 -0
- package/dist/specs/atoms/forms/textarea.pidl.json +1 -0
- package/dist/specs/atoms/forms/toggle.pidl.json +1 -0
- package/dist/specs/layout/grid-item.pidl.json +2 -0
- package/dist/specs/layout/grid.pidl.json +2 -0
- package/dist/specs/layout/page-section.pidl.json +4 -3
- package/dist/specs/layout/smart-container.pidl.json +1 -0
- package/dist/specs/layout/stack.pidl.json +1 -0
- package/dist/specs/organisms/content/accordion.pidl.json +1 -0
- package/dist/specs/organisms/content/card.pidl.json +36 -2
- package/dist/specs/organisms/feedback/alert-dialog.pidl.json +2 -1
- package/dist/specs/organisms/feedback/modal.pidl.json +12 -3
- package/dist/specs/organisms/feedback/toast.pidl.json +1 -0
- package/dist/specs/organisms/forms/dynamic-form.pidl.json +2 -1
- package/dist/specs/organisms/forms/form.pidl.json +1 -0
- package/dist/specs/organisms/forms/subscribe-form.pidl.json +1 -0
- package/dist/specs/organisms/navigation/command-palette.pidl.json +1 -0
- package/dist/specs/organisms/navigation/docs-sidebar.pidl.json +1 -0
- package/dist/specs/organisms/navigation/navbar.pidl.json +7 -1
- package/dist/specs/organisms/navigation/table-of-contents.pidl.json +1 -0
- package/dist/specs/sections/contact-section.pidl.json +2 -1
- package/dist/specs/sections/cta-section.pidl.json +1 -0
- package/dist/specs/sections/faq-section.pidl.json +1 -0
- package/dist/specs/sections/features-section.pidl.json +1 -0
- package/dist/specs/sections/footer-section.pidl.json +1 -0
- package/dist/specs/sections/hero-section.pidl.json +2 -1
- package/dist/src/borders-psychology.js.map +1 -1
- package/dist/src/color-psychology.js.map +1 -1
- package/dist/src/design-intelligence.js.map +1 -1
- package/dist/src/index.d.ts +290 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/motion-psychology.js.map +1 -1
- package/dist/src/mutation-vocabulary.d.ts.map +1 -1
- package/dist/src/mutation-vocabulary.js +65 -0
- package/dist/src/mutation-vocabulary.js.map +1 -1
- package/dist/src/radius-psychology.js.map +1 -1
- package/dist/src/shadows-psychology.js.map +1 -1
- package/dist/src/spacing-psychology.js.map +1 -1
- package/dist/src/typography-psychology.d.ts.map +1 -1
- package/dist/src/typography-psychology.js +3 -2
- package/dist/src/typography-psychology.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "component",
|
|
3
|
+
"id": "ui.slider",
|
|
4
|
+
"name": "Slider",
|
|
5
|
+
"displayName": "Slider",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"level": "atom",
|
|
8
|
+
"domain": "forms",
|
|
9
|
+
"icon": "sliders-horizontal",
|
|
10
|
+
"bidi": true,
|
|
11
|
+
"description": "Range slider atom built on a native <input type='range'>, so the slider role and keyboard (arrows, Page Up/Down, Home/End) come from the browser. Tokenised custom track and thumb with a filled portion, optional visible label and value readout, a formatter for the readout and aria-valuetext, error/success states, helper/error text and full a11y wiring.",
|
|
12
|
+
"props": [
|
|
13
|
+
{
|
|
14
|
+
"name": "id",
|
|
15
|
+
"label": "ID",
|
|
16
|
+
"editor": { "tab": "settings" },
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Explicit input ID — overrides the auto-generated ID."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "name",
|
|
22
|
+
"label": "Name",
|
|
23
|
+
"editor": { "tab": "settings" },
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Input name attribute for form submission."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "label",
|
|
29
|
+
"label": "Label",
|
|
30
|
+
"type": "string",
|
|
31
|
+
"required": true,
|
|
32
|
+
"placeholder": "Volume",
|
|
33
|
+
"description": "Accessible label — aria-label on the input, or visible <label for> text when showLabel is true. Always required."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "value",
|
|
37
|
+
"reactive": true,
|
|
38
|
+
"label": "Value",
|
|
39
|
+
"editor": { "type": "number" },
|
|
40
|
+
"type": "number | Signal<number>",
|
|
41
|
+
"description": "Current value — static number or signal accessor for a controlled slider. A drag is only overwritten when the accessor's value changes. A numeric string is parsed as a number."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "min",
|
|
45
|
+
"label": "Min",
|
|
46
|
+
"editor": { "type": "number" },
|
|
47
|
+
"type": "number",
|
|
48
|
+
"default": 0,
|
|
49
|
+
"description": "Lowest value."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "max",
|
|
53
|
+
"label": "Max",
|
|
54
|
+
"editor": { "type": "number" },
|
|
55
|
+
"type": "number",
|
|
56
|
+
"default": 100,
|
|
57
|
+
"description": "Highest value."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "step",
|
|
61
|
+
"label": "Step",
|
|
62
|
+
"editor": { "type": "number" },
|
|
63
|
+
"type": "number",
|
|
64
|
+
"default": 1,
|
|
65
|
+
"description": "Step between values."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "size",
|
|
69
|
+
"label": "Size",
|
|
70
|
+
"type": "enum",
|
|
71
|
+
"enum": ["sm", "md", "lg"],
|
|
72
|
+
"default": "md",
|
|
73
|
+
"description": "Control size — drives the thumb size, track thickness and control row height."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "color",
|
|
77
|
+
"label": "Color",
|
|
78
|
+
"editor": { "type": "token-color" },
|
|
79
|
+
"type": "enum",
|
|
80
|
+
"enum": [
|
|
81
|
+
"primary",
|
|
82
|
+
"secondary",
|
|
83
|
+
"tertiary",
|
|
84
|
+
"success",
|
|
85
|
+
"info",
|
|
86
|
+
"warning",
|
|
87
|
+
"danger"
|
|
88
|
+
],
|
|
89
|
+
"default": "primary",
|
|
90
|
+
"description": "Semantic color of the filled track and thumb. Error and success override it."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "showLabel",
|
|
94
|
+
"label": "Show label",
|
|
95
|
+
"type": "boolean",
|
|
96
|
+
"default": false,
|
|
97
|
+
"description": "Render the label as a visible <label for> before the track instead of aria-label."
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "showValue",
|
|
101
|
+
"label": "Show value",
|
|
102
|
+
"type": "boolean",
|
|
103
|
+
"default": false,
|
|
104
|
+
"description": "Render a value readout after the track. It is aria-hidden: the input already exposes its value."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "formatValue",
|
|
108
|
+
"type": "(value: number) => string",
|
|
109
|
+
"description": "Formats the readout and sets aria-valuetext. Without it the readout is the plain number and no aria-valuetext is set."
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"name": "disabled",
|
|
113
|
+
"label": "Disabled",
|
|
114
|
+
"editor": { "tab": "settings" },
|
|
115
|
+
"type": "boolean | Signal<boolean>",
|
|
116
|
+
"default": false,
|
|
117
|
+
"description": "Disabled state — uses the native disabled attribute."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "error",
|
|
121
|
+
"label": "Error",
|
|
122
|
+
"editor": { "tab": "settings" },
|
|
123
|
+
"type": "boolean | Signal<boolean>",
|
|
124
|
+
"default": false,
|
|
125
|
+
"description": "Visual error state — sets aria-invalid, danger-colored fill and thumb, shake on transition."
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "errorMessage",
|
|
129
|
+
"label": "Error message",
|
|
130
|
+
"editor": { "tab": "settings" },
|
|
131
|
+
"type": "string | Signal<string>",
|
|
132
|
+
"description": "Error message shown below the slider when error is truthy. Linked via aria-describedby with role='alert'."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "helperText",
|
|
136
|
+
"label": "Helper text",
|
|
137
|
+
"type": "string",
|
|
138
|
+
"description": "Persistent hint text below the slider. Hidden while an error message is visible."
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "success",
|
|
142
|
+
"label": "Success",
|
|
143
|
+
"editor": { "tab": "settings" },
|
|
144
|
+
"type": "boolean | Signal<boolean>",
|
|
145
|
+
"default": false,
|
|
146
|
+
"description": "Visual success state — success-colored fill and thumb. Error takes precedence."
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "onInput",
|
|
150
|
+
"type": "(value: number, e: Event) => void",
|
|
151
|
+
"description": "Fires on every move with the current value as a number. Also fires after a form reset that changes the value, with a 'reset' Event."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"name": "onChange",
|
|
155
|
+
"type": "(value: number, e: Event) => void",
|
|
156
|
+
"description": "Fires when the user commits a value (pointer release, key press), and after a form reset that changes the value, with a 'reset' Event."
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"events": ["input", "change"],
|
|
160
|
+
"slots": [],
|
|
161
|
+
"tokens": {
|
|
162
|
+
"track.size": "--space-xs (sm/md), calc(--space-xs * 1.5) (lg)",
|
|
163
|
+
"track.rest": "color-mix(in oklch, --color-border 80%, --color-text) — ≥3:1 against the surface, as Toggle's track border",
|
|
164
|
+
"track.fill": "--_slider-color up to --_slider-fill (set from JS as a percentage)",
|
|
165
|
+
"track.radius": "--radius-pill",
|
|
166
|
+
"thumb.size.sm": "calc(var(--font-size-xs) * 1.25)",
|
|
167
|
+
"thumb.size.md": "calc(var(--font-size-sm) * 1.25)",
|
|
168
|
+
"thumb.size.lg": "calc(var(--font-size-body) * 1.25)",
|
|
169
|
+
"thumb.bg": "--_slider-color → --color-{color}, --color-danger (error), --color-success (success)",
|
|
170
|
+
"thumb.border": "--border-width-medium solid --color-surface",
|
|
171
|
+
"thumb.radius": "50%",
|
|
172
|
+
"thumb.shadow": "--shadow-sm, plus a --space-xs halo at --state-hover-opacity / --state-pressed-opacity",
|
|
173
|
+
"label.font": "--font-family-mono at --font-size-xs, --color-text-muted",
|
|
174
|
+
"value.color": "--color-text, tabular numerals",
|
|
175
|
+
"focus.ring": "a11y.focus-ring values on the thumb under :focus-visible, composed with the halo; --ring-color follows --_slider-color",
|
|
176
|
+
"disabled.opacity": "--state-disabled-opacity",
|
|
177
|
+
"hit-target": "input min-block-size 1.5rem (WCAG 2.5.8, 24px)",
|
|
178
|
+
"shake.animation": "ui-form-shake"
|
|
179
|
+
},
|
|
180
|
+
"spacing": {
|
|
181
|
+
"internal": {
|
|
182
|
+
"label-to-track": { "relationship": "close-friends", "token": "sm" },
|
|
183
|
+
"track-to-value": { "relationship": "close-friends", "token": "sm" }
|
|
184
|
+
},
|
|
185
|
+
"external": {
|
|
186
|
+
"wrapper-to-helper": { "relationship": "lovers", "token": "xs" },
|
|
187
|
+
"wrapper-to-error": { "relationship": "lovers", "token": "xs" }
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"constraints": [
|
|
191
|
+
"Never use raw hex colors — use design system tokens",
|
|
192
|
+
"Use logical CSS properties only",
|
|
193
|
+
"All transitions wrapped in @include a11y.motion-safe",
|
|
194
|
+
"Native <input type='range'> — never a div with role='slider'",
|
|
195
|
+
"Each engine's track/thumb pseudo-elements in their own rule — a selector list mixing ::-webkit-* and ::-moz-* is dropped by both engines",
|
|
196
|
+
"The WebKit fill gradient is flipped under :dir(rtl); Gecko uses ::-moz-range-progress",
|
|
197
|
+
"Forced-colors mode reverts to native rendering (appearance: auto)",
|
|
198
|
+
"Label prop is required",
|
|
199
|
+
"Callbacks receive numbers (valueAsNumber), never strings",
|
|
200
|
+
"A hidden, nameless <output> detects form resets, so each Slider adds one entry to form.elements and form.length (nothing to FormData)",
|
|
201
|
+
"No validation logic — error/success props are visual only",
|
|
202
|
+
"readonly, required and loading are not supported: a native range ignores them and aria-required is not allowed on the slider role",
|
|
203
|
+
"SliderProps extends Omit<FormControlBaseProps, 'readonly' | 'required' | 'loading'>"
|
|
204
|
+
],
|
|
205
|
+
"a11y": [
|
|
206
|
+
"Native <input type='range'> — slider role, arrows / Page Up / Page Down / Home / End from the browser",
|
|
207
|
+
"aria-label carries the label by default; inside FormField the field's <label for> replaces it",
|
|
208
|
+
"showLabel renders a visible <label for> instead",
|
|
209
|
+
"formatValue sets aria-valuetext so the announced value matches the readout",
|
|
210
|
+
"The value readout is aria-hidden — the input already exposes its value",
|
|
211
|
+
"aria-invalid='true' when error is truthy",
|
|
212
|
+
"aria-describedby dynamically updated for helper/error",
|
|
213
|
+
"Error message has role='alert'",
|
|
214
|
+
"Focus ring on the thumb under :focus-visible, kept under hover and press; follows color, error and success",
|
|
215
|
+
"Input box is at least 24px tall (WCAG 2.5.8 target size)",
|
|
216
|
+
"Forced-colors: native rendering with system colors, and a Highlight outline on the focused input",
|
|
217
|
+
"Halo transition and error shake respect prefers-reduced-motion"
|
|
218
|
+
],
|
|
219
|
+
"ai": {
|
|
220
|
+
"synonyms": [
|
|
221
|
+
"range",
|
|
222
|
+
"range slider",
|
|
223
|
+
"range input",
|
|
224
|
+
"fader",
|
|
225
|
+
"value slider",
|
|
226
|
+
"track bar"
|
|
227
|
+
],
|
|
228
|
+
"avoid": [
|
|
229
|
+
"div with role='slider' — use the native input via <Slider>",
|
|
230
|
+
"raw color values for track / thumb — use the color prop",
|
|
231
|
+
"string values in onInput — the value is already a number",
|
|
232
|
+
"Slider without a label prop",
|
|
233
|
+
"Slider for a choice between a few named options — use SegmentedControl or RadioGroup"
|
|
234
|
+
],
|
|
235
|
+
"patterns": [
|
|
236
|
+
"Basic: <Slider label=\"'Volume'\" />",
|
|
237
|
+
"Range and step: <Slider label=\"'Hue'\" min=\"0\" max=\"360\" step=\"1\" value=\"250\" />",
|
|
238
|
+
"Controlled: <Slider label=\"'Lightness'\" value=\"lightness\" onInput=\"setLightness\" />",
|
|
239
|
+
"Visible label and readout: <Slider label=\"'L'\" showLabel=\"true\" showValue=\"true\" />",
|
|
240
|
+
"Color: <Slider label=\"'Danger hue'\" color=\"'danger'\" />",
|
|
241
|
+
"Disabled: <Slider label=\"'Locked'\" disabled=\"true\" />"
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
"examples": [
|
|
245
|
+
{
|
|
246
|
+
"id": "basic",
|
|
247
|
+
"title": "Basic slider",
|
|
248
|
+
"pithy": "<Slider label=\"'Volume'\" />",
|
|
249
|
+
"assertions": [
|
|
250
|
+
"renders <input type='range'>",
|
|
251
|
+
"aria-label='Volume'",
|
|
252
|
+
"min=0 max=100 step=1",
|
|
253
|
+
"no axe violations"
|
|
254
|
+
]
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"id": "range",
|
|
258
|
+
"title": "Custom range and step",
|
|
259
|
+
"pithy": "<Slider label=\"'Chroma'\" min=\"20\" max=\"220\" step=\"5\" value=\"160\" />",
|
|
260
|
+
"assertions": ["input.min === '20'", "input.valueAsNumber === 160"]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"id": "readout",
|
|
264
|
+
"title": "Visible label and value readout",
|
|
265
|
+
"pithy": "<Slider label=\"'Hue'\" min=\"0\" max=\"360\" value=\"250\" showLabel=\"true\" showValue=\"true\" />",
|
|
266
|
+
"assertions": [
|
|
267
|
+
"<label for> names the input",
|
|
268
|
+
"readout shows 250",
|
|
269
|
+
"readout is aria-hidden"
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"id": "color",
|
|
274
|
+
"title": "Semantic color",
|
|
275
|
+
"pithy": "<Slider label=\"'Danger hue'\" color=\"'danger'\" value=\"25\" />",
|
|
276
|
+
"assertions": ["data-color='danger' on wrapper"]
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"id": "disabled",
|
|
280
|
+
"title": "Disabled slider",
|
|
281
|
+
"pithy": "<Slider label=\"'Locked'\" disabled=\"true\" value=\"40\" />",
|
|
282
|
+
"assertions": ["input.disabled === true", "data-disabled on wrapper"]
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"id": "helper",
|
|
286
|
+
"title": "With helper text",
|
|
287
|
+
"pithy": "<Slider label=\"'Opacity'\" helperText=\"'Applies to the whole layer'\" />",
|
|
288
|
+
"assertions": ["helper text visible", "aria-describedby set"]
|
|
289
|
+
}
|
|
290
|
+
],
|
|
291
|
+
"designGuidance": {
|
|
292
|
+
"surface": "Unfilled track uses a strengthened border color (border 80% / text) so it holds ≥3:1 against the surface. The filled portion and thumb share --_slider-color, which follows the color prop and is overridden by error/success.",
|
|
293
|
+
"depth": "Thumb carries --shadow-sm and a --color-surface border so it separates from the filled track. Focus ring sits on the thumb.",
|
|
294
|
+
"borders": "No border on the track — the thin pill shape reads on its own. Thumb is a circle.",
|
|
295
|
+
"interactivity": "Hover adds a soft halo around the thumb at --state-hover-opacity; press deepens it to --state-pressed-opacity. Clicking anywhere on the track jumps the thumb. Error shakes on transition.",
|
|
296
|
+
"bestPractices": "Use Slider for a continuous or finely stepped number where the relative position matters (lightness, volume, opacity). Show the value when the exact number matters, and pass formatValue so the readout and the announced value agree. Use an Input of type number when users need to type an exact value."
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "forms",
|
|
9
|
+
"icon": "text",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Textarea atom with auto-resize (CSS field-sizing with JS fallback), floating label, success/error states with shake animation and state icons, helper text, error message with aria-describedby, character counter, clear button, and loading spinner. No validation logic — states driven externally.",
|
|
11
12
|
"props": [
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "forms",
|
|
9
|
+
"icon": "toggle-left",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Accessible toggle / switch atom with a pill-shaped track and sliding thumb knob, role='switch' with aria-checked, error/success states with shake animation, helper/error text, loading spinner, and full a11y wiring. No validation logic — states driven externally.",
|
|
11
12
|
"props": [
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "layout",
|
|
9
|
+
"icon": "layout-grid",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "Grid child component for structured placement within a Grid. Maps props to utility classes for column/row spanning and positioning.",
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"label": "Span",
|
|
16
17
|
"editor": { "tab": "layout", "type": "string" },
|
|
17
18
|
"type": "number | 'full'",
|
|
19
|
+
"valueKinds": ["number", "string"],
|
|
18
20
|
"description": "Column span (1-12 or 'full') — maps to u-col-span-*"
|
|
19
21
|
},
|
|
20
22
|
{
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "layout",
|
|
9
|
+
"icon": "layout-grid",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "CSS grid layout atom with simple column counts, responsive breakpoints via container queries, and intrinsic auto-fit/fill patterns. Mobile-first: defaults to 1 column.",
|
|
11
12
|
"props": [
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
"label": "Columns",
|
|
15
16
|
"editor": { "tab": "layout", "type": "string" },
|
|
16
17
|
"type": "number | ResponsiveColumns | 'auto-fit' | 'auto-fill'",
|
|
18
|
+
"valueKinds": ["number", "string", "object"],
|
|
17
19
|
"default": 1,
|
|
18
20
|
"description": "Column configuration — number (1-12), responsive object ({ base, sm, md, lg, xl }), or intrinsic auto-fit/auto-fill"
|
|
19
21
|
},
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "layout",
|
|
9
|
+
"icon": "layout-rows",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Semantic page section wrapper with consistent vertical spacing, optional containment, and full-bleed support. Named PageSection to avoid collision with native <section>.",
|
|
11
12
|
"props": [
|
|
@@ -83,8 +84,8 @@
|
|
|
83
84
|
"contained + fullBleed together — they conflict"
|
|
84
85
|
],
|
|
85
86
|
"patterns": [
|
|
86
|
-
"Contained section: <PageSection spacing=\"'lg'\" contained><Heading>Features</Heading></PageSection>",
|
|
87
|
-
"Full-bleed hero: <PageSection spacing=\"'xl'\" fullBleed>...</PageSection>",
|
|
87
|
+
"Contained section: <PageSection spacing=\"'lg'\" contained=\"true\"><Heading>Features</Heading></PageSection>",
|
|
88
|
+
"Full-bleed hero: <PageSection spacing=\"'xl'\" fullBleed=\"true\">...</PageSection>",
|
|
88
89
|
"Anchored section: <PageSection id=\"'about'\" as=\"'article'\">...</PageSection>"
|
|
89
90
|
]
|
|
90
91
|
},
|
|
@@ -98,7 +99,7 @@
|
|
|
98
99
|
{
|
|
99
100
|
"id": "contained",
|
|
100
101
|
"title": "Contained section with max-width",
|
|
101
|
-
"pithy": "<PageSection contained><Text>Centered content</Text></PageSection>",
|
|
102
|
+
"pithy": "<PageSection contained=\"true\"><Text>Centered content</Text></PageSection>",
|
|
102
103
|
"assertions": ["has max-width style", "has margin-inline: auto"]
|
|
103
104
|
}
|
|
104
105
|
]
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "layout",
|
|
9
|
+
"icon": "layout-rows",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Higher-level layout component providing 5 opinionated responsive layout recipes (auto-grid, stack-to-row, row-wrap, sidebar, equal-cards) that adapt to container width via CSS container queries.",
|
|
11
12
|
"props": [
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "atom",
|
|
8
8
|
"domain": "layout",
|
|
9
|
+
"icon": "layout-rows",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Flexbox layout atom that arranges children in a vertical or horizontal stack. Maps props to design-system utility classes — no custom CSS required.",
|
|
11
12
|
"props": [
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "content",
|
|
9
|
+
"icon": "chevron-down",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Disclosure list built on native <details>/<summary>. Keyboard and screen-reader correct with zero ARIA scaffolding and a zero-JS baseline. Items are passed as a typed array and/or projected as children. Optional single-open mode closes siblings when one item opens (progressive enhancement).",
|
|
11
12
|
"props": [
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "content",
|
|
9
|
+
"icon": "card",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Content container with named slots (media, header, body, footer), five variants, two orientations, and a pseudo-link overlay for full-card navigation. Semantic <article> root with container-query sizing and paint containment.",
|
|
11
12
|
"props": [
|
|
@@ -277,7 +278,40 @@
|
|
|
277
278
|
"Blog preview: <Card variant=\"'default'\" padding=\"'none'\" clickable=\"true\" href=\"'/post/hello'\"><img data-slot='media' ...><header data-slot='header'><Heading level='3'>Hello</Heading></header><Text>Excerpt…</Text><footer data-slot='footer'><Text size=\"'sm'\">3 min</Text></footer></Card>",
|
|
278
279
|
"Plan picker: <Card variant=\"'outlined'\" selected=\"isSelected\" clickable=\"true\" href=\"'/pricing?plan=pro'\"><header data-slot='header'><Heading level='3'>Pro</Heading></header><Text>$19/mo</Text></Card>",
|
|
279
280
|
"Horizontal search result: <Card orientation=\"'horizontal'\" variant=\"'ghost'\" clickable=\"true\" href=\"'/result/42'\"><img data-slot='media' ...><header data-slot='header'><Heading level='4'>Result title</Heading></header><Text>Match snippet…</Text></Card>",
|
|
280
|
-
"Feature tile (inside FeaturesSection-style grid): <Card variant=\"'subtle'\" padding=\"'lg'\"><header data-slot='header'><Icon name=\"'
|
|
281
|
+
"Feature tile (inside FeaturesSection-style grid): <Card variant=\"'subtle'\" padding=\"'lg'\"><header data-slot='header'><Icon name=\"'sparkle'\"/><Heading level='3'>Fast</Heading></header><Text>Reactive updates stay O(1).</Text></Card>"
|
|
281
282
|
]
|
|
282
|
-
}
|
|
283
|
+
},
|
|
284
|
+
"examples": [
|
|
285
|
+
{
|
|
286
|
+
"id": "basic-card",
|
|
287
|
+
"title": "Content card with a header and body",
|
|
288
|
+
"pithy": "<Card><header data-slot=\"header\"><Heading level=\"3\">Quarterly report</Heading></header><Text>Revenue grew 12% against a flat market.</Text></Card>",
|
|
289
|
+
"assertions": [
|
|
290
|
+
"renders a .ui-card root as an <article>",
|
|
291
|
+
"the header slot renders inside a semantic <header>",
|
|
292
|
+
"aria-labelledby points at the first heading",
|
|
293
|
+
"no violations in axe-core"
|
|
294
|
+
]
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"id": "clickable-card",
|
|
298
|
+
"title": "Outlined card that navigates",
|
|
299
|
+
"pithy": "<Card variant=\"'outlined'\" clickable=\"true\" href=\"'/reports/q3'\" linkLabel=\"'Read the Q3 report'\"><header data-slot=\"header\"><Heading level=\"3\">Q3 report</Heading></header><Text>Revenue grew 12% against a flat market.</Text></Card>",
|
|
300
|
+
"assertions": [
|
|
301
|
+
"renders one link whose accessible name is \"Read the Q3 report\"",
|
|
302
|
+
"the root carries data-clickable",
|
|
303
|
+
"inner interactive children stay independently focusable"
|
|
304
|
+
]
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"id": "horizontal-card",
|
|
308
|
+
"title": "Horizontal card with media and footer",
|
|
309
|
+
"pithy": "<Card orientation=\"'horizontal'\" padding=\"'lg'\"><img data-slot=\"media\" src=\"/thumb.png\" alt=\"Report cover\"><header data-slot=\"header\"><Heading level=\"4\">Search result</Heading></header><Text>Matching snippet from the document.</Text><footer data-slot=\"footer\"><Text size=\"'sm'\">3 min read</Text></footer></Card>",
|
|
310
|
+
"assertions": [
|
|
311
|
+
"the root carries data-orientation=\"horizontal\"",
|
|
312
|
+
"the media slot renders inside .ui-card-media",
|
|
313
|
+
"the footer slot renders inside a semantic <footer>"
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
]
|
|
283
317
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "feedback",
|
|
9
|
+
"icon": "alert",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "Confirmation dialog built on <Modal> with role='alertdialog'. A titled message with a tone-colored confirm action and an optional cancel — for destructive confirms and acknowledgements. Does not dismiss on backdrop click; Escape cancels.",
|
|
@@ -122,7 +123,7 @@
|
|
|
122
123
|
"long paragraphs in message — keep it to the action and its consequence"
|
|
123
124
|
],
|
|
124
125
|
"patterns": [
|
|
125
|
-
"Destructive confirm: <AlertDialog open=\"isOpen\" onClose=\"close\" onConfirm=\"clear\" tone=\"'danger'\" icon=\"'
|
|
126
|
+
"Destructive confirm: <AlertDialog open=\"isOpen\" onClose=\"close\" onConfirm=\"clear\" tone=\"'danger'\" icon=\"'octagon-alert'\" title=\"'Clear page'\" message=\"'Remove all blocks? You can undo this.'\" confirmLabel=\"'Clear page'\" />",
|
|
126
127
|
"Acknowledge-only: <AlertDialog open=\"isOpen\" onClose=\"close\" title=\"'Saved'\" message=\"'Your changes are saved.'\" />"
|
|
127
128
|
]
|
|
128
129
|
},
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "feedback",
|
|
9
|
+
"icon": "window",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
|
-
"description": "Viewport-level dialog: a centered surface over a dimming backdrop. Portals to <body>, traps focus and locks body scroll via @pithyjs/core's FocusTrap, and closes on Escape or backdrop click. Reactive open state.",
|
|
12
|
+
"description": "Viewport-level dialog: a centered surface over a dimming backdrop, or a full-height drawer docked to the inline-end edge. Portals to <body>, traps focus and locks body scroll via @pithyjs/core's FocusTrap, and closes on Escape or backdrop click. Reactive open state.",
|
|
12
13
|
"props": [
|
|
13
14
|
{
|
|
14
15
|
"name": "open",
|
|
@@ -42,6 +43,14 @@
|
|
|
42
43
|
"required": false,
|
|
43
44
|
"description": "Sets id on the dialog element — enables aria-controls on the trigger button."
|
|
44
45
|
},
|
|
46
|
+
{
|
|
47
|
+
"name": "placement",
|
|
48
|
+
"label": "Placement",
|
|
49
|
+
"type": "enum",
|
|
50
|
+
"enum": ["center", "end"],
|
|
51
|
+
"default": "center",
|
|
52
|
+
"description": "center floats the dialog in the viewport; end docks it to the inline-end edge as a full-height drawer that slides in."
|
|
53
|
+
},
|
|
45
54
|
{
|
|
46
55
|
"name": "closeOnBackdrop",
|
|
47
56
|
"label": "Close on backdrop click",
|
|
@@ -102,7 +111,7 @@
|
|
|
102
111
|
],
|
|
103
112
|
"patterns": [
|
|
104
113
|
"Confirm dialog: <Modal open=\"isOpen\" onClose=\"close\" title=\"'Delete page?'\">…content…</Modal>",
|
|
105
|
-
"Labelled by a visible heading: <Modal open=\"isOpen\" onClose=\"close\" title=\"'Publish'\" titleId=\"'publish-title'\"><Heading id=\"'publish-title'\">Publish</Heading>…</Modal>"
|
|
114
|
+
"Labelled by a visible heading: <Modal open=\"isOpen\" onClose=\"close\" title=\"'Publish'\" titleId=\"'publish-title'\"><Heading [id]=\"'publish-title'\">Publish</Heading>…</Modal>"
|
|
106
115
|
]
|
|
107
116
|
},
|
|
108
117
|
"designGuidance": {
|
|
@@ -126,7 +135,7 @@
|
|
|
126
135
|
{
|
|
127
136
|
"id": "labelledby",
|
|
128
137
|
"title": "Labelled by a visible title",
|
|
129
|
-
"pithy": "<Modal open=\"true\" onClose=\"close\" title=\"'Publish'\" titleId=\"'publish-title'\"><Heading id=\"'publish-title'\">Publish</Heading></Modal>",
|
|
138
|
+
"pithy": "<Modal open=\"true\" onClose=\"close\" title=\"'Publish'\" titleId=\"'publish-title'\"><Heading [id]=\"'publish-title'\">Publish</Heading></Modal>",
|
|
130
139
|
"assertions": ["aria-labelledby='publish-title'", "aria-label not set"]
|
|
131
140
|
}
|
|
132
141
|
]
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "feedback",
|
|
9
|
+
"icon": "bell",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "Non-blocking notification. Imperative API (toast.show / success / error / warning / info) that stacks transient messages in a fixed corner region, portaled to <body>. Auto-dismisses after a duration, pauses on hover/focus, and supports an optional action button and close control.",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "forms",
|
|
9
|
+
"icon": "clipboard-list",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Config-driven form organism. Renders form fields from a FormConfig object, auto-wiring each field to a shared signal-based FormState. Supports grid layout with column spans, conditional visibility via dependsOn, validation with error display, and async submit.",
|
|
11
12
|
"props": [
|
|
@@ -116,7 +117,7 @@
|
|
|
116
117
|
"label": "Action URL",
|
|
117
118
|
"editor": { "tab": "settings" },
|
|
118
119
|
"type": "string",
|
|
119
|
-
"description": "Advanced override: raw native form action URL. Takes precedence over Submissions email. When set, the form posts via the browser after passing client-side validation (e.g. for a custom Formsubmit.co endpoint)."
|
|
120
|
+
"description": "Advanced override: raw native form action URL. Takes precedence over Submissions email unless it is not a safe URL (http(s), mailto, tel or relative), in which case it is ignored and Submissions email applies. When set, the form posts via the browser after passing client-side validation (e.g. for a custom Formsubmit.co endpoint)."
|
|
120
121
|
},
|
|
121
122
|
{
|
|
122
123
|
"name": "method",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "forms",
|
|
9
|
+
"icon": "clipboard-list",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "Semantic <form> organism with signal-based state engine. Creates a <form novalidate> element, manages submit lifecycle (validate, submit, complete), and exposes state on the return object for manual wiring to child form atoms. No magic context injection — developers wire fields explicitly via state.fieldValue(), state.fieldHasError(), etc.",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "forms",
|
|
9
|
+
"icon": "mail",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"studioReady": false,
|
|
11
12
|
"description": "Backend-agnostic email-capture form for landing pages, waitlists, and newsletter signups. Composes an email Input and submit Button, manages an idle/submitting/success/error state machine, validates email format client-side, and includes a honeypot spam trap. Delivers submissions via a custom onSubmit handler or by POSTing JSON to an action URL — never hardcodes a specific email provider.",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "navigation",
|
|
9
|
+
"icon": "search",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "⌘K command palette — a modal search field over a filtered command list. Composes <Modal> for the dialog (focus trap, Escape, backdrop) and <SearchInput> for the field, and adds the WAI-ARIA combobox-with-listbox-popup semantics on top: aria-expanded / aria-controls / aria-activedescendant on the field, a role=\"listbox\" of role=\"option\" items, real role=\"group\" sections for grouped commands, and a polite live region announcing the result count. SHELL ONLY — it owns no index. The default filter is a case-insensitive substring test over label + description + keywords, replaceable via the filter prop; the real search engine (Pagefind / Fuse over the codex index) is R3. studioReady is FALSE on purpose: the palette is driven by an open signal and an items array supplied in code, so there is nothing meaningful to author or preview on a canvas.",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "navigation",
|
|
9
|
+
"icon": "panel-left",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "Nested docs navigation tree. Renders a <nav> landmark around real nested <ul>/<li> lists of <a href> links, with native <button aria-expanded> disclosures for groups. The link matching the router's path carries aria-current=\"page\"; the ancestor chain of that link is force-expanded on mount and on every route change. Collapse/expand state persists to localStorage under a caller-supplied key, degrading silently when storage is unavailable. NO search or filtering — that is <CommandPalette>'s job. studioReady is FALSE on purpose: the tree is a data prop and the active state comes from the live router, so there is nothing meaningful to author or preview on a canvas.",
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "navigation",
|
|
9
|
+
"icon": "menu",
|
|
9
10
|
"bidi": true,
|
|
10
11
|
"description": "Top-of-page navigation with brand, primary links, trailing actions, and a mobile popover menu. Uses semantic <nav> with required aria-label, native Popover API for the mobile menu, and container queries for responsive layout.",
|
|
11
12
|
"props": [
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
{ "label": "Pricing", "href": "/pricing" },
|
|
62
63
|
{ "label": "Docs", "href": "/docs" }
|
|
63
64
|
],
|
|
64
|
-
"description": "Primary nav links. Each item is {label, href, exactActive?, prefetch?}.",
|
|
65
|
+
"description": "Primary nav links. Each item is {label, href, exactActive?, prefetch?, target?, disabled?}.",
|
|
65
66
|
"itemSchema": {
|
|
66
67
|
"label": {
|
|
67
68
|
"type": "string",
|
|
@@ -89,6 +90,11 @@
|
|
|
89
90
|
"enum": ["_blank", "_self"],
|
|
90
91
|
"required": false,
|
|
91
92
|
"description": "Anchor target. '_blank' opens the link in a new tab with rel='noopener noreferrer' and opts it out of SPA router prefetching; '_self'/undefined stay in the same tab."
|
|
93
|
+
},
|
|
94
|
+
"disabled": {
|
|
95
|
+
"type": "boolean",
|
|
96
|
+
"required": false,
|
|
97
|
+
"description": "Render as a placeholder link that leads nowhere yet: no href, role='link', aria-disabled='true', never active or router-bound, out of the tab order. The href is kept so the item can be enabled later."
|
|
92
98
|
}
|
|
93
99
|
}
|
|
94
100
|
},
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"version": "0.1.0",
|
|
7
7
|
"level": "organism",
|
|
8
8
|
"domain": "navigation",
|
|
9
|
+
"icon": "list",
|
|
9
10
|
"studioReady": false,
|
|
10
11
|
"bidi": true,
|
|
11
12
|
"description": "Docs sidebar listing the headings of the current page, highlighting the one the reader is at and updating on scroll. Pairs with <Prose>, whose .u-prose article is the default heading source. Renders a <nav> landmark around a real nested <ul>/<li> list of <a href=\"#id\"> links; the active entry carries aria-current=\"location\". studioReady is FALSE on purpose: the component derives its entire content from the live page DOM at construction, so there is nothing meaningful to author or preview on a studio canvas — an empty article yields an empty (hidden) nav. It is registered fully so the catalog and docs tooling can see it; it is simply not canvas-droppable.",
|