@neuravision/construct 2.0.3 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +318 -0
- package/README.md +158 -149
- package/behaviors/combobox.js +486 -0
- package/behaviors/drawer.js +18 -0
- package/behaviors/dropdown.js +310 -0
- package/behaviors/index.d.ts +335 -0
- package/behaviors/index.js +9 -0
- package/behaviors/internal/dialog-controller.js +421 -0
- package/behaviors/internal/dom.js +408 -0
- package/behaviors/internal/overlay.js +241 -0
- package/behaviors/internal/popup-layer.js +190 -0
- package/behaviors/modal.js +17 -0
- package/behaviors/popover.js +278 -0
- package/behaviors/select-menu.js +344 -0
- package/behaviors/tabs.js +241 -0
- package/behaviors/toggle-group.js +266 -0
- package/behaviors/tooltip.js +257 -0
- package/components/README.md +319 -83
- package/components/_shared.css +22 -10
- package/components/_shell-shared.css +20 -4
- package/components/accordion.css +8 -0
- package/components/app-shell-v2.css +28 -14
- package/components/app-shell.css +30 -14
- package/components/badge.css +15 -0
- package/components/banner.css +32 -1
- package/components/button.css +103 -15
- package/components/card.css +67 -8
- package/components/chart.css +26 -0
- package/components/checkbox.css +35 -2
- package/components/chip.css +30 -2
- package/components/combobox.css +21 -0
- package/components/datepicker.css +58 -9
- package/components/datum.css +277 -0
- package/components/dropdown.css +23 -3
- package/components/field.css +6 -4
- package/components/file-upload.css +1 -1
- package/components/index.css +5 -0
- package/components/list.css +28 -2
- package/components/meter.css +182 -0
- package/components/navbar.css +102 -4
- package/components/pagination.css +18 -0
- package/components/progress-bar.css +22 -1
- package/components/select-menu.css +8 -0
- package/components/select.css +8 -0
- package/components/sidebar.css +15 -2
- package/components/skip-link.css +6 -2
- package/components/slider.css +7 -1
- package/components/status.css +107 -0
- package/components/switch.css +66 -5
- package/components/table.css +53 -4
- package/components/tabs.css +55 -3
- package/components/toast.css +10 -1
- package/components/toggle-group.css +16 -5
- package/components/toolbar.css +17 -1
- package/components/tooltip.css +4 -0
- package/components/tree.css +30 -0
- package/docs/architecture.md +79 -0
- package/docs/behaviors.md +122 -0
- package/docs/fonts.md +70 -0
- package/docs/governance.md +85 -0
- package/docs/guidelines.md +72 -25
- package/examples/react/README.md +20 -0
- package/examples/react/SettingsModal.jsx +66 -0
- package/examples/react/construct.css +5 -0
- package/fonts.css +10 -0
- package/foundations.css +14 -1
- package/package.json +76 -21
- package/schemas/components.schema.json +122 -0
- package/schemas/examples/theme.example.json +27 -0
- package/schemas/primitives.schema.json +135 -0
- package/schemas/semantic.schema.json +224 -0
- package/schemas/theme.schema.json +63 -0
- package/scripts/build-tokens.mjs +1499 -0
- package/scripts/construct-theme.mjs +43 -0
- package/scripts/validate-tokens.mjs +25 -0
- package/standalone/accordion.css +3 -0
- package/standalone/alert.css +3 -0
- package/standalone/app-shell-v2.css +4 -0
- package/standalone/app-shell.css +4 -0
- package/standalone/avatar.css +3 -0
- package/standalone/badge.css +3 -0
- package/standalone/banner.css +3 -0
- package/standalone/breadcrumbs.css +3 -0
- package/standalone/button.css +3 -0
- package/standalone/card.css +3 -0
- package/standalone/chart.css +3 -0
- package/standalone/checkbox.css +3 -0
- package/standalone/chip.css +3 -0
- package/standalone/combobox.css +3 -0
- package/standalone/core.css +4 -0
- package/standalone/data-table.css +3 -0
- package/standalone/datepicker.css +3 -0
- package/standalone/datum.css +3 -0
- package/standalone/divider.css +3 -0
- package/standalone/drawer.css +3 -0
- package/standalone/dropdown.css +3 -0
- package/standalone/empty-state.css +3 -0
- package/standalone/field.css +3 -0
- package/standalone/file-upload.css +3 -0
- package/standalone/icon.css +3 -0
- package/standalone/input.css +3 -0
- package/standalone/list.css +3 -0
- package/standalone/meter.css +3 -0
- package/standalone/modal.css +3 -0
- package/standalone/navbar.css +3 -0
- package/standalone/pagination.css +3 -0
- package/standalone/popover.css +3 -0
- package/standalone/progress-bar.css +3 -0
- package/standalone/select-menu.css +3 -0
- package/standalone/select.css +3 -0
- package/standalone/sidebar.css +3 -0
- package/standalone/skeleton.css +3 -0
- package/standalone/skip-link.css +3 -0
- package/standalone/slider.css +3 -0
- package/standalone/spinner.css +3 -0
- package/standalone/status.css +3 -0
- package/standalone/switch.css +3 -0
- package/standalone/table.css +3 -0
- package/standalone/tabs.css +3 -0
- package/standalone/textarea.css +3 -0
- package/standalone/toast.css +3 -0
- package/standalone/toggle-group.css +3 -0
- package/standalone/toolbar.css +3 -0
- package/standalone/tooltip.css +3 -0
- package/standalone/tree.css +3 -0
- package/themes/README.md +37 -0
- package/themes/walnut.theme.json +106 -0
- package/tokens/README.md +135 -123
- package/tokens/components.json +64 -0
- package/tokens/primitives.json +268 -0
- package/tokens/semantic.dark.json +94 -0
- package/tokens/semantic.high-contrast-dark.json +94 -0
- package/tokens/semantic.high-contrast.json +94 -0
- package/tokens/semantic.light.json +179 -0
- package/tokens/tokens.css +960 -58
- package/tokens/tokens.js +729 -120
- package/tokens/tokens.json +1158 -116
- package/tokens/tokens.ts +729 -120
package/components/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Construct Components
|
|
1
|
+
# Construct Components v2
|
|
2
2
|
|
|
3
|
-
Framework-agnostic component styles built on Construct design tokens. Use these styles directly with HTML or wrap them in Angular, React, or Svelte components.
|
|
3
|
+
Framework-agnostic component styles built on Construct design tokens. Use these styles directly with HTML or wrap them in Angular, React, or Svelte components. Visual state follows native, ARIA, and documented data attributes; optional typed controllers provide the corresponding keyboard and focus behavior.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -18,18 +18,28 @@ Example:
|
|
|
18
18
|
## State Conventions
|
|
19
19
|
|
|
20
20
|
- `data-state="open"` / `data-state="closed"` for popovers, modals, tooltips
|
|
21
|
+
- `data-state="active"`, `loading`, `error`, or `disabled` for component state
|
|
22
|
+
- `data-status="success"`, `info`, `warning`, `danger`, or connection-specific status text for semantic status
|
|
23
|
+
- `aria-busy="true"` while a region is updating; keep a visible loading message
|
|
24
|
+
- `data-loading="true"` as the button's visual loading hook; native buttons also require `disabled`
|
|
21
25
|
- `aria-invalid="true"` for form errors
|
|
22
26
|
- `aria-current="page"` for current pagination page
|
|
27
|
+
- `aria-current="true"` for the current table row; `aria-selected="true"`/`data-state="selected"` for selected rows in grid contexts
|
|
23
28
|
- `aria-selected="true"` for active tabs and selected datepicker days
|
|
29
|
+
- `aria-sort="ascending|descending"` on the actively sorted column header
|
|
24
30
|
- `aria-disabled="true"` when disabled but not using native `disabled`
|
|
25
31
|
|
|
32
|
+
ARIA communicates state but does not block events. Prefer native `disabled` for form controls. For links and custom controls, suppress pointer and keyboard activation in application code when `aria-disabled="true"` is set. Add live-region attributes only to content that changes dynamically; static status labels do not need `aria-live`.
|
|
33
|
+
|
|
26
34
|
## Keyboard + ARIA Requirements
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
Use `@neuravision/construct/behaviors` where a controller is available (including Tabs, menu Dropdown,
|
|
37
|
+
Modal, and Tooltip), or implement an equivalent tested framework adapter. CSS alone does not provide
|
|
38
|
+
these keyboard patterns:
|
|
29
39
|
|
|
30
40
|
- **Tabs**: Arrow keys move focus between tabs, Home/End jumps to first/last, Enter/Space activates. Use roving tabindex (active tab `tabindex="0"`, others `tabindex="-1"`).
|
|
31
|
-
- **Dropdown (Action
|
|
32
|
-
- **
|
|
41
|
+
- **Dropdown (Action menu)**: Trigger uses `aria-expanded`, `aria-controls`, and `aria-haspopup="menu"`. The menu uses roving focus; Arrow keys, Home/End, and typeahead navigate, Esc closes and restores trigger focus, and Tab closes without cancelling normal focus movement. Use `createDropdownController` for this complete contract.
|
|
42
|
+
- **Generic disclosure**: When popup content contains ordinary links or controls in natural Tab order, use the Popover pattern rather than adding menu roles.
|
|
33
43
|
- **Datepicker**: Arrow keys move by day, PageUp/PageDown switches months, Home/End jumps within week, Enter/Space selects, Esc closes.
|
|
34
44
|
- **Modal**: Focus trap, initial focus inside dialog, Esc closes, focus returns to trigger.
|
|
35
45
|
- **Tooltip**: Opens on hover/focus, closes on blur/Esc, uses `role="tooltip"` with `aria-describedby`.
|
|
@@ -41,31 +51,39 @@ When building framework wrappers, implement these keyboard patterns:
|
|
|
41
51
|
Basic button with variants and sizes:
|
|
42
52
|
|
|
43
53
|
```html
|
|
44
|
-
<button class="ct-button">Primary</button>
|
|
45
|
-
<button class="ct-button ct-button--secondary">Secondary</button>
|
|
46
|
-
<button class="ct-button ct-button--ghost">Ghost</button>
|
|
47
|
-
<button class="ct-button ct-button--outline">Outline</button>
|
|
48
|
-
<button class="ct-button ct-button--danger">Danger</button>
|
|
49
|
-
<button class="ct-button ct-button--accent">Accent</button>
|
|
50
|
-
<button class="ct-button ct-button--link">Link</button>
|
|
54
|
+
<button class="ct-button" type="button">Primary</button>
|
|
55
|
+
<button class="ct-button ct-button--secondary" type="button">Secondary</button>
|
|
56
|
+
<button class="ct-button ct-button--ghost" type="button">Ghost</button>
|
|
57
|
+
<button class="ct-button ct-button--outline" type="button">Outline</button>
|
|
58
|
+
<button class="ct-button ct-button--danger" type="button">Danger</button>
|
|
59
|
+
<button class="ct-button ct-button--accent" type="button">Accent</button>
|
|
60
|
+
<button class="ct-button ct-button--link" type="button">Link</button>
|
|
51
61
|
|
|
52
62
|
<!-- Sizes -->
|
|
53
|
-
<button class="ct-button ct-button--sm">Small</button>
|
|
54
|
-
<button class="ct-button">Medium</button>
|
|
55
|
-
<button class="ct-button ct-button--lg">Large</button>
|
|
63
|
+
<button class="ct-button ct-button--sm" type="button">Small</button>
|
|
64
|
+
<button class="ct-button" type="button">Medium</button>
|
|
65
|
+
<button class="ct-button ct-button--lg" type="button">Large</button>
|
|
56
66
|
|
|
57
67
|
<!-- With icon -->
|
|
58
|
-
<button class="ct-button">
|
|
68
|
+
<button class="ct-button" type="button">
|
|
59
69
|
<span class="ct-button__icon" aria-hidden="true">+</span>
|
|
60
70
|
Add item
|
|
61
71
|
</button>
|
|
62
72
|
|
|
63
73
|
<!-- Icon only -->
|
|
64
|
-
<button class="ct-button ct-button--icon" aria-label="Settings">
|
|
74
|
+
<button class="ct-button ct-button--icon" type="button" aria-label="Settings">
|
|
65
75
|
<span class="ct-button__icon" aria-hidden="true">⚙</span>
|
|
66
76
|
</button>
|
|
77
|
+
|
|
78
|
+
<!-- Loading: stable label, decorative loader, native interaction lock -->
|
|
79
|
+
<button class="ct-button" type="button" aria-busy="true" data-loading="true" disabled>
|
|
80
|
+
<span class="ct-button__content">Save changes</span>
|
|
81
|
+
<span class="ct-button__loader" aria-hidden="true"></span>
|
|
82
|
+
</button>
|
|
67
83
|
```
|
|
68
84
|
|
|
85
|
+
The `.ct-button__content` remains in layout, so switching to loading does not change the button width. Its text also remains the accessible name. `aria-busy` exposes the state, `data-loading` selects the visual treatment, and native `disabled` prevents both pointer and keyboard activation. The spinner stops animating under `prefers-reduced-motion: reduce`.
|
|
86
|
+
|
|
69
87
|
### Field + Input
|
|
70
88
|
|
|
71
89
|
Form field with label, input, hint, and error states:
|
|
@@ -73,15 +91,17 @@ Form field with label, input, hint, and error states:
|
|
|
73
91
|
```html
|
|
74
92
|
<div class="ct-field">
|
|
75
93
|
<label class="ct-field__label" for="email">Email</label>
|
|
76
|
-
<input class="ct-input" id="email" type="email" placeholder="name@company.com"
|
|
77
|
-
|
|
94
|
+
<input class="ct-input" id="email" type="email" placeholder="name@company.com"
|
|
95
|
+
aria-describedby="email-hint" />
|
|
96
|
+
<div class="ct-field__hint" id="email-hint">We will not share this.</div>
|
|
78
97
|
</div>
|
|
79
98
|
|
|
80
99
|
<!-- Error state -->
|
|
81
100
|
<div class="ct-field ct-field--error">
|
|
82
101
|
<label class="ct-field__label" for="name">Name</label>
|
|
83
|
-
<input class="ct-input" id="name" aria-invalid="true"
|
|
84
|
-
|
|
102
|
+
<input class="ct-input" id="name" type="text" aria-invalid="true"
|
|
103
|
+
aria-describedby="name-error" />
|
|
104
|
+
<div class="ct-field__error" id="name-error">Name is required.</div>
|
|
85
105
|
</div>
|
|
86
106
|
|
|
87
107
|
<!-- With icon -->
|
|
@@ -122,7 +142,7 @@ Multi-line text input:
|
|
|
122
142
|
|
|
123
143
|
### Checkbox
|
|
124
144
|
|
|
125
|
-
Single checkbox or checkbox group:
|
|
145
|
+
Single checkbox or a labelled checkbox group:
|
|
126
146
|
|
|
127
147
|
```html
|
|
128
148
|
<label class="ct-check">
|
|
@@ -130,10 +150,17 @@ Single checkbox or checkbox group:
|
|
|
130
150
|
<span class="ct-check__label">Remember me</span>
|
|
131
151
|
</label>
|
|
132
152
|
|
|
133
|
-
<
|
|
134
|
-
<
|
|
135
|
-
<
|
|
136
|
-
|
|
153
|
+
<fieldset>
|
|
154
|
+
<legend class="ct-field__label">Notifications</legend>
|
|
155
|
+
<label class="ct-check">
|
|
156
|
+
<input class="ct-check__input" type="checkbox" checked />
|
|
157
|
+
<span class="ct-check__label">Send weekly reports</span>
|
|
158
|
+
</label>
|
|
159
|
+
<label class="ct-check">
|
|
160
|
+
<input class="ct-check__input" type="checkbox" />
|
|
161
|
+
<span class="ct-check__label">Send security alerts</span>
|
|
162
|
+
</label>
|
|
163
|
+
</fieldset>
|
|
137
164
|
```
|
|
138
165
|
|
|
139
166
|
### Radio
|
|
@@ -141,15 +168,18 @@ Single checkbox or checkbox group:
|
|
|
141
168
|
Radio button group:
|
|
142
169
|
|
|
143
170
|
```html
|
|
144
|
-
<
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
|
|
171
|
+
<fieldset>
|
|
172
|
+
<legend class="ct-field__label">Plan</legend>
|
|
173
|
+
<label class="ct-radio">
|
|
174
|
+
<input class="ct-radio__input" type="radio" name="plan" checked />
|
|
175
|
+
<span class="ct-radio__label">Standard</span>
|
|
176
|
+
</label>
|
|
148
177
|
|
|
149
|
-
<label class="ct-radio">
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
</label>
|
|
178
|
+
<label class="ct-radio">
|
|
179
|
+
<input class="ct-radio__input" type="radio" name="plan" />
|
|
180
|
+
<span class="ct-radio__label">Premium</span>
|
|
181
|
+
</label>
|
|
182
|
+
</fieldset>
|
|
153
183
|
```
|
|
154
184
|
|
|
155
185
|
### Switch
|
|
@@ -171,7 +201,7 @@ Content card with header, body, and footer:
|
|
|
171
201
|
<section class="ct-card">
|
|
172
202
|
<div class="ct-card__header">
|
|
173
203
|
<h3>Team</h3>
|
|
174
|
-
<button class="ct-button ct-button--ghost">Edit</button>
|
|
204
|
+
<button class="ct-button ct-button--ghost" type="button">Edit</button>
|
|
175
205
|
</div>
|
|
176
206
|
<div class="ct-card__body">
|
|
177
207
|
<p>Shared ownership and clear permissions.</p>
|
|
@@ -179,11 +209,42 @@ Content card with header, body, and footer:
|
|
|
179
209
|
</div>
|
|
180
210
|
<div class="ct-card__footer">
|
|
181
211
|
<span class="ct-muted">12 members</span>
|
|
182
|
-
<button class="ct-button ct-button--secondary">Open</button>
|
|
212
|
+
<button class="ct-button ct-button--secondary" type="button">Open</button>
|
|
183
213
|
</div>
|
|
184
214
|
</section>
|
|
185
215
|
```
|
|
186
216
|
|
|
217
|
+
### Datum Primitives
|
|
218
|
+
|
|
219
|
+
The reference-line grammar from `components/datum.css` — Construct's orange signature as reusable
|
|
220
|
+
structural vocabulary. All dimensions are component tokens (`--component-datum-*`).
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
<div class="ct-eyebrow-set">
|
|
224
|
+
<p class="ct-eyebrow ct-eyebrow--numbered">Foundations</p>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div class="ct-datum-scale" role="presentation"></div>
|
|
228
|
+
|
|
229
|
+
<div class="ct-datum-scale ct-datum-scale--progress" role="progressbar"
|
|
230
|
+
aria-label="Upload" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
|
|
231
|
+
style="--ct-datum-scale-progress: 60%;"></div>
|
|
232
|
+
|
|
233
|
+
<figure class="ct-datum-frame">…</figure>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
- `ct-eyebrow` - Mono kicker with a leading datum tick; `--numbered` adds drawing-set sheet numbers
|
|
237
|
+
(scope with `ct-eyebrow-set`)
|
|
238
|
+
- `ct-datum-scale` - Measured ruler line; decorative, mark it `role="presentation"`
|
|
239
|
+
- `ct-datum-scale--progress` - The origin segment reports progress via `--ct-datum-scale-progress`;
|
|
240
|
+
pair with `role="progressbar"` and `aria-value*`
|
|
241
|
+
- `ct-datum-frame` - Corner registration mark for figures, code samples, and media
|
|
242
|
+
- `ct-card--datum`, active leading edges, the tab indicator, sorted table columns, and
|
|
243
|
+
`ct-chart__datum-line` speak the same grammar
|
|
244
|
+
|
|
245
|
+
Orange is never text; edges use `brand-accent`, meaning-bearing fills use `brand-accent-strong`.
|
|
246
|
+
One datum per surface — see `docs/guidelines.md`.
|
|
247
|
+
|
|
187
248
|
### Table
|
|
188
249
|
|
|
189
250
|
Basic data table with variants:
|
|
@@ -217,7 +278,15 @@ Basic data table with variants:
|
|
|
217
278
|
Variants:
|
|
218
279
|
- `ct-table--striped` - Alternating row backgrounds
|
|
219
280
|
- `ct-table--compact` - Reduced padding
|
|
220
|
-
|
|
281
|
+
|
|
282
|
+
Rows receive a visual hover affordance automatically on hover-capable pointers; no hover modifier exists.
|
|
283
|
+
|
|
284
|
+
States (the datum grammar):
|
|
285
|
+
- `tr[aria-current="true"]`, `tr[aria-selected="true"]`, or `tr[data-state="selected"]` marks the
|
|
286
|
+
current/selected row with a muted background and the orange leading edge. Use `aria-current` on
|
|
287
|
+
native tables; `aria-selected` is only valid on rows inside `role="grid"`/`role="treegrid"`.
|
|
288
|
+
- `th[aria-sort="ascending|descending"]` underlines the actively sorted column header with the datum;
|
|
289
|
+
sort direction stays shape-encoded via the `ct-table__sort-indicator` arrows.
|
|
221
290
|
|
|
222
291
|
### Data Table
|
|
223
292
|
|
|
@@ -231,8 +300,8 @@ Complex data table with header, toolbar, filters, and actions:
|
|
|
231
300
|
<span class="ct-data-table__meta">24 total</span>
|
|
232
301
|
</div>
|
|
233
302
|
<div class="ct-data-table__actions">
|
|
234
|
-
<button class="ct-button ct-button--secondary ct-button--sm">Export</button>
|
|
235
|
-
<button class="ct-button ct-button--sm">New project</button>
|
|
303
|
+
<button class="ct-button ct-button--secondary ct-button--sm" type="button">Export</button>
|
|
304
|
+
<button class="ct-button ct-button--sm" type="button">New project</button>
|
|
236
305
|
</div>
|
|
237
306
|
</div>
|
|
238
307
|
|
|
@@ -260,29 +329,89 @@ Complex data table with header, toolbar, filters, and actions:
|
|
|
260
329
|
</div>
|
|
261
330
|
```
|
|
262
331
|
|
|
332
|
+
### Metric / Stat
|
|
333
|
+
|
|
334
|
+
Use `ct-metric` for product metrics. It is the semantic successor to `ct-spec`; the existing `ct-spec` classes remain supported for compatibility. Values use tabular numerals, and every semantic status includes visible text.
|
|
335
|
+
|
|
336
|
+
```html
|
|
337
|
+
<dl class="ct-metrics" aria-label="Service metrics">
|
|
338
|
+
<div class="ct-metric" data-status="success">
|
|
339
|
+
<dt class="ct-metric__label">Availability</dt>
|
|
340
|
+
<dd class="ct-metric__value">99.98<small>%</small>
|
|
341
|
+
<span class="ct-metric__meta">
|
|
342
|
+
<span>Last 30 days</span>
|
|
343
|
+
<span class="ct-metric__status">Healthy</span>
|
|
344
|
+
</span>
|
|
345
|
+
</dd>
|
|
346
|
+
</div>
|
|
347
|
+
</dl>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Use `data-state="active"`, `loading`, `error`, or `disabled` for structural state and `data-status` for `success`, `info`, `warning`, `danger`, or `error` semantics. Pair `data-state="loading"` with `aria-busy="true"`; pair disabled content with `aria-disabled="true"`.
|
|
351
|
+
|
|
352
|
+
### Segmented Meter
|
|
353
|
+
|
|
354
|
+
The segmented meter represents a part-to-whole distribution. Its visible legend is required: colour is decorative reinforcement, never the only way a segment is identified. Use the scalar progress bar instead when one `aria-valuenow` value describes the whole component.
|
|
355
|
+
|
|
356
|
+
```html
|
|
357
|
+
<div class="ct-meter" role="group"
|
|
358
|
+
aria-labelledby="delivery-label" aria-describedby="delivery-legend">
|
|
359
|
+
<div class="ct-meter__header">
|
|
360
|
+
<span class="ct-meter__label" id="delivery-label">Delivery status</span>
|
|
361
|
+
<span class="ct-meter__summary">100 items</span>
|
|
362
|
+
</div>
|
|
363
|
+
<div class="ct-meter__track" aria-hidden="true">
|
|
364
|
+
<span class="ct-meter__segment" data-variant="success"
|
|
365
|
+
style="--ct-meter-segment-size: 62%;"></span>
|
|
366
|
+
<span class="ct-meter__segment" data-variant="warning"
|
|
367
|
+
style="--ct-meter-segment-size: 23%;"></span>
|
|
368
|
+
<span class="ct-meter__segment" data-variant="danger"
|
|
369
|
+
style="--ct-meter-segment-size: 15%;"></span>
|
|
370
|
+
</div>
|
|
371
|
+
<ul class="ct-meter__legend" id="delivery-legend">
|
|
372
|
+
<li class="ct-meter__legend-item">
|
|
373
|
+
<span class="ct-meter__marker" data-variant="success" aria-hidden="true"></span>
|
|
374
|
+
<span>Completed</span><span class="ct-meter__legend-value">62%</span>
|
|
375
|
+
</li>
|
|
376
|
+
<li class="ct-meter__legend-item">
|
|
377
|
+
<span class="ct-meter__marker" data-variant="warning" aria-hidden="true"></span>
|
|
378
|
+
<span>In review</span><span class="ct-meter__legend-value">23%</span>
|
|
379
|
+
</li>
|
|
380
|
+
<li class="ct-meter__legend-item">
|
|
381
|
+
<span class="ct-meter__marker" data-variant="danger" aria-hidden="true"></span>
|
|
382
|
+
<span>Blocked</span><span class="ct-meter__legend-value">15%</span>
|
|
383
|
+
</li>
|
|
384
|
+
</ul>
|
|
385
|
+
</div>
|
|
386
|
+
```
|
|
387
|
+
|
|
263
388
|
### Modal
|
|
264
389
|
|
|
265
390
|
Dialog with backdrop:
|
|
266
391
|
|
|
267
392
|
```html
|
|
268
|
-
<div class="ct-modal" data-state="
|
|
269
|
-
<div class="ct-modal__dialog">
|
|
393
|
+
<div class="ct-modal" data-state="closed" hidden>
|
|
394
|
+
<div class="ct-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
270
395
|
<div class="ct-modal__header">
|
|
271
396
|
<h2 id="modal-title">Invite team</h2>
|
|
272
|
-
<button class="ct-button ct-button--ghost"
|
|
397
|
+
<button class="ct-button ct-button--ghost" type="button"
|
|
398
|
+
aria-label="Close" data-ct-dismiss>×</button>
|
|
273
399
|
</div>
|
|
274
400
|
<div class="ct-modal__body">
|
|
275
401
|
<p>Send an invite to a new team member.</p>
|
|
276
402
|
<!-- form content -->
|
|
277
403
|
</div>
|
|
278
404
|
<div class="ct-modal__footer">
|
|
279
|
-
<button class="ct-button ct-button--secondary">Cancel</button>
|
|
280
|
-
<button class="ct-button">Send</button>
|
|
405
|
+
<button class="ct-button ct-button--secondary" type="button" data-ct-dismiss>Cancel</button>
|
|
406
|
+
<button class="ct-button" type="button">Send</button>
|
|
281
407
|
</div>
|
|
282
408
|
</div>
|
|
283
409
|
</div>
|
|
284
410
|
```
|
|
285
411
|
|
|
412
|
+
Attach `createModalController({ container, dialog, trigger })` to synchronize state, trap focus,
|
|
413
|
+
handle Escape/backdrop dismissal, and restore focus. See [Headless behaviors](../docs/behaviors.md).
|
|
414
|
+
|
|
286
415
|
### Toast
|
|
287
416
|
|
|
288
417
|
Notification with variants:
|
|
@@ -292,12 +421,12 @@ Notification with variants:
|
|
|
292
421
|
<div class="ct-toast" data-variant="success" data-state="open">
|
|
293
422
|
<div class="ct-toast__title">Saved</div>
|
|
294
423
|
<div class="ct-toast__description">Your changes were saved.</div>
|
|
295
|
-
<button class="ct-button ct-button--ghost">Undo</button>
|
|
424
|
+
<button class="ct-button ct-button--ghost" type="button">Undo</button>
|
|
296
425
|
</div>
|
|
297
426
|
</div>
|
|
298
427
|
```
|
|
299
428
|
|
|
300
|
-
Variants: `success`, `
|
|
429
|
+
Variants: `success`, `danger`, `info`, `warning`
|
|
301
430
|
|
|
302
431
|
### Alert / Banner
|
|
303
432
|
|
|
@@ -310,7 +439,7 @@ Inline feedback message with variants:
|
|
|
310
439
|
<div class="ct-alert__title">Action required</div>
|
|
311
440
|
<div class="ct-alert__description">Please review the pending changes.</div>
|
|
312
441
|
<div class="ct-alert__actions">
|
|
313
|
-
<button class="ct-button ct-button--secondary ct-button--sm">Review</button>
|
|
442
|
+
<button class="ct-button ct-button--secondary ct-button--sm" type="button">Review</button>
|
|
314
443
|
</div>
|
|
315
444
|
</div>
|
|
316
445
|
</div>
|
|
@@ -330,6 +459,21 @@ Small status indicators with optional icon or dot:
|
|
|
330
459
|
</span>
|
|
331
460
|
```
|
|
332
461
|
|
|
462
|
+
### Live / Connection Status
|
|
463
|
+
|
|
464
|
+
Status text is mandatory; the indicator is decorative. Static statuses need no live-region role. For a connection label that changes in place, use a polite atomic status region.
|
|
465
|
+
|
|
466
|
+
```html
|
|
467
|
+
<span class="ct-status" data-status="connecting" data-state="loading"
|
|
468
|
+
role="status" aria-live="polite" aria-atomic="true" aria-busy="true">
|
|
469
|
+
<span class="ct-status__indicator" aria-hidden="true"></span>
|
|
470
|
+
<span class="ct-status__label">Connecting</span>
|
|
471
|
+
<span class="ct-status__detail">Retrying</span>
|
|
472
|
+
</span>
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Connection values are `online`, `connecting`, `degraded`, `offline`, and `error`. Generic semantic aliases `success`, `info`, `warning`, `neutral`, and `danger` are also supported. Update the existing label in place so screen readers announce the change once.
|
|
476
|
+
|
|
333
477
|
### Chip / Tag
|
|
334
478
|
|
|
335
479
|
Compact tags for filters or metadata:
|
|
@@ -348,7 +492,9 @@ Compact tags for filters or metadata:
|
|
|
348
492
|
|
|
349
493
|
### File Upload / Drag & Drop
|
|
350
494
|
|
|
351
|
-
Drag area + file list pattern. Use `data-state="dragover"`
|
|
495
|
+
Drag area + file list pattern. Use `data-state="dragover"` while dragging. For validation, put
|
|
496
|
+
`aria-invalid="true"` on both the styled dropzone and the native file input, then connect visible
|
|
497
|
+
error text to the input with `aria-describedby`.
|
|
352
498
|
|
|
353
499
|
```html
|
|
354
500
|
<div class="ct-file-upload">
|
|
@@ -368,7 +514,18 @@ Drag area + file list pattern. Use `data-state="dragover"` and `aria-invalid="tr
|
|
|
368
514
|
<span class="ct-badge ct-badge--success">Uploaded</span>
|
|
369
515
|
</li>
|
|
370
516
|
</ul>
|
|
371
|
-
|
|
517
|
+
</div>
|
|
518
|
+
|
|
519
|
+
<!-- Invalid state -->
|
|
520
|
+
<div class="ct-file-upload">
|
|
521
|
+
<label class="ct-file-upload__dropzone" for="files-invalid" aria-invalid="true">
|
|
522
|
+
<input class="ct-file-upload__input" id="files-invalid" type="file"
|
|
523
|
+
aria-invalid="true" aria-describedby="files-error" />
|
|
524
|
+
<div class="ct-file-upload__title">Choose another file</div>
|
|
525
|
+
<div class="ct-file-upload__hint">PDF, DOCX up to 10MB</div>
|
|
526
|
+
<span class="ct-button ct-button--secondary ct-button--sm">Browse files</span>
|
|
527
|
+
</label>
|
|
528
|
+
<div class="ct-file-upload__error" id="files-error">File is too large.</div>
|
|
372
529
|
</div>
|
|
373
530
|
```
|
|
374
531
|
|
|
@@ -377,7 +534,9 @@ Drag area + file list pattern. Use `data-state="dragover"` and `aria-invalid="tr
|
|
|
377
534
|
Spinner for inline loading and overlay for blocking states:
|
|
378
535
|
|
|
379
536
|
```html
|
|
380
|
-
<div class="ct-spinner ct-spinner--sm" role="status" aria-live="polite"
|
|
537
|
+
<div class="ct-spinner ct-spinner--sm" role="status" aria-live="polite">
|
|
538
|
+
<span class="ct-sr-only">Loading…</span>
|
|
539
|
+
</div>
|
|
381
540
|
|
|
382
541
|
<div class="ct-card" style="position: relative; min-height: 180px;">
|
|
383
542
|
<div class="ct-loading-overlay" data-state="active" aria-busy="true">
|
|
@@ -394,8 +553,9 @@ Spinner for inline loading and overlay for blocking states:
|
|
|
394
553
|
Use the modal base with a confirmation layout:
|
|
395
554
|
|
|
396
555
|
```html
|
|
397
|
-
<div class="ct-modal ct-modal--confirmation" data-state="open"
|
|
398
|
-
<div class="ct-modal__dialog"
|
|
556
|
+
<div class="ct-modal ct-modal--confirmation" data-state="open">
|
|
557
|
+
<div class="ct-modal__dialog" role="dialog" aria-modal="true"
|
|
558
|
+
aria-labelledby="confirm-title" aria-describedby="confirm-desc">
|
|
399
559
|
<div class="ct-modal__body">
|
|
400
560
|
<div class="ct-confirmation" data-variant="danger">
|
|
401
561
|
<div class="ct-confirmation__icon" aria-hidden="true">!</div>
|
|
@@ -406,8 +566,8 @@ Use the modal base with a confirmation layout:
|
|
|
406
566
|
</div>
|
|
407
567
|
</div>
|
|
408
568
|
<div class="ct-modal__footer">
|
|
409
|
-
<button class="ct-button ct-button--secondary">Cancel</button>
|
|
410
|
-
<button class="ct-button ct-button--danger">Delete</button>
|
|
569
|
+
<button class="ct-button ct-button--secondary" type="button" data-ct-dismiss>Cancel</button>
|
|
570
|
+
<button class="ct-button ct-button--danger" type="button">Delete</button>
|
|
411
571
|
</div>
|
|
412
572
|
</div>
|
|
413
573
|
</div>
|
|
@@ -432,31 +592,41 @@ Tab navigation with panels:
|
|
|
432
592
|
```html
|
|
433
593
|
<div class="ct-tabs">
|
|
434
594
|
<div class="ct-tabs__list" role="tablist">
|
|
435
|
-
<button class="ct-tabs__trigger" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">Overview</button>
|
|
436
|
-
<button class="ct-tabs__trigger" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">Settings</button>
|
|
595
|
+
<button class="ct-tabs__trigger" type="button" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">Overview</button>
|
|
596
|
+
<button class="ct-tabs__trigger" type="button" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">Settings</button>
|
|
437
597
|
</div>
|
|
438
598
|
<div class="ct-tabs__panel" role="tabpanel" id="panel-1" aria-labelledby="tab-1">
|
|
439
599
|
<p>Panel content</p>
|
|
440
600
|
</div>
|
|
601
|
+
<div class="ct-tabs__panel" role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
|
|
602
|
+
<p>Settings content</p>
|
|
603
|
+
</div>
|
|
441
604
|
</div>
|
|
442
605
|
```
|
|
443
606
|
|
|
607
|
+
Attach `createTabsController({ tablist })` unless the consuming framework already owns the equivalent roving-focus and selection state machine.
|
|
608
|
+
|
|
444
609
|
### Dropdown
|
|
445
610
|
|
|
446
611
|
Action menu:
|
|
447
612
|
|
|
448
613
|
```html
|
|
449
|
-
<div class="ct-dropdown" data-state="
|
|
450
|
-
<button class="ct-button ct-button--secondary ct-dropdown__trigger"
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
614
|
+
<div class="ct-dropdown" data-state="closed">
|
|
615
|
+
<button class="ct-button ct-button--secondary ct-dropdown__trigger" type="button"
|
|
616
|
+
id="actions-trigger" aria-expanded="false" aria-haspopup="menu"
|
|
617
|
+
aria-controls="actions-menu">Actions</button>
|
|
618
|
+
<div class="ct-dropdown__menu" id="actions-menu" role="menu"
|
|
619
|
+
aria-labelledby="actions-trigger" hidden>
|
|
620
|
+
<button class="ct-dropdown__item" type="button" role="menuitem" tabindex="-1">Edit</button>
|
|
621
|
+
<button class="ct-dropdown__item" type="button" role="menuitem" tabindex="-1">Duplicate</button>
|
|
454
622
|
<div class="ct-dropdown__separator" role="separator"></div>
|
|
455
|
-
<button class="ct-dropdown__item" type="button">Delete</button>
|
|
623
|
+
<button class="ct-dropdown__item" type="button" role="menuitem" tabindex="-1">Delete</button>
|
|
456
624
|
</div>
|
|
457
625
|
</div>
|
|
458
626
|
```
|
|
459
627
|
|
|
628
|
+
Attach `createDropdownController({ root, trigger, menu })` to own `hidden`, `data-state`, ARIA, keyboard navigation, selection, outside interaction, and focus return. The caller still gives the menu an accessible name with `aria-label` or `aria-labelledby`.
|
|
629
|
+
|
|
460
630
|
### Pagination
|
|
461
631
|
|
|
462
632
|
Page navigation:
|
|
@@ -480,14 +650,14 @@ Path navigation:
|
|
|
480
650
|
<ol class="ct-breadcrumbs__list">
|
|
481
651
|
<li class="ct-breadcrumbs__item">
|
|
482
652
|
<a class="ct-breadcrumbs__link" href="/">Home</a>
|
|
483
|
-
<span class="ct-breadcrumbs__separator">/</span>
|
|
653
|
+
<span class="ct-breadcrumbs__separator" aria-hidden="true">/</span>
|
|
484
654
|
</li>
|
|
485
655
|
<li class="ct-breadcrumbs__item">
|
|
486
656
|
<a class="ct-breadcrumbs__link" href="/projects">Projects</a>
|
|
487
|
-
<span class="ct-breadcrumbs__separator">/</span>
|
|
657
|
+
<span class="ct-breadcrumbs__separator" aria-hidden="true">/</span>
|
|
488
658
|
</li>
|
|
489
659
|
<li class="ct-breadcrumbs__item">
|
|
490
|
-
<span class="ct-breadcrumbs__current">Alpha</span>
|
|
660
|
+
<span class="ct-breadcrumbs__current" aria-current="page">Alpha</span>
|
|
491
661
|
</li>
|
|
492
662
|
</ol>
|
|
493
663
|
</nav>
|
|
@@ -495,25 +665,46 @@ Path navigation:
|
|
|
495
665
|
|
|
496
666
|
### Datepicker
|
|
497
667
|
|
|
498
|
-
Calendar popup for date selection
|
|
668
|
+
Calendar popup for date selection. The application or framework adapter owns the calendar's
|
|
669
|
+
roving-tabindex keyboard behavior, month changes, selection, and open/close state described in the
|
|
670
|
+
accessibility guidelines.
|
|
499
671
|
|
|
500
672
|
```html
|
|
501
673
|
<div class="ct-field">
|
|
502
674
|
<label class="ct-field__label" for="date">Date</label>
|
|
503
675
|
<div class="ct-datepicker" data-state="open">
|
|
504
|
-
<input class="ct-input" id="date" type="text" placeholder="Select date"
|
|
505
|
-
|
|
676
|
+
<input class="ct-input" id="date" type="text" placeholder="Select date"
|
|
677
|
+
role="combobox" aria-haspopup="dialog" aria-expanded="true"
|
|
678
|
+
aria-controls="date-popover" />
|
|
679
|
+
<div class="ct-datepicker__popover" id="date-popover"
|
|
680
|
+
role="dialog" aria-modal="true" aria-label="Choose date">
|
|
506
681
|
<div class="ct-datepicker__header">
|
|
507
|
-
<button class="ct-button ct-button--ghost ct-button--icon"
|
|
508
|
-
|
|
509
|
-
<
|
|
682
|
+
<button class="ct-button ct-button--ghost ct-button--icon" type="button"
|
|
683
|
+
aria-label="Previous month">‹</button>
|
|
684
|
+
<div class="ct-datepicker__title" aria-live="polite">March 2026</div>
|
|
685
|
+
<button class="ct-button ct-button--ghost ct-button--icon" type="button"
|
|
686
|
+
aria-label="Next month">›</button>
|
|
510
687
|
</div>
|
|
511
|
-
<div class="ct-datepicker__grid">
|
|
512
|
-
<div class="ct-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
688
|
+
<div class="ct-datepicker__grid" role="grid" aria-label="March 2026">
|
|
689
|
+
<div class="ct-datepicker__row" role="row">
|
|
690
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Monday">Mo</abbr>
|
|
691
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Tuesday">Tu</abbr>
|
|
692
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Wednesday">We</abbr>
|
|
693
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Thursday">Th</abbr>
|
|
694
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Friday">Fr</abbr>
|
|
695
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Saturday">Sa</abbr>
|
|
696
|
+
<abbr class="ct-datepicker__weekday" role="columnheader" title="Sunday">Su</abbr>
|
|
697
|
+
</div>
|
|
698
|
+
<div class="ct-datepicker__row" role="row">
|
|
699
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="-1" aria-label="9 March 2026">9</button>
|
|
700
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="-1" aria-label="10 March 2026">10</button>
|
|
701
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="-1" aria-label="11 March 2026">11</button>
|
|
702
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="0" aria-selected="true" aria-label="12 March 2026">12</button>
|
|
703
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="-1" aria-label="13 March 2026">13</button>
|
|
704
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="-1" aria-label="14 March 2026">14</button>
|
|
705
|
+
<button class="ct-datepicker__day" type="button" role="gridcell" tabindex="-1" aria-label="15 March 2026">15</button>
|
|
706
|
+
</div>
|
|
707
|
+
<!-- Render the remaining weeks with the same row/gridcell contract. -->
|
|
517
708
|
</div>
|
|
518
709
|
</div>
|
|
519
710
|
</div>
|
|
@@ -525,12 +716,57 @@ Calendar popup for date selection:
|
|
|
525
716
|
Hint on hover/focus:
|
|
526
717
|
|
|
527
718
|
```html
|
|
528
|
-
<span class="ct-tooltip" data-state="
|
|
529
|
-
<button class="ct-button" aria-describedby="tip-1">Hover me</button>
|
|
530
|
-
<span class="ct-tooltip__content" role="tooltip" id="tip-1">Short hint</span>
|
|
719
|
+
<span class="ct-tooltip" data-state="closed" data-side="top">
|
|
720
|
+
<button class="ct-button" type="button" aria-describedby="tip-1">Hover me</button>
|
|
721
|
+
<span class="ct-tooltip__content" role="tooltip" id="tip-1" hidden>Short hint</span>
|
|
531
722
|
</span>
|
|
532
723
|
```
|
|
533
724
|
|
|
725
|
+
Attach `createTooltipController({ root, trigger, tooltip })` for hover-capable pointers, keyboard focus, touch dismissal, Escape, delays, and cleanup. The tooltip supplements the trigger's accessible name; it must not contain interactive content.
|
|
726
|
+
|
|
727
|
+
### Navbar
|
|
728
|
+
|
|
729
|
+
Use `ct-navbar--compact` for dense product headers. Mark expendable actions with `data-priority="secondary"`; they hide below the 600px small breakpoint while the menu toggle and primary action remain available. If no mobile toggle exists, primary navigation remains reachable as a horizontal scroll row instead of disappearing.
|
|
730
|
+
|
|
731
|
+
```html
|
|
732
|
+
<header class="ct-navbar ct-navbar--compact">
|
|
733
|
+
<a class="ct-navbar__brand" href="/">
|
|
734
|
+
<span class="ct-navbar__title">Workspace</span>
|
|
735
|
+
</a>
|
|
736
|
+
<button class="ct-navbar__toggle" type="button"
|
|
737
|
+
aria-label="Open navigation" aria-expanded="false" aria-controls="mobile-nav">
|
|
738
|
+
<span class="ct-navbar__toggle-icon" aria-hidden="true"><span></span><span></span><span></span></span>
|
|
739
|
+
</button>
|
|
740
|
+
<div class="ct-navbar__spacer"></div>
|
|
741
|
+
<div class="ct-navbar__actions">
|
|
742
|
+
<button class="ct-button ct-button--ghost ct-button--icon"
|
|
743
|
+
type="button" aria-label="Search" data-priority="secondary">…</button>
|
|
744
|
+
<button class="ct-button ct-button--ghost ct-button--icon"
|
|
745
|
+
type="button" aria-label="Profile">…</button>
|
|
746
|
+
</div>
|
|
747
|
+
<nav class="ct-navbar__mobile-menu" id="mobile-nav" data-state="closed"
|
|
748
|
+
aria-label="Mobile navigation">…</nav>
|
|
749
|
+
</header>
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
The CSS controls presentation only. Application code must keep `aria-expanded`, `data-state`, focus movement, Escape handling, and focus return in sync.
|
|
753
|
+
|
|
754
|
+
### App Shell
|
|
755
|
+
|
|
756
|
+
App Shell V2 is the recommended layout for new applications. The original `.ct-app-shell` remains a supported compatibility layout and is not being rewritten, so existing DOM and selectors keep working. Both variants now share these public override hooks:
|
|
757
|
+
|
|
758
|
+
- `--ct-shell-sidebar-width`
|
|
759
|
+
- `--ct-shell-sidebar-rail-width`
|
|
760
|
+
- `--ct-shell-panel-width`
|
|
761
|
+
- `--ct-shell-motion-duration`
|
|
762
|
+
- `--ct-shell-motion-easing`
|
|
763
|
+
|
|
764
|
+
The historical V1 (`--ct-sidebar-*`) and V2 (`--ct-v2-*`) variables remain compatibility aliases. Prefer the shared hooks in new integrations.
|
|
765
|
+
|
|
766
|
+
## Touch Target Policy
|
|
767
|
+
|
|
768
|
+
On coarse pointers Construct exposes at least a 44×44 CSS-pixel interaction area for the covered compact controls: default and small buttons, checkbox and radio inputs, default and small switches, sliders, pagination links, and interactive chips. Automated browser contracts verify those targets. This exceeds the WCAG 2.2 AA size threshold in SC 2.5.8 and meets Construct's enhanced target policy; SC 2.5.5 itself is AAA. Visual sizes remain compact on fine pointers. Products remain responsible for custom controls and applicable spacing exceptions.
|
|
769
|
+
|
|
534
770
|
## Layout Utilities
|
|
535
771
|
|
|
536
772
|
### Stack
|
|
@@ -549,9 +785,9 @@ Vertical spacing between children:
|
|
|
549
785
|
Horizontal spacing with wrapping:
|
|
550
786
|
|
|
551
787
|
```html
|
|
552
|
-
<div class="ct-cluster" style="--ct-cluster-
|
|
553
|
-
<button>Button 1</button>
|
|
554
|
-
<button>Button 2</button>
|
|
788
|
+
<div class="ct-cluster" style="--ct-cluster-gap: var(--space-3);">
|
|
789
|
+
<button type="button">Button 1</button>
|
|
790
|
+
<button type="button">Button 2</button>
|
|
555
791
|
</div>
|
|
556
792
|
```
|
|
557
793
|
|