@wovenui/components 0.0.0-stage → 1.0.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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -2
  3. package/dist/components.json +4204 -0
  4. package/dist/llms-full.txt +2839 -0
  5. package/dist/wovenui-kit.css +5002 -0
  6. package/dist/wovenui-kit.css.map +1 -0
  7. package/dist/wovenui-kit.iife.js +532 -0
  8. package/dist/wovenui-kit.js +529 -0
  9. package/dist/wovenui-kit.min.css +2 -0
  10. package/dist/wovenui-kit.min.css.map +1 -0
  11. package/package.json +268 -4
  12. package/src/_close.scss +45 -0
  13. package/src/_helpers.scss +11 -0
  14. package/src/_kit.scss +124 -0
  15. package/src/_shared.scss +10 -0
  16. package/src/components/_accordion.scss +132 -0
  17. package/src/components/_alert.scss +161 -0
  18. package/src/components/_autocomplete.scss +129 -0
  19. package/src/components/_avatar-group.scss +80 -0
  20. package/src/components/_avatar.scss +134 -0
  21. package/src/components/_badge.scss +91 -0
  22. package/src/components/_breadcrumb.scss +115 -0
  23. package/src/components/_btn-group.scss +113 -0
  24. package/src/components/_button.scss +301 -0
  25. package/src/components/_card.scss +120 -0
  26. package/src/components/_checkbox.scss +168 -0
  27. package/src/components/_code-block.scss +78 -0
  28. package/src/components/_confirm-modal.scss +75 -0
  29. package/src/components/_data-table.scss +79 -0
  30. package/src/components/_desc-list.scss +180 -0
  31. package/src/components/_divider.scss +66 -0
  32. package/src/components/_dot.scss +115 -0
  33. package/src/components/_drawer.scss +200 -0
  34. package/src/components/_dropdown.scss +182 -0
  35. package/src/components/_empty-state.scss +146 -0
  36. package/src/components/_field-group.scss +68 -0
  37. package/src/components/_field-message.scss +75 -0
  38. package/src/components/_field.scss +127 -0
  39. package/src/components/_form-actions.scss +58 -0
  40. package/src/components/_info-row.scss +69 -0
  41. package/src/components/_input-group.scss +87 -0
  42. package/src/components/_input.scss +278 -0
  43. package/src/components/_list.scss +76 -0
  44. package/src/components/_modal.scss +168 -0
  45. package/src/components/_page-header.scss +85 -0
  46. package/src/components/_pagination.scss +138 -0
  47. package/src/components/_panel.scss +69 -0
  48. package/src/components/_popover.scss +154 -0
  49. package/src/components/_progress-circle.scss +123 -0
  50. package/src/components/_progress.scss +122 -0
  51. package/src/components/_radio.scss +162 -0
  52. package/src/components/_segmented.scss +183 -0
  53. package/src/components/_select.scss +154 -0
  54. package/src/components/_sidebar.scss +300 -0
  55. package/src/components/_skeleton.scss +90 -0
  56. package/src/components/_spinner.scss +84 -0
  57. package/src/components/_stat.scss +92 -0
  58. package/src/components/_table.scss +460 -0
  59. package/src/components/_tabs.scss +327 -0
  60. package/src/components/_tag.scss +143 -0
  61. package/src/components/_text.scss +42 -0
  62. package/src/components/_textarea.scss +57 -0
  63. package/src/components/_timeline.scss +196 -0
  64. package/src/components/_title.scss +41 -0
  65. package/src/components/_toast.scss +221 -0
  66. package/src/components/_toggle.scss +171 -0
  67. package/src/components/_toolbar.scss +74 -0
  68. package/src/components/_topbar.scss +204 -0
  69. package/src/entries/accordion.scss +4 -0
  70. package/src/entries/alert.scss +4 -0
  71. package/src/entries/autocomplete.scss +4 -0
  72. package/src/entries/avatar-group.scss +4 -0
  73. package/src/entries/avatar.scss +4 -0
  74. package/src/entries/badge.scss +4 -0
  75. package/src/entries/breadcrumb.scss +4 -0
  76. package/src/entries/btn-group.scss +4 -0
  77. package/src/entries/button.scss +4 -0
  78. package/src/entries/card.scss +4 -0
  79. package/src/entries/checkbox.scss +4 -0
  80. package/src/entries/code-block.scss +4 -0
  81. package/src/entries/confirm-modal.scss +4 -0
  82. package/src/entries/data-table.scss +4 -0
  83. package/src/entries/desc-list.scss +4 -0
  84. package/src/entries/divider.scss +4 -0
  85. package/src/entries/dot.scss +4 -0
  86. package/src/entries/drawer.scss +4 -0
  87. package/src/entries/dropdown.scss +4 -0
  88. package/src/entries/empty-state.scss +4 -0
  89. package/src/entries/field-group.scss +4 -0
  90. package/src/entries/field-message.scss +4 -0
  91. package/src/entries/field.scss +4 -0
  92. package/src/entries/form-actions.scss +4 -0
  93. package/src/entries/info-row.scss +4 -0
  94. package/src/entries/input-group.scss +4 -0
  95. package/src/entries/input.scss +4 -0
  96. package/src/entries/list.scss +4 -0
  97. package/src/entries/modal.scss +4 -0
  98. package/src/entries/page-header.scss +4 -0
  99. package/src/entries/pagination.scss +4 -0
  100. package/src/entries/panel.scss +4 -0
  101. package/src/entries/popover.scss +4 -0
  102. package/src/entries/progress-circle.scss +4 -0
  103. package/src/entries/progress.scss +4 -0
  104. package/src/entries/radio.scss +4 -0
  105. package/src/entries/segmented.scss +4 -0
  106. package/src/entries/select.scss +4 -0
  107. package/src/entries/sidebar.scss +4 -0
  108. package/src/entries/skeleton.scss +4 -0
  109. package/src/entries/spinner.scss +4 -0
  110. package/src/entries/stat.scss +4 -0
  111. package/src/entries/table.scss +4 -0
  112. package/src/entries/tabs.scss +4 -0
  113. package/src/entries/tag.scss +4 -0
  114. package/src/entries/text.scss +4 -0
  115. package/src/entries/textarea.scss +4 -0
  116. package/src/entries/timeline.scss +4 -0
  117. package/src/entries/title.scss +4 -0
  118. package/src/entries/toast.scss +4 -0
  119. package/src/entries/toggle.scss +4 -0
  120. package/src/entries/toolbar.scss +4 -0
  121. package/src/entries/topbar.scss +4 -0
  122. package/src/index.scss +7 -0
  123. package/src/js/autocomplete.js +86 -0
  124. package/src/js/data-table.js +141 -0
  125. package/src/js/index.js +34 -0
  126. package/src/js/input-clear.js +25 -0
  127. package/src/js/package.json +3 -0
  128. package/src/js/tabs.js +73 -0
  129. package/src/js/toast.js +161 -0
  130. package/src/js/util.js +21 -0
@@ -0,0 +1,2839 @@
1
+ # WovenUI Components LLM Guide
2
+
3
+ **Package**: `@wovenui/components`. HTML and CSS components built on `@wovenui/core` tokens, plus a small optional script.
4
+
5
+ **Purpose**: Drop this file into a project so an AI assistant writes correct WovenUI Components markup.
6
+
7
+ Generated from each component's source. Core reference: https://wovenui.com/llms-full.txt.
8
+
9
+ ## Loading
10
+
11
+ - All components, prebuilt: `@wovenui/components/min.css`.
12
+ - All components from Sass: `@use "@wovenui/components";`, with `$components: ("name": false)` to switch components off.
13
+ - One component from Sass: `@use "@wovenui/components/{name}";`. Its needs load with it.
14
+ - Script, only for components whose JavaScript is not none: `import { init } from "@wovenui/components/js";` or `dist/wovenui-kit.iife.js` (global `WovenUIKit`). It starts components on `DOMContentLoaded`; `init(element)` starts content added later.
15
+ - State lives in attributes (`aria-*`, `hidden`, `open`), not classes.
16
+
17
+ ## Naming grammar
18
+
19
+ - Prefixed root: `wu-btn`.
20
+ - Bare compound modifiers, valid only together with the root: `wu-btn brand sm` emits `.wu-btn.brand`, never a lone `.brand`.
21
+ - Color and emphasis words use core role names (`brand`, `success`, `danger`, `warning`, `info`). Never `primary`.
22
+ - Parts are `wu-<name>-<part>`: `wu-card-header`.
23
+ - A modifier word never reuses a core class name.
24
+ - Component variables use `--wu-<name>--Prop` (`--wu-btn--BackgroundColor`) as read values with fallbacks.
25
+
26
+ ## Motion
27
+
28
+ Calm by default. Motion is opt-in with the compound word `animated` (`wu-btn-group animated`) or `wu-animated` on an ancestor, always inside `@media (prefers-reduced-motion: no-preference)`. The skeleton shimmer is opt-in the same way.
29
+
30
+ ## Icons
31
+
32
+ - The kit ships no icons and no icon library or component.
33
+ - Built-in indicators (select and dropdown arrows, accordion chevron, close marks) are drawn in CSS: masks, borders and pseudo-elements. Any other icon comes from the app's own `svg`. The kit never ships icon files.
34
+ - A direct child `svg` in a component is only sized (`1em`, `flex-shrink: 0`). Never set `fill` or `stroke`, so the app's icon set, stroke or filled, works.
35
+ - `wu-mirror` flips directional icons in right to left.
36
+
37
+ ## Components
38
+
39
+ - `accordion`: Stacked disclosure panels built on native `details`, in one bordered group.
40
+ - `alert`: Inline message with a shape marker, a title, optional actions and a dismiss button.
41
+ - `autocomplete`: Combobox that filters a list of options as the user types.
42
+ - `avatar-group`: Overlapping stack of avatars with a ring between them.
43
+ - `avatar`: Round or square badge for a person or product, with initials, an image or an svg, and an optional status dot.
44
+ - `badge`: Small pill for counts and short labels, tinted by default and solid for emphasis.
45
+ - `breadcrumb`: Trail of links that shows where the page sits in the site.
46
+ - `btn-group`: Joins buttons into one segmented control, as a split button, a toggle set or a radio set.
47
+ - `button`: Buttons for actions, in roles and sizes, with a caret for menus and split buttons.
48
+ - `card`: Bordered surface that groups a header, a body and a footer.
49
+ - `checkbox`: Custom checkbox with a label, a check mark and an indeterminate dash.
50
+ - `code-block`: Scrollable block for code, with edge shadows when the content overflows.
51
+ - `confirm-modal`: Dialog that asks the user to confirm or cancel an action, with a title, a message and two buttons.
52
+ - `data-table`: Sortable table: click a header to sort rows by text or number.
53
+ - `desc-list`: Description list of terms and values, plain, divided, bordered or in columns.
54
+ - `divider`: Horizontal or vertical separator line, optionally with a text label between two lines.
55
+ - `dot`: Small status dot for use next to status text, tinted by role.
56
+ - `drawer`: Side panel on the native `dialog` element, docked to the inline end, the inline start or the bottom.
57
+ - `dropdown`: Menu of links and buttons in a native popover, anchored under its trigger.
58
+ - `empty-state`: Centered message for a view with no content or a finished result, with an icon, title, text and actions.
59
+ - `field-group`: Group of fields in a `fieldset`, stacked by default or side by side.
60
+ - `field-message`: Hint, error or success text under a form control, with a marker shape for each role.
61
+ - `field`: Form field that stacks a label, a control and its messages, with a required marker and a floating label option.
62
+ - `form-actions`: Footer row for form buttons, start-aligned by default.
63
+ - `info-row`: One label and value pair, with a line between adjacent rows.
64
+ - `input-group`: Joined row of text addons, an input and buttons that share one border and radius.
65
+ - `input`: Text input in three sizes, with disabled, read-only and invalid looks, plus prefix, suffix and a clear button inside the field border.
66
+ - `list`: Vertical list of rows with a title, text and room for leading or trailing content.
67
+ - `modal`: Dialog built on the native `dialog` element, with header, body and footer.
68
+ - `page-header`: Page title row with an optional description, breadcrumb and action buttons.
69
+ - `pagination`: Page navigation made of button links, with optional total count and page size select.
70
+ - `panel`: Flat bordered region for grouped settings or data.
71
+ - `popover`: Small floating panel built on the native `popover` attribute.
72
+ - `progress-circle`: Circular progress ring that shows a percentage.
73
+ - `progress`: Horizontal progress bar on the native `progress` element.
74
+ - `radio`: Radio button with a label, drawn from the native input.
75
+ - `segmented`: Radio group shown as connected segments, one selected at a time.
76
+ - `select`: Native `select` with the kit input look and a drawn arrow.
77
+ - `sidebar`: Vertical navigation with sections, link lists and collapsible groups.
78
+ - `skeleton`: Placeholder shapes shown while content loads.
79
+ - `spinner`: Loading ring in the current text color, with an overlay for busy regions.
80
+ - `stat`: One figure with a label, a large value in tabular figures and an optional change line.
81
+ - `table`: Data table with sticky header and columns, selection, expandable rows, truncated cells, a summary row and striped styles.
82
+ - `tabs`: Tabs that show one panel at a time, following the WAI-ARIA tabs pattern, in line, card, boxed and vertical styles.
83
+ - `tag`: Label for a category or a filter, in seven hues, as plain text, a link or a button.
84
+ - `text`: Body text on any element, with relaxed leading and space between paragraphs in a row.
85
+ - `textarea`: Multi-line field with a bounded height that grows with the content, where the browser supports it.
86
+ - `timeline`: Ordered list of events with markers and a rail, role colors and a progress mode.
87
+ - `title`: Visual heading size on any element, in four steps.
88
+ - `toast`: Short notification in a corner of the viewport, shown in a polite live region.
89
+ - `toggle`: On and off switch built on a native checkbox with `role="switch"`.
90
+ - `toolbar`: Wrapping row of controls, with groups, separators, an end group and a boxed variant.
91
+ - `topbar`: Application header with a brand, a scrolling nav, an end group, a pinned variant and a page progress line.
92
+
93
+ ## accordion
94
+
95
+ Stacked disclosure panels built on native `details`, in one bordered group.
96
+
97
+ The same `name` on several `details` makes the group exclusive, one open at a time. Leave it out to allow several open.
98
+
99
+ - Root class: `wu-accordion`
100
+ - Sass entry: `@use "@wovenui/components/accordion";`
101
+ - Needs: none
102
+ - JavaScript: none, CSS only
103
+ - Since: 1.0.0
104
+
105
+ ### Markup
106
+
107
+ ```html
108
+ <div class="wu-accordion">
109
+ <details class="wu-accordion-item" name="faq" open>
110
+ <summary class="wu-accordion-trigger">What is included?</summary>
111
+ <div class="wu-accordion-body">Every plan includes support.</div>
112
+ </details>
113
+ <details class="wu-accordion-item" name="faq">
114
+ <summary class="wu-accordion-trigger">Can I cancel?</summary>
115
+ <div class="wu-accordion-body">Yes, at any time.</div>
116
+ </details>
117
+ </div>
118
+ ```
119
+
120
+ ```html
121
+ <div class="wu-accordion animated">
122
+ <details class="wu-accordion-item">
123
+ <summary class="wu-accordion-trigger">Shipping</summary>
124
+ <div class="wu-accordion-body">Orders ship in two days.</div>
125
+ </details>
126
+ </div>
127
+ ```
128
+
129
+ ### Modifiers
130
+
131
+ | Modifier | Description | Since |
132
+ | --- | --- | --- |
133
+ | `animated` | The chevron turns and the panel height glides when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
134
+
135
+ ### Parts
136
+
137
+ | Part | Description | Since |
138
+ | --- | --- | --- |
139
+ | `wu-accordion-item` | A `details` element holding one trigger and one body | 1.0.0 |
140
+ | `wu-accordion-trigger` | The `summary` that opens and closes the item, with a chevron | 1.0.0 |
141
+ | `wu-accordion-body` | The panel content shown when the item is open | 1.0.0 |
142
+
143
+ ### States
144
+
145
+ | State | Description | Since |
146
+ | --- | --- | --- |
147
+ | `open` | The chevron points up and the body is shown | 1.0.0 |
148
+
149
+ ## alert
150
+
151
+ Inline message with a shape marker, a title, optional actions and a dismiss button.
152
+
153
+ Meaning is carried by the `wu-alert-title` text, such as "Error" or "Saved"; the marker shape repeats it.
154
+
155
+ Use `role="alert"` for urgent messages and `role="status"` for the rest. The close button is markup only; the app removes the alert.
156
+
157
+ - Root class: `wu-alert`
158
+ - Sass entry: `@use "@wovenui/components/alert";`
159
+ - Needs: none
160
+ - JavaScript: none, CSS only
161
+ - Since: 1.0.0
162
+
163
+ ### Markup
164
+
165
+ ```html
166
+ <div class="wu-alert success" role="status">
167
+ <strong class="wu-alert-title">Saved</strong>
168
+ <p>Your changes are live.</p>
169
+ </div>
170
+ ```
171
+
172
+ ```html
173
+ <div class="wu-alert danger" role="alert">
174
+ <strong class="wu-alert-title">Payment failed</strong>
175
+ <p>Check the card details and try again.</p>
176
+ <div class="wu-alert-actions">
177
+ <button class="wu-btn danger" type="button">Retry</button>
178
+ <button class="wu-btn" type="button">Cancel</button>
179
+ </div>
180
+ <button class="wu-alert-close" type="button" aria-label="Dismiss"></button>
181
+ </div>
182
+ ```
183
+
184
+ ### Modifiers
185
+
186
+ | Modifier | Description | Since |
187
+ | --- | --- | --- |
188
+ | `brand`, `success`, `danger`, `warning`, `info` | Role color for border, fill, marker and title; `success`, `warning` and `danger` also change the marker shape | 1.0.0 |
189
+
190
+ ### Parts
191
+
192
+ | Part | Description | Since |
193
+ | --- | --- | --- |
194
+ | `wu-alert-title` | Bold title line that names the message | 1.0.0 |
195
+ | `wu-alert-actions` | Row of buttons under the text | 1.0.0 |
196
+ | `wu-alert-close` | Dismiss button in the end corner, last child of the alert | 1.0.0 |
197
+
198
+ ## autocomplete
199
+
200
+ Combobox that filters a list of options as the user types.
201
+
202
+ The kit script filters the options, handles the keyboard and sets `aria-expanded`, `aria-activedescendant`, `aria-selected` and `hidden`.
203
+
204
+ - Root class: `wu-autocomplete`
205
+ - Sass entry: `@use "@wovenui/components/autocomplete";`
206
+ - Needs: `input`
207
+ - JavaScript: required: `initAutocomplete` from `@wovenui/components/js`
208
+ - Since: 1.0.0
209
+
210
+ ### Markup
211
+
212
+ ```html
213
+ <div class="wu-autocomplete">
214
+ <label for="city">City</label>
215
+ <input class="wu-input" id="city" type="text" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="city-list" autocomplete="off">
216
+ <ul class="wu-autocomplete-list" id="city-list" role="listbox" aria-label="Cities" hidden>
217
+ <li class="wu-autocomplete-option" id="city-1" role="option">Amsterdam</li>
218
+ <li class="wu-autocomplete-option" id="city-2" role="option">Berlin</li>
219
+ <li class="wu-autocomplete-option" id="city-3" role="option" aria-disabled="true">Closed city</li>
220
+ </ul>
221
+ <p class="wu-autocomplete-empty" hidden>No matches</p>
222
+ </div>
223
+ ```
224
+
225
+ ### Modifiers
226
+
227
+ | Modifier | Description | Since |
228
+ | --- | --- | --- |
229
+ | `animated` | The popup fades and slides in when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
230
+
231
+ ### Parts
232
+
233
+ | Part | Description | Since |
234
+ | --- | --- | --- |
235
+ | `wu-autocomplete-list` | Popup `ul` that holds the options | 1.0.0 |
236
+ | `wu-autocomplete-option` | One selectable `li` option | 1.0.0 |
237
+ | `wu-autocomplete-empty` | Message shown when no option matches | 1.0.0 |
238
+
239
+ ### Variables
240
+
241
+ | Variable | Default | Description | Since |
242
+ | --- | --- | --- | --- |
243
+ | `--wu-autocomplete--MaxHeight` | `16rem` | Maximum height of the option list before it scrolls | 1.0.0 |
244
+
245
+ ### States
246
+
247
+ | State | Description | Since |
248
+ | --- | --- | --- |
249
+ | `aria-selected="true"` | The active option: selected fill and a start bar | 1.0.0 |
250
+ | `aria-disabled="true"` | Half opacity and a not-allowed cursor | 1.0.0 |
251
+ | `hidden` | An option filtered out is not displayed | 1.0.0 |
252
+
253
+ ## avatar-group
254
+
255
+ Overlapping stack of avatars with a ring between them.
256
+
257
+ Put the overflow count, such as "+3", in a last `wu-avatar` as real text.
258
+
259
+ - Root class: `wu-avatar-group`
260
+ - Sass entry: `@use "@wovenui/components/avatar-group";`
261
+ - Needs: `avatar`
262
+ - JavaScript: none, CSS only
263
+ - Since: 1.0.0
264
+
265
+ ### Markup
266
+
267
+ ```html
268
+ <div class="wu-avatar-group" role="group" aria-label="Members">
269
+ <span class="wu-avatar blue" role="img" aria-label="Ana Lopez">AL</span>
270
+ <span class="wu-avatar green" role="img" aria-label="Ben Kim">BK</span>
271
+ <span class="wu-avatar" role="img" aria-label="3 more members">+3</span>
272
+ </div>
273
+ ```
274
+
275
+ ```html
276
+ <div class="wu-avatar-group animated" role="group" aria-label="Team">
277
+ <span class="wu-avatar purple" role="img" aria-label="Cy Ray">CR</span>
278
+ <span class="wu-avatar pink" role="img" aria-label="Di Eze">DE</span>
279
+ </div>
280
+ ```
281
+
282
+ ### Modifiers
283
+
284
+ | Modifier | Description | Since |
285
+ | --- | --- | --- |
286
+ | `animated` | On hover or focus within, the stack fans out into a row when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
287
+
288
+ ## avatar
289
+
290
+ Round or square badge for a person or product, with initials, an image or an svg, and an optional status dot.
291
+
292
+ Label it with `aria-label` or the image `alt`, status included.
293
+
294
+ `--wu-avatar--Size` overrides every size.
295
+
296
+ - Root class: `wu-avatar`
297
+ - Sass entry: `@use "@wovenui/components/avatar";`
298
+ - Needs: none
299
+ - JavaScript: none, CSS only
300
+ - Since: 1.0.0
301
+
302
+ ### Markup
303
+
304
+ ```html
305
+ <span class="wu-avatar" role="img" aria-label="Ana Lopez">AL</span>
306
+ <span class="wu-avatar sm blue" role="img" aria-label="Ben Kim">BK</span>
307
+ <span class="wu-avatar lg purple" role="img" aria-label="Cy Ray">CR</span>
308
+ ```
309
+
310
+ ```html
311
+ <span class="wu-avatar green" data-status="online" role="img" aria-label="Di Eze, online">DE</span>
312
+ <span class="wu-avatar tile"><img src="https://wovenui.com/images/logo.svg" alt="WovenUI"></span>
313
+ ```
314
+
315
+ ### Modifiers
316
+
317
+ | Modifier | Description | Since |
318
+ | --- | --- | --- |
319
+ | `sm`, `lg` | Small or large size; the default is medium | 1.0.0 |
320
+ | `tile` | Square with rounded corners, for company and product logos | 1.0.0 |
321
+ | `gray`, `red`, `blue`, `green`, `yellow`, `purple`, `pink` | Tinted fill and text in that hue | 1.0.0 |
322
+
323
+ ### Variables
324
+
325
+ | Variable | Default | Description | Since |
326
+ | --- | --- | --- | --- |
327
+ | `--wu-avatar--Size` | `var(--wu-space-10)` | Width and height; initials scale with it | 1.0.0 |
328
+ | `--wu-avatar--RingColor` | `var(--wu-bg-surface)` | Color of the ring around the status dot, shared with avatar group and corner badge | 1.0.0 |
329
+
330
+ ### States
331
+
332
+ | State | Description | Since |
333
+ | --- | --- | --- |
334
+ | `data-status="online"` | Solid dot at the end corner | 1.0.0 |
335
+ | `data-status="away"` | Half-filled dot | 1.0.0 |
336
+ | `data-status="busy"` | Filled dot with a bar | 1.0.0 |
337
+ | `data-status="offline"` | Hollow ring | 1.0.0 |
338
+
339
+ ## badge
340
+
341
+ Small pill for counts and short labels, tinted by default and solid for emphasis.
342
+
343
+ - Root class: `wu-badge`
344
+ - Sass entry: `@use "@wovenui/components/badge";`
345
+ - Needs: none
346
+ - JavaScript: none, CSS only
347
+ - Since: 1.0.0
348
+
349
+ ### Markup
350
+
351
+ ```html
352
+ <span class="wu-badge">Draft</span>
353
+ <span class="wu-badge success">Paid</span>
354
+ <span class="wu-badge danger solid">3</span>
355
+ ```
356
+
357
+ ```html
358
+ <span class="wu-badge-host">
359
+ <button class="wu-btn" type="button" aria-label="Inbox, 3 unread">Inbox</button>
360
+ <span class="wu-badge corner danger solid">3</span>
361
+ </span>
362
+ ```
363
+
364
+ ### Modifiers
365
+
366
+ | Modifier | Description | Since |
367
+ | --- | --- | --- |
368
+ | `brand`, `success`, `danger`, `warning`, `info` | Role color: tinted fill, border and text; solid fill with `solid` | 1.0.0 |
369
+ | `solid` | Solid fill for stronger emphasis, gray without a role | 1.0.0 |
370
+ | `corner` | Pins the badge to the top inline-end corner of a `wu-badge-host` | 1.0.0 |
371
+
372
+ ### Parts
373
+
374
+ | Part | Description | Since |
375
+ | --- | --- | --- |
376
+ | `wu-badge-host` | Wrapper that a corner badge pins to | 1.0.0 |
377
+
378
+ ### Variables
379
+
380
+ | Variable | Default | Description | Since |
381
+ | --- | --- | --- | --- |
382
+ | `--wu-badge--Offset` | `50%` | How far a corner badge sits outside the host corner | 1.0.0 |
383
+ | `--wu-avatar--RingColor` | `var(--wu-bg-surface)` | Color of the ring around a corner badge, shared with avatar | 1.0.0 |
384
+
385
+ ### States
386
+
387
+ | State | Description | Since |
388
+ | --- | --- | --- |
389
+ | `:empty` | A corner badge with no content is hidden | 1.0.0 |
390
+
391
+ ## breadcrumb
392
+
393
+ Trail of links that shows where the page sits in the site.
394
+
395
+ Wrap an ordered list in a `nav` with an `aria-label`, and mark the last item with `aria-current="page"`.
396
+
397
+ - Root class: `wu-breadcrumb`
398
+ - Sass entry: `@use "@wovenui/components/breadcrumb";`
399
+ - Needs: none
400
+ - JavaScript: none, CSS only
401
+ - Since: 1.0.0
402
+
403
+ ### Markup
404
+
405
+ ```html
406
+ <nav class="wu-breadcrumb" aria-label="Breadcrumb">
407
+ <ol class="wu-breadcrumb-list">
408
+ <li class="wu-breadcrumb-item"><a class="wu-breadcrumb-link" href="/">Home</a></li>
409
+ <li class="wu-breadcrumb-item"><a class="wu-breadcrumb-link" href="/projects/">Projects</a></li>
410
+ <li class="wu-breadcrumb-item"><span aria-current="page">Website</span></li>
411
+ </ol>
412
+ </nav>
413
+ ```
414
+
415
+ ```html
416
+ <nav class="wu-breadcrumb slash sm" aria-label="Breadcrumb">
417
+ <ol class="wu-breadcrumb-list">
418
+ <li class="wu-breadcrumb-item"><a class="wu-breadcrumb-link" href="/">Home</a></li>
419
+ <li class="wu-breadcrumb-item"><span aria-current="page">Docs</span></li>
420
+ </ol>
421
+ </nav>
422
+ ```
423
+
424
+ ### Modifiers
425
+
426
+ | Modifier | Description | Since |
427
+ | --- | --- | --- |
428
+ | `slash` | A slash separator instead of the chevron | 1.0.0 |
429
+ | `sm` | Smaller font size | 1.0.0 |
430
+
431
+ ### Parts
432
+
433
+ | Part | Description | Since |
434
+ | --- | --- | --- |
435
+ | `wu-breadcrumb-list` | The `ol` that lays the items out and wraps them | 1.0.0 |
436
+ | `wu-breadcrumb-item` | One `li` in the trail, with a separator before it | 1.0.0 |
437
+ | `wu-breadcrumb-link` | Underlined link to a parent page | 1.0.0 |
438
+
439
+ ### States
440
+
441
+ | State | Description | Since |
442
+ | --- | --- | --- |
443
+ | `aria-current="page"` | The current page: regular text weight and color, no underline | 1.0.0 |
444
+
445
+ ## btn-group
446
+
447
+ Joins buttons into one segmented control, as a split button, a toggle set or a radio set.
448
+
449
+ Split divider: use `brand`, `danger` or `quiet` on both buttons of a pair. Checkable: use `role="group"` with checkboxes or `role="radiogroup"` with radios.
450
+
451
+ The checked label takes the `aria-pressed` look; arrow keys are native for radios. `animated` on a button gives it the press from `wu-btn`.
452
+
453
+ - Root class: `wu-btn-group`
454
+ - Sass entry: `@use "@wovenui/components/btn-group";`
455
+ - Needs: `button`
456
+ - JavaScript: none, CSS only
457
+ - Since: 1.0.0
458
+
459
+ ### Markup
460
+
461
+ ```html
462
+ <div class="wu-btn-group">
463
+ <button class="wu-btn brand" type="button">Save</button>
464
+ <button class="wu-btn brand caret" type="button" popovertarget="more-save" aria-label="More save options"></button>
465
+ </div>
466
+ <div id="more-save" popover>Save as draft</div>
467
+ ```
468
+
469
+ ```html
470
+ <div class="wu-btn-group" role="radiogroup" aria-label="Align">
471
+ <label class="wu-btn"><input type="radio" name="align" class="sr-only" checked> Left</label>
472
+ <label class="wu-btn"><input type="radio" name="align" class="sr-only"> Right</label>
473
+ </div>
474
+ ```
475
+
476
+ ```html
477
+ <div class="wu-btn-group vertical" role="group" aria-label="View">
478
+ <button class="wu-btn" type="button" aria-pressed="true">List</button>
479
+ <button class="wu-btn" type="button" aria-pressed="false">Grid</button>
480
+ </div>
481
+ ```
482
+
483
+ ### Modifiers
484
+
485
+ | Modifier | Description | Since |
486
+ | --- | --- | --- |
487
+ | `vertical` | Stacks the buttons in a column | 1.0.0 |
488
+ | `brand`, `danger`, `quiet` | On two neighbor buttons: a divider line between them, for split buttons | 1.0.0 |
489
+
490
+ ### States
491
+
492
+ | State | Description | Since |
493
+ | --- | --- | --- |
494
+ | `aria-pressed="true"` | Toggle on: selected tint and an inner ring; a checked input inside the label does the same | 1.0.0 |
495
+
496
+ ## button
497
+
498
+ Buttons for actions, in roles and sizes, with a caret for menus and split buttons.
499
+
500
+ A `button` element is the default; an `a` element with `wu-btn` looks the same for navigation.
501
+
502
+ Inside a `wu-field-group sm` or `lg`, a button without its own size takes the group size.
503
+
504
+ - Root class: `wu-btn`
505
+ - Sass entry: `@use "@wovenui/components/button";`
506
+ - Needs: none
507
+ - JavaScript: none, CSS only
508
+ - Since: 1.0.0
509
+
510
+ ### Markup
511
+
512
+ ```html
513
+ <button class="wu-btn" type="button">Cancel</button>
514
+ <button class="wu-btn brand" type="button">Save</button>
515
+ <button class="wu-btn danger subtle sm" type="button">Delete</button>
516
+ ```
517
+
518
+ ```html
519
+ <button class="wu-btn icon round" type="button" aria-label="Settings">
520
+ <svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4" /></svg>
521
+ </button>
522
+ <button class="wu-btn brand" type="button" aria-busy="true">Saving</button>
523
+ ```
524
+
525
+ ```html
526
+ <button class="wu-btn caret" type="button" popovertarget="actions">Actions</button>
527
+ <div id="actions" popover>Menu content</div>
528
+ ```
529
+
530
+ ### Modifiers
531
+
532
+ | Modifier | Description | Since |
533
+ | --- | --- | --- |
534
+ | `brand` | Solid brand fill for the main action | 1.0.0 |
535
+ | `danger` | Solid danger fill for destructive actions | 1.0.0 |
536
+ | `subtle` | With `brand` or `danger`: tinted fill and border, solid on hover | 1.0.0 |
537
+ | `quiet` | No border or fill until hover | 1.0.0 |
538
+ | `link` | Looks like a link, no padding, keeps the control height | 1.0.0 |
539
+ | `round` | Pill shape; a circle when the button is empty or holds only an svg | 1.0.0 |
540
+ | `icon` | Square button for one svg and no text; give it an `aria-label` | 1.0.0 |
541
+ | `caret` | Adds a caret after the label; an empty caret button with an `aria-label` is a narrow caret-only button for split buttons | 1.0.0 |
542
+ | `sm`, `lg` | Small or large control height and font size | 1.0.0 |
543
+ | `animated` | Spring press scale when the user allows motion; `wu-animated` on an ancestor or `wu-btn-group animated` does the same | 1.0.0 |
544
+
545
+ ### Variables
546
+
547
+ | Variable | Default | Description | Since |
548
+ | --- | --- | --- | --- |
549
+ | `--wu-btn--CaretSize` | `0.4em` | Size of the caret | 1.0.0 |
550
+
551
+ ### States
552
+
553
+ | State | Description | Since |
554
+ | --- | --- | --- |
555
+ | `disabled`, `aria-disabled="true"` | Half opacity, no pointer events | 1.0.0 |
556
+ | `aria-busy="true"` | Loading: blocks pointer clicks and keeps full opacity | 1.0.0 |
557
+ | `:popover-open` | The caret turns while the popover right after the button is open | 1.0.0 |
558
+
559
+ ## card
560
+
561
+ Bordered surface that groups a header, a body and a footer.
562
+
563
+ Put one real link, usually in the title, on `wu-card-link` to make the whole card clickable. Other controls stay clickable above it.
564
+
565
+ - Root class: `wu-card`
566
+ - Sass entry: `@use "@wovenui/components/card";`
567
+ - Needs: none
568
+ - JavaScript: none, CSS only
569
+ - Since: 1.0.0
570
+
571
+ ### Markup
572
+
573
+ ```html
574
+ <div class="wu-card">
575
+ <div class="wu-card-header"><h3 class="wu-card-title">Plan</h3></div>
576
+ <div class="wu-card-body">Everything you need to start.</div>
577
+ <div class="wu-card-footer"><button class="wu-btn brand" type="button">Choose</button></div>
578
+ </div>
579
+ ```
580
+
581
+ ```html
582
+ <div class="wu-card">
583
+ <div class="wu-card-body">
584
+ <h3 class="wu-card-title"><a class="wu-card-link" href="/docs/">Read the docs</a></h3>
585
+ <p>Guides for every component.</p>
586
+ </div>
587
+ </div>
588
+ ```
589
+
590
+ ### Parts
591
+
592
+ | Part | Description | Since |
593
+ | --- | --- | --- |
594
+ | `wu-card-header` | Top row for a title and actions, with a bottom border | 1.0.0 |
595
+ | `wu-card-title` | Heading inside the header or body | 1.0.0 |
596
+ | `wu-card-body` | Main content; it grows so footers line up across a row of cards | 1.0.0 |
597
+ | `wu-card-footer` | Bottom row for actions, aligned to the end, with a top border | 1.0.0 |
598
+ | `wu-card-link` | Link stretched over the whole card | 1.0.0 |
599
+
600
+ ### Variables
601
+
602
+ | Variable | Default | Description | Since |
603
+ | --- | --- | --- | --- |
604
+ | `--wu-card--Padding` | `var(--wu-space-5)` | Padding of the header, body and footer | 1.0.0 |
605
+
606
+ ## checkbox
607
+
608
+ Custom checkbox with a label, a check mark and an indeterminate dash.
609
+
610
+ The box is 1em, so it follows the root type size. The `wu-checkbox-input` part also works alone with an `aria-label`.
611
+
612
+ Inside a `wu-field-group sm` or `lg`, a checkbox without its own size takes the group size.
613
+
614
+ - Root class: `wu-checkbox`
615
+ - Sass entry: `@use "@wovenui/components/checkbox";`
616
+ - Needs: none
617
+ - JavaScript: none, CSS only
618
+ - Since: 1.0.0
619
+
620
+ ### Markup
621
+
622
+ ```html
623
+ <label class="wu-checkbox">
624
+ <input type="checkbox" class="wu-checkbox-input" name="terms" checked>
625
+ <span class="wu-checkbox-label">I accept the terms</span>
626
+ </label>
627
+ ```
628
+
629
+ ```html
630
+ <label class="wu-checkbox sm">
631
+ <input type="checkbox" class="wu-checkbox-input" name="news" disabled>
632
+ <span class="wu-checkbox-label">Send the newsletter</span>
633
+ </label>
634
+ <input type="checkbox" class="wu-checkbox-input" name="all" aria-label="Select all">
635
+ ```
636
+
637
+ ### Modifiers
638
+
639
+ | Modifier | Description | Since |
640
+ | --- | --- | --- |
641
+ | `sm`, `lg` | Small or large font size, and so box size | 1.0.0 |
642
+
643
+ ### Parts
644
+
645
+ | Part | Description | Since |
646
+ | --- | --- | --- |
647
+ | `wu-checkbox-input` | The `input` drawn as a box | 1.0.0 |
648
+ | `wu-checkbox-label` | Label text next to the box | 1.0.0 |
649
+
650
+ ### States
651
+
652
+ | State | Description | Since |
653
+ | --- | --- | --- |
654
+ | `checked` | Brand fill with a check mark | 1.0.0 |
655
+ | `indeterminate` | Brand fill with a dash | 1.0.0 |
656
+ | `disabled` | Half opacity and a not-allowed cursor | 1.0.0 |
657
+ | `aria-invalid="true"` | Danger edge that stays on hover and when checked | 1.0.0 |
658
+
659
+ ## code-block
660
+
661
+ Scrollable block for code, with edge shadows when the content overflows.
662
+
663
+ Use `pre` with `code` inside, and add `tabindex="0"` and an `aria-label` so keyboard users can scroll it.
664
+
665
+ For line numbers, wrap each line in a `wu-code-block-line` span. The numbers are not copied or read aloud.
666
+
667
+ - Root class: `wu-code-block`
668
+ - Sass entry: `@use "@wovenui/components/code-block";`
669
+ - Needs: none
670
+ - JavaScript: none, CSS only
671
+ - Since: 1.0.0
672
+
673
+ ### Markup
674
+
675
+ ```html
676
+ <pre class="wu-code-block" tabindex="0" aria-label="Install command"><code>pnpm add @wovenui/components</code></pre>
677
+ ```
678
+
679
+ ```html
680
+ <pre class="wu-code-block" tabindex="0" aria-label="Example"><code><span class="wu-code-block-line">const a = 1;</span>
681
+ <span class="wu-code-block-line">const b = 2;</span></code></pre>
682
+ ```
683
+
684
+ ### Parts
685
+
686
+ | Part | Description | Since |
687
+ | --- | --- | --- |
688
+ | `wu-code-block-line` | Inline span for one line, with an optional line number | 1.0.0 |
689
+
690
+ ### Variables
691
+
692
+ | Variable | Default | Description | Since |
693
+ | --- | --- | --- | --- |
694
+ | `--wu-code-block--Background` | `var(--wu-bg-alternate)` | Background of the block and of its scroll shadow covers | 1.0.0 |
695
+ | `--wu-code-block--FontFamily` | `ui-monospace` | Font family; the default is a system monospace stack | 1.0.0 |
696
+
697
+ ### States
698
+
699
+ | State | Description | Since |
700
+ | --- | --- | --- |
701
+ | `:focus-visible` | Focus ring, for the scrollable block | 1.0.0 |
702
+
703
+ ## confirm-modal
704
+
705
+ Dialog that asks the user to confirm or cancel an action, with a title, a message and two buttons.
706
+
707
+ Builds on `wu-modal` for open, close, backdrop and motion, and on `wu-btn` for the actions. Use `role="alertdialog"`.
708
+
709
+ The subject is optional and names the object. `danger` gives the message full weight; the confirming button takes `wu-btn danger`. `dialog.returnValue` holds the value of the button that closed it.
710
+
711
+ - Root class: `wu-confirm-modal`
712
+ - Sass entry: `@use "@wovenui/components/confirm-modal";`
713
+ - Needs: `modal`, `button`
714
+ - JavaScript: none, CSS only
715
+ - Since: 1.0.0
716
+
717
+ ### Markup
718
+
719
+ ```html
720
+ <button class="wu-btn" type="button" commandfor="c1" command="show-modal">Delete</button>
721
+ <dialog class="wu-modal wu-confirm-modal danger" id="c1" role="alertdialog" aria-labelledby="c1-title" aria-describedby="c1-subject c1-message">
722
+ <h2 class="wu-confirm-modal-title" id="c1-title">Delete project?</h2>
723
+ <p class="wu-confirm-modal-subject" id="c1-subject">Website redesign</p>
724
+ <p class="wu-confirm-modal-message" id="c1-message">This cannot be undone.</p>
725
+ <form class="wu-confirm-modal-actions" method="dialog">
726
+ <button class="wu-btn" value="cancel" autofocus>Cancel</button>
727
+ <button class="wu-btn danger" value="confirm">Delete</button>
728
+ </form>
729
+ </dialog>
730
+ ```
731
+
732
+ ### Modifiers
733
+
734
+ | Modifier | Description | Since |
735
+ | --- | --- | --- |
736
+ | `danger` | Message at full contrast and body size, for destructive actions | 1.0.0 |
737
+
738
+ ### Parts
739
+
740
+ | Part | Description | Since |
741
+ | --- | --- | --- |
742
+ | `wu-confirm-modal-title` | Heading that names the question | 1.0.0 |
743
+ | `wu-confirm-modal-subject` | Optional line that names the object the action applies to | 1.0.0 |
744
+ | `wu-confirm-modal-message` | Supporting text about the consequence | 1.0.0 |
745
+ | `wu-confirm-modal-actions` | Form that holds the buttons, aligned to the end | 1.0.0 |
746
+
747
+ ### Variables
748
+
749
+ | Variable | Default | Description | Since |
750
+ | --- | --- | --- | --- |
751
+ | `--wu-confirm-modal--Width` | `26rem` | Width of the dialog | 1.0.0 |
752
+
753
+ ## data-table
754
+
755
+ Sortable table: click a header to sort rows by text or number.
756
+
757
+ Add it to a `wu-table`; the sort button and arrows come from the table component, and the kit script sorts the first `tbody` and sets `aria-sort`. A `data-sort-value` on a cell wins over its text.
758
+
759
+ The script adds a `wu-data-table-status` live region after the table and announces each sort there.
760
+
761
+ - Root class: `wu-data-table`
762
+ - Sass entry: `@use "@wovenui/components/data-table";`
763
+ - Needs: `table`
764
+ - JavaScript: required: `initDataTable` from `@wovenui/components/js`
765
+ - Since: 1.0.0
766
+
767
+ ### Markup
768
+
769
+ ```html
770
+ <table class="wu-table wu-data-table">
771
+ <thead>
772
+ <tr>
773
+ <th scope="col" aria-sort="ascending"><button class="wu-table-sort" type="button">Name</button></th>
774
+ <th scope="col" class="wu-table-num"><button class="wu-table-sort" type="button">Amount</button></th>
775
+ </tr>
776
+ </thead>
777
+ <tbody>
778
+ <tr><td>Ada</td><td class="wu-table-num" data-sort-value="1200">$1,200</td></tr>
779
+ <tr><td>Grace</td><td class="wu-table-num" data-sort-value="950">$950</td></tr>
780
+ </tbody>
781
+ </table>
782
+ ```
783
+
784
+ ### Modifiers
785
+
786
+ | Modifier | Description | Since |
787
+ | --- | --- | --- |
788
+ | `animated` | Rows glide to their new place when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
789
+
790
+ ### Parts
791
+
792
+ | Part | Description | Since |
793
+ | --- | --- | --- |
794
+ | `wu-data-table-status` | Visually hidden live region added by the script to announce each sort | 1.0.0 |
795
+ | `wu-data-table-row` | View transition class, not an HTML class, set on body rows while they move; target `::view-transition-group(*.wu-data-table-row)` to restyle the glide | 1.0.0 |
796
+
797
+ ### States
798
+
799
+ | State | Description | Since |
800
+ | --- | --- | --- |
801
+ | `aria-sort="ascending"` | Header cell sorted ascending, set by the script | 1.0.0 |
802
+ | `aria-sort="descending"` | Header cell sorted descending, set by the script | 1.0.0 |
803
+ | `aria-busy="true"` | Set while rows move: progress cursor, and the row animation runs | 1.0.0 |
804
+
805
+ ## desc-list
806
+
807
+ Description list of terms and values, plain, divided, bordered or in columns.
808
+
809
+ Use a `dl`; wrap each pair in a `div` for divided, bordered and column layouts.
810
+
811
+ - Root class: `wu-desc-list`
812
+ - Sass entry: `@use "@wovenui/components/desc-list";`
813
+ - Needs: none
814
+ - JavaScript: none, CSS only
815
+ - Since: 1.0.0
816
+
817
+ ### Markup
818
+
819
+ ```html
820
+ <dl class="wu-desc-list horizontal divided">
821
+ <div><dt>Name</dt><dd>Ada Lovelace</dd></div>
822
+ <div><dt>Role</dt><dd>Engineer</dd></div>
823
+ </dl>
824
+ ```
825
+
826
+ ```html
827
+ <dl class="wu-desc-list bordered">
828
+ <div><dt>Plan</dt><dd>Team</dd></div>
829
+ <div><dt>Seats</dt><dd>12</dd></div>
830
+ </dl>
831
+ ```
832
+
833
+ ```html
834
+ <dl class="wu-desc-list columns">
835
+ <div><dt>City</dt><dd>Berlin</dd></div>
836
+ <div><dt>Country</dt><dd>Germany</dd></div>
837
+ <div><dt>Timezone</dt><dd>CET</dd></div>
838
+ </dl>
839
+ ```
840
+
841
+ ### Modifiers
842
+
843
+ | Modifier | Description | Since |
844
+ | --- | --- | --- |
845
+ | `horizontal` | Terms in a column beside their descriptions | 1.0.0 |
846
+ | `divided` | Lines between the `div` pairs | 1.0.0 |
847
+ | `bordered` | Table look: lines around every cell, terms on the alternate background | 1.0.0 |
848
+ | `columns` | Several pairs per row; with `bordered`, each pair is a term cell beside its description | 1.0.0 |
849
+
850
+ ### Variables
851
+
852
+ | Variable | Default | Description | Since |
853
+ | --- | --- | --- | --- |
854
+ | `--wu-desc-list--TermWidth` | `fit-content(40%)` | Width of the term column in `horizontal` and `bordered` | 1.0.0 |
855
+ | `--wu-desc-list--Columns` | `3` | Most pairs per row in `columns` | 1.0.0 |
856
+ | `--wu-desc-list--ColumnWidth` | `10rem` | Narrowest a pair gets in `columns` before a row holds fewer | 1.0.0 |
857
+
858
+ ## divider
859
+
860
+ Horizontal or vertical separator line, optionally with a text label between two lines.
861
+
862
+ Use an `hr` element. `aria-orientation="vertical"` turns it into a vertical line between inline items. With content, the text is read and the lines are decoration.
863
+
864
+ - Root class: `wu-divider`
865
+ - Sass entry: `@use "@wovenui/components/divider";`
866
+ - Needs: none
867
+ - JavaScript: none, CSS only
868
+ - Since: 1.0.0
869
+
870
+ ### Markup
871
+
872
+ ```html
873
+ <hr class="wu-divider">
874
+ <hr class="wu-divider dashed">
875
+ ```
876
+
877
+ ```html
878
+ <div class="wu-divider">or</div>
879
+ ```
880
+
881
+ ```html
882
+ <span>Edit</span><hr class="wu-divider" aria-orientation="vertical"><span>Share</span>
883
+ ```
884
+
885
+ ### Modifiers
886
+
887
+ | Modifier | Description | Since |
888
+ | --- | --- | --- |
889
+ | `dashed` | Dashed line, also on the lines beside a label | 1.0.0 |
890
+
891
+ ### States
892
+
893
+ | State | Description | Since |
894
+ | --- | --- | --- |
895
+ | `aria-orientation="vertical"` | Vertical line that stretches to the row height | 1.0.0 |
896
+ | `:not(:empty)` | With content: the text sits between two lines | 1.0.0 |
897
+
898
+ ## dot
899
+
900
+ Small status dot for use next to status text, tinted by role.
901
+
902
+ Add `aria-hidden="true"` and put the meaning in the text beside it. `success`, `warning` and `danger` also change the shape (check, triangle, octagon), so color is never the only signal.
903
+
904
+ A corner dot pins to a `wu-badge-host`, the same wrapper a corner badge uses, and reads `--wu-badge--Offset` and `--wu-avatar--RingColor` from the badge and avatar.
905
+
906
+ - Root class: `wu-dot`
907
+ - Sass entry: `@use "@wovenui/components/dot";`
908
+ - Needs: none
909
+ - JavaScript: none, CSS only
910
+ - Since: 1.0.0
911
+
912
+ ### Markup
913
+
914
+ ```html
915
+ <span class="wu-dot success" aria-hidden="true"></span> Online
916
+ ```
917
+
918
+ ```html
919
+ <span class="wu-dot danger animated" aria-hidden="true"></span> Live
920
+ ```
921
+
922
+ ```html
923
+ <span class="wu-badge-host">
924
+ <button class="wu-btn" type="button" aria-label="Inbox, new messages">Inbox</button>
925
+ <span class="wu-dot corner brand" aria-hidden="true"></span>
926
+ </span>
927
+ ```
928
+
929
+ ### Modifiers
930
+
931
+ | Modifier | Description | Since |
932
+ | --- | --- | --- |
933
+ | `brand`, `success`, `danger`, `warning`, `info` | Role color; success, warning and danger also change the shape | 1.0.0 |
934
+ | `corner` | Pins the dot to the top inline-end corner of a `wu-badge-host` | 1.0.0 |
935
+ | `animated` | Fading ring for live status when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
936
+
937
+ ### Variables
938
+
939
+ | Variable | Default | Description | Since |
940
+ | --- | --- | --- | --- |
941
+ | `--wu-dot--Size` | `var(--wu-space-2)` | Width and height of the dot | 1.0.0 |
942
+
943
+ ## drawer
944
+
945
+ Side panel on the native `dialog` element, docked to the inline end, the inline start or the bottom.
946
+
947
+ Opens with invoker commands (`commandfor` and `command`), no script. With `show-modal` the page does not scroll while it is open; the page keeps its scrollbar gutter so it does not shift. `show` opens it non-modal and leaves the page alone.
948
+
949
+ Docks to the inline end by default; `inline-start` or `bottom` moves it. Right-to-left pages mirror the slide.
950
+
951
+ - Root class: `wu-drawer`
952
+ - Sass entry: `@use "@wovenui/components/drawer";`
953
+ - Needs: none
954
+ - JavaScript: none, CSS only
955
+ - Since: 1.0.0
956
+
957
+ ### Markup
958
+
959
+ ```html
960
+ <button class="wu-btn" type="button" commandfor="d1" command="show-modal">Filters</button>
961
+ <dialog class="wu-drawer" id="d1" aria-labelledby="d1-title" closedby="any">
962
+ <header class="wu-drawer-header">
963
+ <h2 class="wu-drawer-title" id="d1-title">Filters</h2>
964
+ <button class="wu-drawer-close" type="button" commandfor="d1" command="close" aria-label="Close"></button>
965
+ </header>
966
+ <div class="wu-drawer-body">Content</div>
967
+ <form class="wu-drawer-footer" method="dialog">
968
+ <button class="wu-btn brand">Apply</button>
969
+ </form>
970
+ </dialog>
971
+ ```
972
+
973
+ ```html
974
+ <dialog class="wu-drawer bottom animated" id="d2" aria-label="Details" closedby="any">
975
+ <div class="wu-drawer-body">Content</div>
976
+ </dialog>
977
+ ```
978
+
979
+ ### Modifiers
980
+
981
+ | Modifier | Description | Since |
982
+ | --- | --- | --- |
983
+ | `inline-start` | Docks to the inline start instead of the inline end | 1.0.0 |
984
+ | `bottom` | Docks to the bottom edge with rounded top corners, up to `--wu-drawer--MaxHeight` | 1.0.0 |
985
+ | `animated` | Slides in from its edge and fades the backdrop when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
986
+
987
+ ### Parts
988
+
989
+ | Part | Description | Since |
990
+ | --- | --- | --- |
991
+ | `wu-drawer-header` | Top row with the title and close button | 1.0.0 |
992
+ | `wu-drawer-title` | Heading in the header | 1.0.0 |
993
+ | `wu-drawer-body` | Scrolling content area that fills the drawer | 1.0.0 |
994
+ | `wu-drawer-footer` | Bottom row for actions, aligned to the inline end | 1.0.0 |
995
+ | `wu-drawer-close` | Close button drawn by the shared close style; give it an `aria-label` | 1.0.0 |
996
+
997
+ ### Variables
998
+
999
+ | Variable | Default | Description | Since |
1000
+ | --- | --- | --- | --- |
1001
+ | `--wu-drawer--Width` | `24rem` | Drawer width when docked to a side | 1.0.0 |
1002
+ | `--wu-drawer--MaxHeight` | `75dvh` | Maximum height of a bottom drawer | 1.0.0 |
1003
+ | `--wu-drawer--Backdrop` | `50% black, 70% in dark themes` | Color behind a modal drawer | 1.0.0 |
1004
+
1005
+ ### States
1006
+
1007
+ | State | Description | Since |
1008
+ | --- | --- | --- |
1009
+ | `open` | Shown as a flex column so the body scrolls between header and footer | 1.0.0 |
1010
+ | `:modal` | Locks page scroll while the drawer is open with `show-modal` | 1.0.0 |
1011
+
1012
+ ## dropdown
1013
+
1014
+ Menu of links and buttons in a native popover, anchored under its trigger.
1015
+
1016
+ A button with `popovertarget` opens it; `commandfor` with `command="toggle-popover"` works as the trigger too. Esc and a click outside close it. `popovertargetaction="hide"` on a button item closes the menu after its click handler runs.
1017
+
1018
+ Opens under the trigger where anchor positioning exists and flips when there is no room; elsewhere it is centered in the viewport. Engines without implicit anchors need `anchor-name` on the trigger and `position-anchor` on the menu, with the same name.
1019
+
1020
+ `inline-end` aligns the menu's inline end with the trigger's, as for the caret of a split button.
1021
+
1022
+ - Root class: `wu-dropdown`
1023
+ - Sass entry: `@use "@wovenui/components/dropdown";`
1024
+ - Needs: none
1025
+ - JavaScript: none, CSS only
1026
+ - Since: 1.0.0
1027
+
1028
+ ### Markup
1029
+
1030
+ ```html
1031
+ <button class="wu-btn caret" type="button" popovertarget="menu1">Actions</button>
1032
+ <div class="wu-dropdown" id="menu1" popover>
1033
+ <a class="wu-dropdown-item" href="/edit">Edit</a>
1034
+ <button class="wu-dropdown-item" type="button" popovertarget="menu1" popovertargetaction="hide">Duplicate</button>
1035
+ <hr class="wu-dropdown-separator">
1036
+ <button class="wu-dropdown-item danger" type="button" popovertarget="menu1" popovertargetaction="hide">Delete</button>
1037
+ </div>
1038
+ ```
1039
+
1040
+ ### Modifiers
1041
+
1042
+ | Modifier | Description | Since |
1043
+ | --- | --- | --- |
1044
+ | `inline-end` | Aligns the menu's inline end with the trigger's inline end | 1.0.0 |
1045
+ | `danger` | On an item: danger text and a danger hover tint | 1.0.0 |
1046
+ | `animated` | Fades and slides in and out when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
1047
+
1048
+ ### Parts
1049
+
1050
+ | Part | Description | Since |
1051
+ | --- | --- | --- |
1052
+ | `wu-dropdown-item` | Link or button row in the menu; an `svg` child is sized as an icon | 1.0.0 |
1053
+ | `wu-dropdown-separator` | Line between groups of items; use an `hr` | 1.0.0 |
1054
+
1055
+ ### Variables
1056
+
1057
+ | Variable | Default | Description | Since |
1058
+ | --- | --- | --- | --- |
1059
+ | `--wu-dropdown--MinWidth` | `10rem` | Minimum menu width | 1.0.0 |
1060
+ | `--wu-dropdown--MaxHeight` | `20rem` | Maximum menu height before it scrolls | 1.0.0 |
1061
+
1062
+ ### States
1063
+
1064
+ | State | Description | Since |
1065
+ | --- | --- | --- |
1066
+ | `disabled`, `aria-disabled="true"` | On an item: half opacity, no pointer events | 1.0.0 |
1067
+ | `:popover-open` | A plain trigger button right before the menu takes a hover look while it is open | 1.0.0 |
1068
+
1069
+ ## empty-state
1070
+
1071
+ Centered message for a view with no content or a finished result, with an icon, title, text and actions.
1072
+
1073
+ The icon slot holds the app's own icon; left empty, it draws a calm empty-box shape. Result roles tint the slot, and an empty slot draws the alert's marker shape.
1074
+
1075
+ The title text carries the meaning ("Payment received"); the shape repeats it.
1076
+
1077
+ - Root class: `wu-empty-state`
1078
+ - Sass entry: `@use "@wovenui/components/empty-state";`
1079
+ - Needs: none
1080
+ - JavaScript: none, CSS only
1081
+ - Since: 1.0.0
1082
+
1083
+ ### Markup
1084
+
1085
+ ```html
1086
+ <div class="wu-empty-state">
1087
+ <span class="wu-empty-state-icon" aria-hidden="true"></span>
1088
+ <h3 class="wu-empty-state-title">No projects yet</h3>
1089
+ <p class="wu-empty-state-text">Create a project to get started.</p>
1090
+ <div class="wu-empty-state-actions">
1091
+ <button class="wu-btn brand" type="button">New project</button>
1092
+ </div>
1093
+ </div>
1094
+ ```
1095
+
1096
+ ```html
1097
+ <div class="wu-empty-state success sm dashed">
1098
+ <span class="wu-empty-state-icon" aria-hidden="true"></span>
1099
+ <h3 class="wu-empty-state-title">Payment received</h3>
1100
+ </div>
1101
+ ```
1102
+
1103
+ ### Modifiers
1104
+
1105
+ | Modifier | Description | Since |
1106
+ | --- | --- | --- |
1107
+ | `dashed` | Dashed border and radius around the whole state | 1.0.0 |
1108
+ | `sm` | Compact size for use inside a card, panel or table | 1.0.0 |
1109
+ | `success`, `warning`, `danger`, `info` | Result: tints the icon slot; an empty slot draws the marker shape | 1.0.0 |
1110
+
1111
+ ### Parts
1112
+
1113
+ | Part | Description | Since |
1114
+ | --- | --- | --- |
1115
+ | `wu-empty-state-icon` | Round icon slot; empty, it draws a shape | 1.0.0 |
1116
+ | `wu-empty-state-title` | Heading | 1.0.0 |
1117
+ | `wu-empty-state-text` | Supporting text, limited to a readable width | 1.0.0 |
1118
+ | `wu-empty-state-actions` | Centered row for buttons | 1.0.0 |
1119
+
1120
+ ### States
1121
+
1122
+ | State | Description | Since |
1123
+ | --- | --- | --- |
1124
+ | `:empty` | An empty icon slot draws the empty-box or marker shape | 1.0.0 |
1125
+
1126
+ ## field-group
1127
+
1128
+ Group of fields in a `fieldset`, stacked by default or side by side.
1129
+
1130
+ Use a `fieldset` with a `legend` of class `wu-field-group-legend`. A `sm` or `lg` on the group sizes the inputs, affixes and buttons inside it that have no size of their own; that rule lives in the input and button partials.
1131
+
1132
+ - Root class: `wu-field-group`
1133
+ - Sass entry: `@use "@wovenui/components/field-group";`
1134
+ - Needs: none
1135
+ - JavaScript: none, CSS only
1136
+ - Since: 1.0.0
1137
+
1138
+ ### Markup
1139
+
1140
+ ```html
1141
+ <fieldset class="wu-field-group">
1142
+ <legend class="wu-field-group-legend">Contact</legend>
1143
+ <div class="wu-field">
1144
+ <label class="wu-field-label" for="g-name">Name</label>
1145
+ <input class="wu-input" id="g-name" type="text">
1146
+ </div>
1147
+ <div class="wu-field">
1148
+ <label class="wu-field-label" for="g-mail">Email</label>
1149
+ <input class="wu-input" id="g-mail" type="email">
1150
+ </div>
1151
+ </fieldset>
1152
+ ```
1153
+
1154
+ ```html
1155
+ <fieldset class="wu-field-group horizontal">
1156
+ <legend class="wu-field-group-legend">Dates</legend>
1157
+ <div class="wu-field">
1158
+ <label class="wu-field-label" for="g-from">From</label>
1159
+ <input class="wu-input" id="g-from" type="date">
1160
+ </div>
1161
+ <div class="wu-field">
1162
+ <label class="wu-field-label" for="g-to">To</label>
1163
+ <input class="wu-input" id="g-to" type="date">
1164
+ </div>
1165
+ </fieldset>
1166
+ ```
1167
+
1168
+ ### Modifiers
1169
+
1170
+ | Modifier | Description | Since |
1171
+ | --- | --- | --- |
1172
+ | `horizontal` | Fields side by side, wrapping when narrower than `--wu-field-group--MinWidth` | 1.0.0 |
1173
+
1174
+ ### Parts
1175
+
1176
+ | Part | Description | Since |
1177
+ | --- | --- | --- |
1178
+ | `wu-field-group-legend` | Group title | 1.0.0 |
1179
+
1180
+ ### Variables
1181
+
1182
+ | Variable | Default | Description | Since |
1183
+ | --- | --- | --- | --- |
1184
+ | `--wu-field-group--MinWidth` | `12rem` | Width at which a field in a horizontal group wraps | 1.0.0 |
1185
+
1186
+ ## field-message
1187
+
1188
+ Hint, error or success text under a form control, with a marker shape for each role.
1189
+
1190
+ Link it from the control with `aria-describedby`. The words carry the meaning ("Error: ..."); the role adds color and a marker shape: `error` a larger octagon with an exclamation mark, `success` a check, `info` a small circle.
1191
+
1192
+ Inside a `wu-field`, an `error` message stays hidden until the control is invalid.
1193
+
1194
+ - Root class: `wu-field-message`
1195
+ - Sass entry: `@use "@wovenui/components/field-message";`
1196
+ - Needs: none
1197
+ - JavaScript: none, CSS only
1198
+ - Since: 1.0.0
1199
+
1200
+ ### Markup
1201
+
1202
+ ```html
1203
+ <p class="wu-field-message" id="hint">Use your work email.</p>
1204
+ <p class="wu-field-message error" id="err">Error: enter a valid email.</p>
1205
+ <p class="wu-field-message success" id="ok">Email confirmed.</p>
1206
+ <p class="wu-field-message info" id="note">We never share it.</p>
1207
+ ```
1208
+
1209
+ ### Modifiers
1210
+
1211
+ | Modifier | Description | Since |
1212
+ | --- | --- | --- |
1213
+ | `error`, `success`, `info` | Role color and marker shape; error is also medium weight | 1.0.0 |
1214
+
1215
+ ## field
1216
+
1217
+ Form field that stacks a label, a control and its messages, with a required marker and a floating label option.
1218
+
1219
+ The required marker comes from `required` or `aria-required="true"` on the control. An `error` message is hidden until the control is invalid. Its id joins `aria-describedby` only while it shows: server-rendered with `aria-invalid="true"`, or added by your script. Leave it out otherwise, so a hidden error is never announced; for `:user-invalid` the browser's own validation message speaks on submit.
1220
+
1221
+ `floating` needs `placeholder=" "` on the input and is not used with `horizontal`. `horizontal` puts the label beside the control from the `sm` breakpoint up.
1222
+
1223
+ - Root class: `wu-field`
1224
+ - Sass entry: `@use "@wovenui/components/field";`
1225
+ - Needs: none
1226
+ - JavaScript: none, CSS only
1227
+ - Since: 1.0.0
1228
+
1229
+ ### Markup
1230
+
1231
+ ```html
1232
+ <div class="wu-field">
1233
+ <label class="wu-field-label" for="f1">Email</label>
1234
+ <input class="wu-input" id="f1" type="email" required aria-describedby="f1-hint">
1235
+ <p class="wu-field-message" id="f1-hint">Hint</p>
1236
+ <p class="wu-field-message error" id="f1-error">Error: enter a valid email.</p>
1237
+ </div>
1238
+ ```
1239
+
1240
+ ```html
1241
+ <div class="wu-field floating animated">
1242
+ <label class="wu-field-label" for="f2">Name</label>
1243
+ <input class="wu-input" id="f2" type="text" placeholder=" ">
1244
+ </div>
1245
+ ```
1246
+
1247
+ ### Modifiers
1248
+
1249
+ | Modifier | Description | Since |
1250
+ | --- | --- | --- |
1251
+ | `horizontal` | Label beside the control from `sm` up, in a column of `--wu-field--LabelWidth` | 1.0.0 |
1252
+ | `error` | On a `wu-field-message` inside the field: hidden until the control is invalid | 1.0.0 |
1253
+ | `floating` | Label inside the input, moved up on focus or once filled | 1.0.0 |
1254
+ | `animated` | On a floating field: the label slides when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
1255
+
1256
+ ### Parts
1257
+
1258
+ | Part | Description | Since |
1259
+ | --- | --- | --- |
1260
+ | `wu-field-label` | Label above the control | 1.0.0 |
1261
+
1262
+ ### Variables
1263
+
1264
+ | Variable | Default | Description | Since |
1265
+ | --- | --- | --- | --- |
1266
+ | `--wu-field--LabelWidth` | `10rem` | Label column width in a horizontal field | 1.0.0 |
1267
+
1268
+ ### States
1269
+
1270
+ | State | Description | Since |
1271
+ | --- | --- | --- |
1272
+ | `:required`, `aria-required="true"` | On the control: adds a required marker to the label | 1.0.0 |
1273
+ | `aria-invalid="true"`, `:user-invalid` | On the control: shows the `error` message | 1.0.0 |
1274
+
1275
+ ## form-actions
1276
+
1277
+ Footer row for form buttons, start-aligned by default.
1278
+
1279
+ `wu-form-actions-end` inside the row pushes a set of buttons to the inline end.
1280
+
1281
+ - Root class: `wu-form-actions`
1282
+ - Sass entry: `@use "@wovenui/components/form-actions";`
1283
+ - Needs: none
1284
+ - JavaScript: none, CSS only
1285
+ - Since: 1.0.0
1286
+
1287
+ ### Markup
1288
+
1289
+ ```html
1290
+ <div class="wu-form-actions divided">
1291
+ <button class="wu-btn brand" type="submit">Save</button>
1292
+ <button class="wu-btn" type="reset">Reset</button>
1293
+ </div>
1294
+ ```
1295
+
1296
+ ```html
1297
+ <div class="wu-form-actions">
1298
+ <button class="wu-btn danger subtle" type="button">Delete</button>
1299
+ <div class="wu-form-actions-end">
1300
+ <button class="wu-btn" type="button">Cancel</button>
1301
+ <button class="wu-btn brand" type="submit">Save</button>
1302
+ </div>
1303
+ </div>
1304
+ ```
1305
+
1306
+ ### Modifiers
1307
+
1308
+ | Modifier | Description | Since |
1309
+ | --- | --- | --- |
1310
+ | `trailing` | Aligns the buttons to the inline end | 1.0.0 |
1311
+ | `centered` | Centers the buttons | 1.0.0 |
1312
+ | `divided` | Line and padding above the row | 1.0.0 |
1313
+
1314
+ ### Parts
1315
+
1316
+ | Part | Description | Since |
1317
+ | --- | --- | --- |
1318
+ | `wu-form-actions-end` | Set of buttons pushed to the inline end of the row | 1.0.0 |
1319
+
1320
+ ## info-row
1321
+
1322
+ One label and value pair, with a line between adjacent rows.
1323
+
1324
+ Works as a `div` with `dt` and `dd` inside a `dl`.
1325
+
1326
+ - Root class: `wu-info-row`
1327
+ - Sass entry: `@use "@wovenui/components/info-row";`
1328
+ - Needs: none
1329
+ - JavaScript: none, CSS only
1330
+ - Since: 1.0.0
1331
+
1332
+ ### Markup
1333
+
1334
+ ```html
1335
+ <div class="wu-info-row">
1336
+ <span class="wu-info-row-label">Tax</span>
1337
+ <span class="wu-info-row-value">$9.60</span>
1338
+ </div>
1339
+ <div class="wu-info-row total">
1340
+ <span class="wu-info-row-label">Total</span>
1341
+ <span class="wu-info-row-value">$129.60</span>
1342
+ </div>
1343
+ ```
1344
+
1345
+ ### Modifiers
1346
+
1347
+ | Modifier | Description | Since |
1348
+ | --- | --- | --- |
1349
+ | `total` | Strong line above, regular label text and a larger value | 1.0.0 |
1350
+
1351
+ ### Parts
1352
+
1353
+ | Part | Description | Since |
1354
+ | --- | --- | --- |
1355
+ | `wu-info-row-label` | Muted label | 1.0.0 |
1356
+ | `wu-info-row-value` | Value, end-aligned with tabular numbers | 1.0.0 |
1357
+
1358
+ ## input-group
1359
+
1360
+ Joined row of text addons, an input and buttons that share one border and radius.
1361
+
1362
+ Size the children (`wu-input sm`, `wu-btn sm`); addons stretch to the row height. Default buttons take the field border; `brand`, `danger`, `quiet` and `link` buttons keep their own look. The hovered, focused or invalid control rises above its neighbors.
1363
+
1364
+ - Root class: `wu-input-group`
1365
+ - Sass entry: `@use "@wovenui/components/input-group";`
1366
+ - Needs: none
1367
+ - JavaScript: none, CSS only
1368
+ - Since: 1.0.0
1369
+
1370
+ ### Markup
1371
+
1372
+ ```html
1373
+ <div class="wu-input-group">
1374
+ <span class="wu-input-group-text">https://</span>
1375
+ <input class="wu-input" type="text" aria-label="Website" placeholder="example.com">
1376
+ </div>
1377
+ ```
1378
+
1379
+ ```html
1380
+ <div class="wu-input-group">
1381
+ <input class="wu-input sm" type="search" aria-label="Search" placeholder="Search">
1382
+ <button class="wu-btn brand sm" type="button">Search</button>
1383
+ </div>
1384
+ ```
1385
+
1386
+ ### Parts
1387
+
1388
+ | Part | Description | Since |
1389
+ | --- | --- | --- |
1390
+ | `wu-input-group-text` | Text addon with the alternate background; an `svg` child is sized as an icon | 1.0.0 |
1391
+
1392
+ ## input
1393
+
1394
+ Text input in three sizes, with disabled, read-only and invalid looks, plus prefix, suffix and a clear button inside the field border.
1395
+
1396
+ Core base already sets the border, background, hover, focus ring, invalid border, disabled look, placeholder color, padding and the md height; the kit adds the radius, type size and sizes.
1397
+
1398
+ `wu-input-affix` draws the box and the input inside goes bare. Prefix and suffix sit on the inline start and end, so they swap in right-to-left pages. A `wu-select` can replace the input. `sm` and `lg` go on the wrapper. `wu-input-clear` is started by the kit script: it shows while the field has text, clears it and refocuses.
1399
+
1400
+ - Root class: `wu-input`
1401
+ - Sass entry: `@use "@wovenui/components/input";`
1402
+ - Needs: none
1403
+ - JavaScript: optional: `initInputClear` from `@wovenui/components/js`
1404
+ - Since: 1.0.0
1405
+
1406
+ ### Markup
1407
+
1408
+ ```html
1409
+ <input class="wu-input" type="text" aria-label="Name" placeholder="Name">
1410
+ <input class="wu-input sm" type="text" aria-label="Code" value="A-12" readonly>
1411
+ <input class="wu-input lg" type="email" aria-label="Email" aria-invalid="true">
1412
+ ```
1413
+
1414
+ ```html
1415
+ <div class="wu-input-affix">
1416
+ <span class="wu-input-prefix"><svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="11" cy="11" r="6" /></svg></span>
1417
+ <input class="wu-input" type="search" aria-label="Search">
1418
+ <button class="wu-input-clear" type="button" aria-label="Clear" hidden></button>
1419
+ <span class="wu-input-suffix">USD</span>
1420
+ </div>
1421
+ ```
1422
+
1423
+ ### Modifiers
1424
+
1425
+ | Modifier | Description | Since |
1426
+ | --- | --- | --- |
1427
+ | `sm`, `lg` | Small or large control height, padding and font size; also on `wu-input-affix`, and taken from a `wu-field-group sm` or `lg` when the control has no size | 1.0.0 |
1428
+
1429
+ ### Parts
1430
+
1431
+ | Part | Description | Since |
1432
+ | --- | --- | --- |
1433
+ | `wu-input-affix` | Wrapper that draws the field box around a bare input or select, with a prefix, suffix and clear button | 1.0.0 |
1434
+ | `wu-input-prefix` | Content before the input, such as an icon or currency symbol | 1.0.0 |
1435
+ | `wu-input-suffix` | Content after the input, such as a unit | 1.0.0 |
1436
+ | `wu-input-clear` | Clear button inside the affix; an empty button shows a multiplication sign; give it an `aria-label` | 1.0.0 |
1437
+
1438
+ ### States
1439
+
1440
+ | State | Description | Since |
1441
+ | --- | --- | --- |
1442
+ | `disabled` | Not-allowed cursor; an affix with a disabled control dims | 1.0.0 |
1443
+ | `readonly` | Alternate background | 1.0.0 |
1444
+ | `aria-invalid="true"`, `:user-invalid` | Danger border doubled to 2px, so the change is not color alone | 1.0.0 |
1445
+
1446
+ ## list
1447
+
1448
+ Vertical list of rows with a title, text and room for leading or trailing content.
1449
+
1450
+ Use a `ul` or `ol`; add `role="list"` to keep list semantics without markers. `wu-list-body` grows between leading and trailing content such as an avatar, badge or button.
1451
+
1452
+ - Root class: `wu-list`
1453
+ - Sass entry: `@use "@wovenui/components/list";`
1454
+ - Needs: none
1455
+ - JavaScript: none, CSS only
1456
+ - Since: 1.0.0
1457
+
1458
+ ### Markup
1459
+
1460
+ ```html
1461
+ <ul class="wu-list divided" role="list">
1462
+ <li class="wu-list-item">
1463
+ <div class="wu-list-body">
1464
+ <p class="wu-list-title">Ada Lovelace</p>
1465
+ <p class="wu-list-text">Engineer</p>
1466
+ </div>
1467
+ <span class="wu-badge success">Active</span>
1468
+ </li>
1469
+ <li class="wu-list-item">
1470
+ <div class="wu-list-body">
1471
+ <p class="wu-list-title">Grace Hopper</p>
1472
+ </div>
1473
+ </li>
1474
+ </ul>
1475
+ ```
1476
+
1477
+ ### Modifiers
1478
+
1479
+ | Modifier | Description | Since |
1480
+ | --- | --- | --- |
1481
+ | `divided` | Line between rows | 1.0.0 |
1482
+ | `sm` | Tighter row padding | 1.0.0 |
1483
+
1484
+ ### Parts
1485
+
1486
+ | Part | Description | Since |
1487
+ | --- | --- | --- |
1488
+ | `wu-list-item` | Row, a flex container | 1.0.0 |
1489
+ | `wu-list-body` | Column that grows between leading and trailing content | 1.0.0 |
1490
+ | `wu-list-title` | Row title, medium weight | 1.0.0 |
1491
+ | `wu-list-text` | Muted supporting text | 1.0.0 |
1492
+
1493
+ ## modal
1494
+
1495
+ Dialog built on the native `dialog` element, with header, body and footer.
1496
+
1497
+ Esc closes it, and `closedby="any"` adds click outside where supported. `showModal()` and the `command` attribute lock page scroll; `show()` opens it non-modal and leaves the page alone.
1498
+
1499
+ The body scrolls on its own when the content is taller than the dialog.
1500
+
1501
+ - Root class: `wu-modal`
1502
+ - Sass entry: `@use "@wovenui/components/modal";`
1503
+ - Needs: none
1504
+ - JavaScript: none, CSS only
1505
+ - Since: 1.0.0
1506
+
1507
+ ### Markup
1508
+
1509
+ ```html
1510
+ <button class="wu-btn" type="button" commandfor="m1" command="show-modal">Open</button>
1511
+ <dialog class="wu-modal" id="m1" aria-labelledby="m1-title" closedby="any">
1512
+ <header class="wu-modal-header">
1513
+ <h2 class="wu-modal-title" id="m1-title">Title</h2>
1514
+ <button class="wu-modal-close" type="button" commandfor="m1" command="close" aria-label="Close"></button>
1515
+ </header>
1516
+ <div class="wu-modal-body">Content</div>
1517
+ <form class="wu-modal-footer" method="dialog">
1518
+ <button class="wu-btn">Cancel</button>
1519
+ <button class="wu-btn brand" value="confirm">Save</button>
1520
+ </form>
1521
+ </dialog>
1522
+ ```
1523
+
1524
+ ### Modifiers
1525
+
1526
+ | Modifier | Description | Since |
1527
+ | --- | --- | --- |
1528
+ | `sm`, `lg` | Narrow (24rem) or wide (48rem) dialog; the default is 32rem | 1.0.0 |
1529
+ | `animated` | Fades and scales the dialog in and out and fades the backdrop when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
1530
+
1531
+ ### Parts
1532
+
1533
+ | Part | Description | Since |
1534
+ | --- | --- | --- |
1535
+ | `wu-modal-header` | Top row for the title and close button | 1.0.0 |
1536
+ | `wu-modal-title` | Dialog heading | 1.0.0 |
1537
+ | `wu-modal-close` | Close button drawn with the shared close icon; give it an `aria-label` | 1.0.0 |
1538
+ | `wu-modal-body` | Scrolling content area | 1.0.0 |
1539
+ | `wu-modal-footer` | Bottom row for actions, aligned to the inline end | 1.0.0 |
1540
+
1541
+ ### Variables
1542
+
1543
+ | Variable | Default | Description | Since |
1544
+ | --- | --- | --- | --- |
1545
+ | `--wu-modal--Width` | `32rem` | Dialog width | 1.0.0 |
1546
+ | `--wu-modal--MaxHeight` | `calc(100dvh - 4rem)` | Largest dialog height before the body scrolls | 1.0.0 |
1547
+ | `--wu-modal--Backdrop` | `50% black, 70% in dark themes` | Backdrop color | 1.0.0 |
1548
+
1549
+ ### States
1550
+
1551
+ | State | Description | Since |
1552
+ | --- | --- | --- |
1553
+ | `open` | Displays the dialog as a flex column | 1.0.0 |
1554
+
1555
+ ## page-header
1556
+
1557
+ Page title row with an optional description, breadcrumb and action buttons.
1558
+
1559
+ Actions line up with the title and wrap below it on narrow screens. A direct `wu-breadcrumb` child takes its own full-width row above the title.
1560
+
1561
+ - Root class: `wu-page-header`
1562
+ - Sass entry: `@use "@wovenui/components/page-header";`
1563
+ - Needs: none
1564
+ - JavaScript: none, CSS only
1565
+ - Since: 1.0.0
1566
+
1567
+ ### Markup
1568
+
1569
+ ```html
1570
+ <header class="wu-page-header divided">
1571
+ <nav class="wu-breadcrumb" aria-label="Breadcrumb">
1572
+ <ol>
1573
+ <li><a href="/projects">Projects</a></li>
1574
+ <li aria-current="page">Apollo</li>
1575
+ </ol>
1576
+ </nav>
1577
+ <div class="wu-page-header-main">
1578
+ <h1 class="wu-page-header-title">Apollo</h1>
1579
+ <p class="wu-page-header-text">Launch plan and open tasks.</p>
1580
+ </div>
1581
+ <div class="wu-page-header-actions">
1582
+ <button class="wu-btn" type="button">Share</button>
1583
+ <button class="wu-btn brand" type="button">New task</button>
1584
+ </div>
1585
+ </header>
1586
+ ```
1587
+
1588
+ ### Modifiers
1589
+
1590
+ | Modifier | Description | Since |
1591
+ | --- | --- | --- |
1592
+ | `divided` | Adds a bottom border and padding under the header | 1.0.0 |
1593
+
1594
+ ### Parts
1595
+
1596
+ | Part | Description | Since |
1597
+ | --- | --- | --- |
1598
+ | `wu-page-header-main` | Column that holds the title and description | 1.0.0 |
1599
+ | `wu-page-header-title` | Page heading | 1.0.0 |
1600
+ | `wu-page-header-text` | Muted description under the title | 1.0.0 |
1601
+ | `wu-page-header-actions` | Row of buttons, centered on the title first line | 1.0.0 |
1602
+
1603
+ ## pagination
1604
+
1605
+ Page navigation made of button links, with optional total count and page size select.
1606
+
1607
+ Use a `nav` with an `aria-label`, and give an arrow-only previous or next link an `aria-label`. Mark the current link with `aria-current="page"`.
1608
+
1609
+ Put a `wu-pagination-total` and a `wu-pagination-size` label holding a `wu-select` beside the list.
1610
+
1611
+ - Root class: `wu-pagination`
1612
+ - Sass entry: `@use "@wovenui/components/pagination";`
1613
+ - Needs: `button`
1614
+ - JavaScript: none, CSS only
1615
+ - Since: 1.0.0
1616
+
1617
+ ### Markup
1618
+
1619
+ ```html
1620
+ <nav class="wu-pagination" aria-label="Pagination">
1621
+ <ul class="wu-pagination-list">
1622
+ <li><a class="wu-btn wu-pagination-prev" href="/orders?page=1" aria-label="Previous page"></a></li>
1623
+ <li><a class="wu-btn" href="/orders?page=1">1</a></li>
1624
+ <li><a class="wu-btn" href="/orders?page=2" aria-current="page">2</a></li>
1625
+ <li><span class="wu-pagination-ellipsis">&hellip;</span></li>
1626
+ <li><a class="wu-btn" href="/orders?page=9">9</a></li>
1627
+ <li><a class="wu-btn wu-pagination-next" href="/orders?page=3" aria-label="Next page"></a></li>
1628
+ </ul>
1629
+ <span class="wu-pagination-total">Showing 11 to 20 of 86</span>
1630
+ </nav>
1631
+ ```
1632
+
1633
+ ### Modifiers
1634
+
1635
+ | Modifier | Description | Since |
1636
+ | --- | --- | --- |
1637
+ | `sm`, `lg` | Smaller or larger buttons and ellipsis | 1.0.0 |
1638
+
1639
+ ### Parts
1640
+
1641
+ | Part | Description | Since |
1642
+ | --- | --- | --- |
1643
+ | `wu-pagination-list` | List that holds the page links | 1.0.0 |
1644
+ | `wu-pagination-ellipsis` | Gap marker between page numbers | 1.0.0 |
1645
+ | `wu-pagination-prev` | On a link: draws a back arrow that follows the reading direction | 1.0.0 |
1646
+ | `wu-pagination-next` | On a link: draws a forward arrow that follows the reading direction | 1.0.0 |
1647
+ | `wu-pagination-total` | Muted total count text | 1.0.0 |
1648
+ | `wu-pagination-size` | Label that holds a `wu-select` for the page size | 1.0.0 |
1649
+
1650
+ ### States
1651
+
1652
+ | State | Description | Since |
1653
+ | --- | --- | --- |
1654
+ | `aria-current="page"` | The current page: solid brand fill | 1.0.0 |
1655
+
1656
+ ## panel
1657
+
1658
+ Flat bordered region for grouped settings or data.
1659
+
1660
+ The card is the raised surface; the panel stays flat with a border.
1661
+
1662
+ - Root class: `wu-panel`
1663
+ - Sass entry: `@use "@wovenui/components/panel";`
1664
+ - Needs: none
1665
+ - JavaScript: none, CSS only
1666
+ - Since: 1.0.0
1667
+
1668
+ ### Markup
1669
+
1670
+ ```html
1671
+ <section class="wu-panel">
1672
+ <header class="wu-panel-header">
1673
+ <h2 class="wu-panel-title">Notifications</h2>
1674
+ <button class="wu-btn sm" type="button">Edit</button>
1675
+ </header>
1676
+ <div class="wu-panel-body">Email me about new comments.</div>
1677
+ <footer class="wu-panel-footer">
1678
+ <button class="wu-btn brand" type="button">Save</button>
1679
+ </footer>
1680
+ </section>
1681
+ ```
1682
+
1683
+ ### Parts
1684
+
1685
+ | Part | Description | Since |
1686
+ | --- | --- | --- |
1687
+ | `wu-panel-header` | Top row with a tinted background and bottom border | 1.0.0 |
1688
+ | `wu-panel-title` | Panel heading | 1.0.0 |
1689
+ | `wu-panel-body` | Content area | 1.0.0 |
1690
+ | `wu-panel-footer` | Bottom row for actions, aligned to the inline end | 1.0.0 |
1691
+
1692
+ ### Variables
1693
+
1694
+ | Variable | Default | Description | Since |
1695
+ | --- | --- | --- | --- |
1696
+ | `--wu-panel--Padding` | `1rem` | Inline padding of the header and footer, and padding of the body | 1.0.0 |
1697
+
1698
+ ## popover
1699
+
1700
+ Small floating panel built on the native `popover` attribute.
1701
+
1702
+ Esc and click outside close it. `commandfor` with `command="toggle-popover"` works as the trigger too.
1703
+
1704
+ Opens under its trigger where anchor positioning exists, and flips when there is no room. Elsewhere it is centered in the viewport. It has no arrow.
1705
+
1706
+ Placement follows the popover own direction, so it is right in an RTL section of an LTR page.
1707
+
1708
+ - Root class: `wu-popover`
1709
+ - Sass entry: `@use "@wovenui/components/popover";`
1710
+ - Needs: none
1711
+ - JavaScript: none, CSS only
1712
+ - Since: 1.0.0
1713
+
1714
+ ### Markup
1715
+
1716
+ ```html
1717
+ <button class="wu-btn" type="button" popovertarget="p1">Details</button>
1718
+ <div class="wu-popover" id="p1" popover>
1719
+ <h3 class="wu-popover-title">Title</h3>
1720
+ <p>Short content.</p>
1721
+ </div>
1722
+ ```
1723
+
1724
+ ```html
1725
+ <button class="wu-btn" type="button" popovertarget="p2">Delete</button>
1726
+ <div class="wu-popover" id="p2" popover>
1727
+ <p>Delete this item?</p>
1728
+ <div class="wu-popover-actions">
1729
+ <button class="wu-btn sm" type="button" popovertarget="p2" popovertargetaction="hide">Cancel</button>
1730
+ <button class="wu-btn danger sm" type="button" popovertarget="p2" popovertargetaction="hide">Delete</button>
1731
+ </div>
1732
+ </div>
1733
+ ```
1734
+
1735
+ ### Modifiers
1736
+
1737
+ | Modifier | Description | Since |
1738
+ | --- | --- | --- |
1739
+ | `animated` | Fades and scales the popover in and out, growing away from the trigger, when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
1740
+
1741
+ ### Parts
1742
+
1743
+ | Part | Description | Since |
1744
+ | --- | --- | --- |
1745
+ | `wu-popover-title` | Popover heading | 1.0.0 |
1746
+ | `wu-popover-actions` | Button row at the inline end, for an inline confirm | 1.0.0 |
1747
+
1748
+ ### Variables
1749
+
1750
+ | Variable | Default | Description | Since |
1751
+ | --- | --- | --- | --- |
1752
+ | `--wu-popover--MaxWidth` | `20rem` | Largest popover width | 1.0.0 |
1753
+
1754
+ ### States
1755
+
1756
+ | State | Description | Since |
1757
+ | --- | --- | --- |
1758
+ | `:popover-open` | Fully visible during the animated transition | 1.0.0 |
1759
+
1760
+ ## progress-circle
1761
+
1762
+ Circular progress ring that shows a percentage.
1763
+
1764
+ It is not a native element, so name the role and value: `role="progressbar"` with `aria-label`, `aria-valuenow`, `aria-valuemin` and `aria-valuemax`.
1765
+
1766
+ The value is a percentage from 0 to 100, set with `--wu-progress-circle--Value`. The ring color is `currentcolor`, so roles and `color` set it.
1767
+
1768
+ - Root class: `wu-progress-circle`
1769
+ - Sass entry: `@use "@wovenui/components/progress-circle";`
1770
+ - Needs: none
1771
+ - JavaScript: none, CSS only
1772
+ - Since: 1.0.0
1773
+
1774
+ ### Markup
1775
+
1776
+ ```html
1777
+ <div class="wu-progress-circle" role="progressbar" aria-label="Upload" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100" style="--wu-progress-circle--Value: 72">
1778
+ <span class="wu-progress-circle-label">72%</span>
1779
+ </div>
1780
+ ```
1781
+
1782
+ ```html
1783
+ <div class="wu-progress-circle success lg animated" role="progressbar" aria-label="Storage" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="--wu-progress-circle--Value: 40">
1784
+ <span class="wu-progress-circle-label">40%</span>
1785
+ </div>
1786
+ ```
1787
+
1788
+ ### Modifiers
1789
+
1790
+ | Modifier | Description | Since |
1791
+ | --- | --- | --- |
1792
+ | `success`, `danger`, `warning`, `info` | Role color for the ring | 1.0.0 |
1793
+ | `sm`, `lg` | Smaller (2.5rem) or larger (6rem) ring with a thinner or thicker band | 1.0.0 |
1794
+ | `animated` | The ring eases to each new value when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
1795
+
1796
+ ### Parts
1797
+
1798
+ | Part | Description | Since |
1799
+ | --- | --- | --- |
1800
+ | `wu-progress-circle-label` | Text centered in the ring | 1.0.0 |
1801
+
1802
+ ### Variables
1803
+
1804
+ | Variable | Default | Description | Since |
1805
+ | --- | --- | --- | --- |
1806
+ | `--wu-progress-circle--Value` | `0` | Percentage from 0 to 100; a registered property, so it can transition | 1.0.0 |
1807
+ | `--wu-progress-circle--Size` | `4rem` | Width and height of the ring | 1.0.0 |
1808
+ | `--wu-progress-circle--Thickness` | `0.5rem` | Width of the ring band | 1.0.0 |
1809
+
1810
+ ## progress
1811
+
1812
+ Horizontal progress bar on the native `progress` element.
1813
+
1814
+ Without a `value` attribute it is indeterminate and shows a sliding segment. Label it with a `label` element or `aria-label`.
1815
+
1816
+ The fill uses `currentcolor`, so roles and `color` set it.
1817
+
1818
+ - Root class: `wu-progress`
1819
+ - Sass entry: `@use "@wovenui/components/progress";`
1820
+ - Needs: none
1821
+ - JavaScript: none, CSS only
1822
+ - Since: 1.0.0
1823
+
1824
+ ### Markup
1825
+
1826
+ ```html
1827
+ <label class="wu-progress-label" for="upload"><span>Uploading</span><span>72%</span></label>
1828
+ <progress class="wu-progress" id="upload" value="72" max="100"></progress>
1829
+ ```
1830
+
1831
+ ```html
1832
+ <progress class="wu-progress success sm" value="40" max="100" aria-label="Storage used"></progress>
1833
+ <progress class="wu-progress lg" aria-label="Loading"></progress>
1834
+ ```
1835
+
1836
+ ### Modifiers
1837
+
1838
+ | Modifier | Description | Since |
1839
+ | --- | --- | --- |
1840
+ | `success`, `danger`, `warning`, `info` | Role color for the fill | 1.0.0 |
1841
+ | `sm`, `lg` | Thinner (0.25rem) or thicker (0.75rem) bar | 1.0.0 |
1842
+
1843
+ ### Parts
1844
+
1845
+ | Part | Description | Since |
1846
+ | --- | --- | --- |
1847
+ | `wu-progress-label` | Row above the bar for a title and a value | 1.0.0 |
1848
+
1849
+ ### Variables
1850
+
1851
+ | Variable | Default | Description | Since |
1852
+ | --- | --- | --- | --- |
1853
+ | `--wu-progress--Height` | `0.5rem` | Bar height | 1.0.0 |
1854
+
1855
+ ### States
1856
+
1857
+ | State | Description | Since |
1858
+ | --- | --- | --- |
1859
+ | `:indeterminate` | No value: a segment slides across the track | 1.0.0 |
1860
+
1861
+ ## radio
1862
+
1863
+ Radio button with a label, drawn from the native input.
1864
+
1865
+ The circle is `1em`, so it follows the root type size. The `wu-radio-input` part also works alone with an `aria-label`.
1866
+
1867
+ Inside a `wu-field-group sm` or `lg`, a radio without its own size takes the group size.
1868
+
1869
+ - Root class: `wu-radio`
1870
+ - Sass entry: `@use "@wovenui/components/radio";`
1871
+ - Needs: none
1872
+ - JavaScript: none, CSS only
1873
+ - Since: 1.0.0
1874
+
1875
+ ### Markup
1876
+
1877
+ ```html
1878
+ <label class="wu-radio">
1879
+ <input type="radio" class="wu-radio-input" name="plan" value="free" checked>
1880
+ <span class="wu-radio-label">Free</span>
1881
+ </label>
1882
+ <label class="wu-radio">
1883
+ <input type="radio" class="wu-radio-input" name="plan" value="pro">
1884
+ <span class="wu-radio-label">Pro</span>
1885
+ </label>
1886
+ ```
1887
+
1888
+ ```html
1889
+ <input type="radio" class="wu-radio-input" name="size" value="m" aria-label="Medium">
1890
+ ```
1891
+
1892
+ ### Modifiers
1893
+
1894
+ | Modifier | Description | Since |
1895
+ | --- | --- | --- |
1896
+ | `sm`, `lg` | Smaller or larger text and circle | 1.0.0 |
1897
+
1898
+ ### Parts
1899
+
1900
+ | Part | Description | Since |
1901
+ | --- | --- | --- |
1902
+ | `wu-radio-input` | The native radio input, drawn as a circle with a dot | 1.0.0 |
1903
+ | `wu-radio-label` | Label text beside the circle | 1.0.0 |
1904
+
1905
+ ### States
1906
+
1907
+ | State | Description | Since |
1908
+ | --- | --- | --- |
1909
+ | `disabled` | Half opacity, no pointer events | 1.0.0 |
1910
+ | `aria-invalid="true"`, `:user-invalid` | Two pixel danger edge that stays on hover and when checked | 1.0.0 |
1911
+ | `:checked` | Brand fill with a dot | 1.0.0 |
1912
+
1913
+ ## segmented
1914
+
1915
+ Radio group shown as connected segments, one selected at a time.
1916
+
1917
+ Arrow keys move the choice natively, and segments share the width equally. A `div` with `role="radiogroup"` and an `aria-label` works instead of a `fieldset`.
1918
+
1919
+ Type follows the button: `xs`, `sm` and `md` for `sm`, default and `lg`. The animated thumb supports up to six label segments.
1920
+
1921
+ - Root class: `wu-segmented`
1922
+ - Sass entry: `@use "@wovenui/components/segmented";`
1923
+ - Needs: none
1924
+ - JavaScript: none, CSS only
1925
+ - Since: 1.0.0
1926
+
1927
+ ### Markup
1928
+
1929
+ ```html
1930
+ <fieldset class="wu-segmented">
1931
+ <legend class="sr-only">View</legend>
1932
+ <label class="wu-segmented-item"><input type="radio" name="view" class="wu-segmented-input" value="day" checked> Day</label>
1933
+ <label class="wu-segmented-item"><input type="radio" name="view" class="wu-segmented-input" value="week"> Week</label>
1934
+ <label class="wu-segmented-item"><input type="radio" name="view" class="wu-segmented-input" value="month"> Month</label>
1935
+ </fieldset>
1936
+ ```
1937
+
1938
+ ### Modifiers
1939
+
1940
+ | Modifier | Description | Since |
1941
+ | --- | --- | --- |
1942
+ | `sm`, `lg` | Smaller or larger control height and font size | 1.0.0 |
1943
+ | `animated` | One thumb slides to the checked segment with a spring when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
1944
+
1945
+ ### Parts
1946
+
1947
+ | Part | Description | Since |
1948
+ | --- | --- | --- |
1949
+ | `wu-segmented-item` | One segment: the label that wraps a radio input | 1.0.0 |
1950
+ | `wu-segmented-input` | The radio input, visually hidden but focusable and announced | 1.0.0 |
1951
+
1952
+ ### States
1953
+
1954
+ | State | Description | Since |
1955
+ | --- | --- | --- |
1956
+ | `:checked` | The selected segment has a raised surface, an edge and a shadow | 1.0.0 |
1957
+ | `disabled` | Half opacity on that segment | 1.0.0 |
1958
+
1959
+ ## select
1960
+
1961
+ Native `select` with the kit input look and a drawn arrow.
1962
+
1963
+ Core base already sets the border, background, hover, focus ring, invalid border, disabled look, padding and the default height. List boxes (`multiple` or `size`) keep their natural height and have no arrow.
1964
+
1965
+ Where `appearance: base-select` is supported, the picker matches the kit; other browsers keep the native list.
1966
+
1967
+ Inside a `wu-field-group sm` or `lg`, a select without its own size takes the group size.
1968
+
1969
+ - Root class: `wu-select`
1970
+ - Sass entry: `@use "@wovenui/components/select";`
1971
+ - Needs: none
1972
+ - JavaScript: none, CSS only
1973
+ - Since: 1.0.0
1974
+
1975
+ ### Markup
1976
+
1977
+ ```html
1978
+ <select class="wu-select" name="country" aria-label="Country">
1979
+ <option>Pakistan</option>
1980
+ <option>United Arab Emirates</option>
1981
+ </select>
1982
+ ```
1983
+
1984
+ ```html
1985
+ <select class="wu-select sm" name="sort" aria-label="Sort by" aria-invalid="true">
1986
+ <option>Newest</option>
1987
+ <option>Oldest</option>
1988
+ </select>
1989
+ ```
1990
+
1991
+ ### Modifiers
1992
+
1993
+ | Modifier | Description | Since |
1994
+ | --- | --- | --- |
1995
+ | `sm`, `lg` | Smaller or larger control height and padding | 1.0.0 |
1996
+
1997
+ ### Variables
1998
+
1999
+ | Variable | Default | Description | Since |
2000
+ | --- | --- | --- | --- |
2001
+ | `--wu-select--ArrowColor` | `var(--wu-text-muted)` | Color of the arrow | 1.0.0 |
2002
+
2003
+ ### States
2004
+
2005
+ | State | Description | Since |
2006
+ | --- | --- | --- |
2007
+ | `disabled` | Not-allowed cursor on top of core disabled look | 1.0.0 |
2008
+ | `aria-invalid="true"`, `:user-invalid` | Danger edge doubled to two pixels, so the change is not color alone | 1.0.0 |
2009
+
2010
+ ## sidebar
2011
+
2012
+ Vertical navigation with sections, link lists and collapsible groups.
2013
+
2014
+ Use a `nav` with an `aria-label`, and mark the current link with `aria-current="page"`. A collapsible group is a `details` with the `wu-sidebar-group` class and a `summary` with `wu-sidebar-toggle`; the toggle may also be a `button` with `aria-expanded`.
2015
+
2016
+ On narrow screens put the sidebar in a `dialog` with `wu-drawer inline-start`, where it fills the drawer.
2017
+
2018
+ Icon rail: add `collapsed`; each link holds an `svg` and a `wu-sidebar-label`, shown as a tooltip on hover or focus.
2019
+
2020
+ - Root class: `wu-sidebar`
2021
+ - Sass entry: `@use "@wovenui/components/sidebar";`
2022
+ - Needs: none
2023
+ - JavaScript: none, CSS only
2024
+ - Since: 1.0.0
2025
+
2026
+ ### Markup
2027
+
2028
+ ```html
2029
+ <nav class="wu-sidebar" aria-label="Main">
2030
+ <div class="wu-sidebar-section">
2031
+ <h2 class="wu-sidebar-heading">Workspace</h2>
2032
+ <ul class="wu-sidebar-list">
2033
+ <li><a class="wu-sidebar-link" href="/dashboard" aria-current="page">Dashboard</a></li>
2034
+ <li>
2035
+ <details class="wu-sidebar-group" open>
2036
+ <summary class="wu-sidebar-toggle">Projects</summary>
2037
+ <ul class="wu-sidebar-list">
2038
+ <li><a class="wu-sidebar-link" href="/projects/apollo">Apollo</a></li>
2039
+ </ul>
2040
+ </details>
2041
+ </li>
2042
+ </ul>
2043
+ </div>
2044
+ <div class="wu-sidebar-footer">Signed in as Sam</div>
2045
+ </nav>
2046
+ ```
2047
+
2048
+ ```html
2049
+ <nav class="wu-sidebar collapsed pinned" aria-label="Main">
2050
+ <ul class="wu-sidebar-list">
2051
+ <li>
2052
+ <a class="wu-sidebar-link" href="/settings">
2053
+ <svg aria-hidden="true" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4" /></svg>
2054
+ <span class="wu-sidebar-label">Settings</span>
2055
+ </a>
2056
+ </li>
2057
+ </ul>
2058
+ </nav>
2059
+ ```
2060
+
2061
+ ### Modifiers
2062
+
2063
+ | Modifier | Description | Since |
2064
+ | --- | --- | --- |
2065
+ | `pinned` | Sticks beside scrolling content and scrolls on its own | 1.0.0 |
2066
+ | `collapsed` | Icon rail: labels and headings are visually hidden, and a label shows as a tooltip on hover or focus | 1.0.0 |
2067
+ | `animated` | Animates the width, the chevron and the opening of groups when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
2068
+
2069
+ ### Parts
2070
+
2071
+ | Part | Description | Since |
2072
+ | --- | --- | --- |
2073
+ | `wu-sidebar-header` | Top row for a logo or title | 1.0.0 |
2074
+ | `wu-sidebar-footer` | Bottom row, pushed to the end | 1.0.0 |
2075
+ | `wu-sidebar-section` | Group of a heading and a list | 1.0.0 |
2076
+ | `wu-sidebar-heading` | Small muted section heading | 1.0.0 |
2077
+ | `wu-sidebar-list` | Vertical list of links; nested inside a group it is indented | 1.0.0 |
2078
+ | `wu-sidebar-group` | A `details` element that collapses a nested list | 1.0.0 |
2079
+ | `wu-sidebar-toggle` | Row that opens or closes a group, with a chevron | 1.0.0 |
2080
+ | `wu-sidebar-link` | Navigation link row | 1.0.0 |
2081
+ | `wu-sidebar-label` | Text label of a link; hidden and shown as a tooltip when collapsed | 1.0.0 |
2082
+
2083
+ ### Variables
2084
+
2085
+ | Variable | Default | Description | Since |
2086
+ | --- | --- | --- | --- |
2087
+ | `--wu-sidebar--Width` | `16rem` | Sidebar width | 1.0.0 |
2088
+ | `--wu-sidebar--CollapsedWidth` | `calc(1em + 3rem)` | Width of the icon rail | 1.0.0 |
2089
+
2090
+ ### States
2091
+
2092
+ | State | Description | Since |
2093
+ | --- | --- | --- |
2094
+ | `aria-current="page"` | The current link: tinted fill and a bar at the inline start | 1.0.0 |
2095
+ | `open`, `aria-expanded="true"` | The chevron points down | 1.0.0 |
2096
+
2097
+ ## skeleton
2098
+
2099
+ Placeholder shapes shown while content loads.
2100
+
2101
+ Placeholders are decorative: add `aria-hidden="true"` to each skeleton and `aria-busy="true"` to the region being loaded, plus visible or hidden Loading text. Remove both when the content arrives.
2102
+
2103
+ The default shape is a block. Adjacent `text` skeletons stack with a gap, and a last `text` line is shorter.
2104
+
2105
+ - Root class: `wu-skeleton`
2106
+ - Sass entry: `@use "@wovenui/components/skeleton";`
2107
+ - Needs: none
2108
+ - JavaScript: none, CSS only
2109
+ - Since: 1.0.0
2110
+
2111
+ ### Markup
2112
+
2113
+ ```html
2114
+ <div aria-busy="true">
2115
+ <span class="sr-only">Loading</span>
2116
+ <div class="wu-skeleton circle animated" aria-hidden="true"></div>
2117
+ <div class="wu-skeleton text animated" aria-hidden="true"></div>
2118
+ <div class="wu-skeleton text animated" aria-hidden="true"></div>
2119
+ </div>
2120
+ ```
2121
+
2122
+ ```html
2123
+ <div class="wu-skeleton" aria-hidden="true" style="--wu-skeleton--Height: 8rem"></div>
2124
+ ```
2125
+
2126
+ ### Modifiers
2127
+
2128
+ | Modifier | Description | Since |
2129
+ | --- | --- | --- |
2130
+ | `text` | One line of text: height of `1em` | 1.0.0 |
2131
+ | `circle` | Round shape for an avatar | 1.0.0 |
2132
+ | `animated` | Shimmer sweep, staggered across siblings where supported, when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
2133
+
2134
+ ### Variables
2135
+
2136
+ | Variable | Default | Description | Since |
2137
+ | --- | --- | --- | --- |
2138
+ | `--wu-skeleton--Width` | `100%` | Width; a last `text` line defaults to 60% | 1.0.0 |
2139
+ | `--wu-skeleton--Height` | `6rem` | Height; `text` defaults to 1em | 1.0.0 |
2140
+ | `--wu-skeleton--Size` | `2.5rem` | Width and height of a `circle` | 1.0.0 |
2141
+
2142
+ ## spinner
2143
+
2144
+ Loading ring in the current text color, with an overlay for busy regions.
2145
+
2146
+ Pair it with `role="status"` and hidden text, or use `aria-hidden="true"` next to visible text. Reduced motion is handled by core reset.
2147
+
2148
+ The overlay covers its parent, dims the content and centers a spinner with optional text. The parent becomes the positioning box unless it already has a position.
2149
+
2150
+ - Root class: `wu-spinner`
2151
+ - Sass entry: `@use "@wovenui/components/spinner";`
2152
+ - Needs: none
2153
+ - JavaScript: none, CSS only
2154
+ - Since: 1.0.0
2155
+
2156
+ ### Markup
2157
+
2158
+ ```html
2159
+ <span class="wu-spinner" role="status"><span class="sr-only">Loading</span></span>
2160
+ <button class="wu-btn" type="button" aria-busy="true"><span class="wu-spinner sm" aria-hidden="true"></span> Saving</button>
2161
+ ```
2162
+
2163
+ ```html
2164
+ <div aria-busy="true">
2165
+ <div inert>Orders table</div>
2166
+ <div class="wu-spinner-overlay"><span class="wu-spinner" aria-hidden="true"></span><span>Loading orders</span></div>
2167
+ </div>
2168
+ ```
2169
+
2170
+ ### Modifiers
2171
+
2172
+ | Modifier | Description | Since |
2173
+ | --- | --- | --- |
2174
+ | `sm`, `lg` | Smaller (1rem) or larger (2rem, thicker band) ring | 1.0.0 |
2175
+
2176
+ ### Parts
2177
+
2178
+ | Part | Description | Since |
2179
+ | --- | --- | --- |
2180
+ | `wu-spinner-overlay` | Layer that covers a busy region and centers a spinner with text | 1.0.0 |
2181
+
2182
+ ### Variables
2183
+
2184
+ | Variable | Default | Description | Since |
2185
+ | --- | --- | --- | --- |
2186
+ | `--wu-spinner--Size` | `1.25rem` | Width and height of the ring | 1.0.0 |
2187
+ | `--wu-spinner-overlay--Background` | `85% surface` | Overlay background | 1.0.0 |
2188
+
2189
+ ## stat
2190
+
2191
+ One figure with a label, a large value in tabular figures and an optional change line.
2192
+
2193
+ Keep the signed number as text in the change line; the arrow and the color repeat it, they never replace it.
2194
+
2195
+ - Root class: `wu-stat`
2196
+ - Sass entry: `@use "@wovenui/components/stat";`
2197
+ - Needs: none
2198
+ - JavaScript: none, CSS only
2199
+ - Since: 1.0.0
2200
+
2201
+ ### Markup
2202
+
2203
+ ```html
2204
+ <div class="wu-stat">
2205
+ <span class="wu-stat-label">Active users</span>
2206
+ <span class="wu-stat-value">1,284</span>
2207
+ <span class="wu-stat-delta success" data-trend="up">+12% from last month</span>
2208
+ </div>
2209
+ ```
2210
+
2211
+ ```html
2212
+ <div class="wu-stat lg">
2213
+ <span class="wu-stat-label">Open tickets</span>
2214
+ <span class="wu-stat-value">37</span>
2215
+ <span class="wu-stat-delta" data-trend="down">-5 this week</span>
2216
+ </div>
2217
+ ```
2218
+
2219
+ ### Modifiers
2220
+
2221
+ | Modifier | Description | Since |
2222
+ | --- | --- | --- |
2223
+ | `sm`, `lg` | Smaller or larger value | 1.0.0 |
2224
+ | `success`, `danger` | On the change line: success or danger text color | 1.0.0 |
2225
+
2226
+ ### Parts
2227
+
2228
+ | Part | Description | Since |
2229
+ | --- | --- | --- |
2230
+ | `wu-stat-label` | Muted label | 1.0.0 |
2231
+ | `wu-stat-value` | Large value | 1.0.0 |
2232
+ | `wu-stat-delta` | Change line, muted by default | 1.0.0 |
2233
+
2234
+ ### States
2235
+
2236
+ | State | Description | Since |
2237
+ | --- | --- | --- |
2238
+ | `data-trend="up"`, `data-trend="down"` | On the change line: draws an up or down arrow | 1.0.0 |
2239
+
2240
+ ## table
2241
+
2242
+ Data table with sticky header and columns, selection, expandable rows, truncated cells, a summary row and striped styles.
2243
+
2244
+ Put a wide table in a `wu-table-scroll` region with `tabindex="0"`, `role="region"` and an `aria-label`; it shows scroll shadows on the sides with more content. Sticky columns need that region.
2245
+
2246
+ The select-all checkbox and expandable rows need the kit script; everything else is CSS. Sorting the rows and updating `aria-sort` is up to your script.
2247
+
2248
+ A truncated cell takes the full text in core's `data-tooltip` and `tabindex="0"`. `aria-selected` on a row is valid only in a `role="grid"` table.
2249
+
2250
+ - Root class: `wu-table`
2251
+ - Sass entry: `@use "@wovenui/components/table";`
2252
+ - Needs: none
2253
+ - JavaScript: optional: `initTable` from `@wovenui/components/js`
2254
+ - Since: 1.0.0
2255
+
2256
+ ### Markup
2257
+
2258
+ ```html
2259
+ <div class="wu-table-scroll" tabindex="0" role="region" aria-label="Invoices">
2260
+ <table class="wu-table striped hover">
2261
+ <caption>Open invoices</caption>
2262
+ <thead>
2263
+ <tr>
2264
+ <th scope="col">Invoice</th>
2265
+ <th scope="col" class="wu-table-num">Amount</th>
2266
+ </tr>
2267
+ </thead>
2268
+ <tbody>
2269
+ <tr><th scope="row">INV-1042</th><td class="wu-table-num">$1,200.00</td></tr>
2270
+ <tr><th scope="row">INV-1043</th><td class="wu-table-num">$340.00</td></tr>
2271
+ </tbody>
2272
+ <tfoot><tr><th scope="row">Total</th><td class="wu-table-num">$1,540.00</td></tr></tfoot>
2273
+ </table>
2274
+ </div>
2275
+ ```
2276
+
2277
+ ```html
2278
+ <table class="wu-table">
2279
+ <thead>
2280
+ <tr>
2281
+ <th scope="col" class="wu-table-select"><input type="checkbox" class="wu-checkbox-input" aria-label="Select all rows"></th>
2282
+ <th scope="col" aria-sort="ascending"><button class="wu-table-sort" type="button">Name</button></th>
2283
+ </tr>
2284
+ </thead>
2285
+ <tbody>
2286
+ <tr>
2287
+ <td class="wu-table-select"><input type="checkbox" class="wu-checkbox-input" aria-label="Select Ana Lopez"></td>
2288
+ <th scope="row"><button class="wu-table-expand" type="button" aria-expanded="false" aria-label="Details for Ana Lopez"></button> Ana Lopez</th>
2289
+ </tr>
2290
+ <tr class="wu-table-detail" hidden><td colspan="2">Owner since 2021</td></tr>
2291
+ </tbody>
2292
+ </table>
2293
+ ```
2294
+
2295
+ ```html
2296
+ <table class="wu-table">
2297
+ <tbody>
2298
+ <tr><td class="wu-table-truncate" tabindex="0" data-tooltip="Typo on the billing page"><span>Typo on the billing page</span></td></tr>
2299
+ </tbody>
2300
+ </table>
2301
+ ```
2302
+
2303
+ ### Modifiers
2304
+
2305
+ | Modifier | Description | Since |
2306
+ | --- | --- | --- |
2307
+ | `sm` | Tighter cell padding | 1.0.0 |
2308
+ | `striped` | Tint on every second body row | 1.0.0 |
2309
+ | `hover` | Tint on the row under the pointer | 1.0.0 |
2310
+ | `bordered` | Outer border with rounded corners and borders between cells | 1.0.0 |
2311
+ | `sticky-header` | Header cells stick to the top of the page, or of the scroll region when `--wu-table--MaxHeight` is set | 1.0.0 |
2312
+ | `sticky-start`, `sticky-end` | First or last column sticks inside a `wu-table-scroll` region, with an edge shadow on the scrolling side | 1.0.0 |
2313
+ | `animated` | Spring turn of the expand chevron when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
2314
+
2315
+ ### Parts
2316
+
2317
+ | Part | Description | Since |
2318
+ | --- | --- | --- |
2319
+ | `wu-table-scroll` | Scroll region for a wide table, with scroll shadows | 1.0.0 |
2320
+ | `wu-table-num` | Cell with end-aligned numbers in tabular figures | 1.0.0 |
2321
+ | `wu-table-select` | Cell of a checkbox column, as narrow as its checkbox | 1.0.0 |
2322
+ | `wu-table-truncate` | Cell whose text child stays on one line with an ellipsis | 1.0.0 |
2323
+ | `wu-table-sort` | Button in a header cell that shows sort arrows | 1.0.0 |
2324
+ | `wu-table-expand` | Button with `aria-expanded` that shows or hides the detail row | 1.0.0 |
2325
+ | `wu-table-detail` | Row after an expandable row, with the alternate background | 1.0.0 |
2326
+
2327
+ ### Variables
2328
+
2329
+ | Variable | Default | Description | Since |
2330
+ | --- | --- | --- | --- |
2331
+ | `--wu-table--Background` | `var(--wu-bg-surface)` | Background that scroll shadows and sticky cells blend into | 1.0.0 |
2332
+ | `--wu-table--MaxHeight` | `none` | Maximum height of the scroll region, so the header can stick inside it | 1.0.0 |
2333
+ | `--wu-table--TruncateWidth` | `16rem` | Maximum width of a truncated cell text | 1.0.0 |
2334
+
2335
+ ### States
2336
+
2337
+ | State | Description | Since |
2338
+ | --- | --- | --- |
2339
+ | `aria-sort="ascending"`, `aria-sort="descending"` | On a header cell: one arrow on the sort button and regular text color | 1.0.0 |
2340
+ | `aria-expanded="true"` | On an expand button: the chevron points down | 1.0.0 |
2341
+ | `aria-selected="true"` | On a row: selected tint and a bar at the start of the first cell; a checked `wu-table-select` box does the same | 1.0.0 |
2342
+
2343
+ ## tabs
2344
+
2345
+ Tabs that show one panel at a time, following the WAI-ARIA tabs pattern, in line, card, boxed and vertical styles.
2346
+
2347
+ The kit script adds arrow keys, Home and End, switches the panels and fills in roles, ids, `aria-controls`, `aria-labelledby` and `tabindex` when they are missing. Without it the first panel shows.
2348
+
2349
+ In `vertical` tabs the script sets `aria-orientation="vertical"` on the list; without the script, add it yourself.
2350
+
2351
+ - Root class: `wu-tabs`
2352
+ - Sass entry: `@use "@wovenui/components/tabs";`
2353
+ - Needs: none
2354
+ - JavaScript: required: `initTabs` from `@wovenui/components/js`
2355
+ - Since: 1.0.0
2356
+
2357
+ ### Markup
2358
+
2359
+ ```html
2360
+ <div class="wu-tabs">
2361
+ <div class="wu-tabs-list" role="tablist" aria-label="Account">
2362
+ <button class="wu-tabs-tab" type="button" role="tab" id="tab-profile" aria-controls="panel-profile" aria-selected="true">Profile</button>
2363
+ <button class="wu-tabs-tab" type="button" role="tab" id="tab-billing" aria-controls="panel-billing" aria-selected="false" tabindex="-1">Billing</button>
2364
+ </div>
2365
+ <div class="wu-tabs-panel" role="tabpanel" id="panel-profile" aria-labelledby="tab-profile" tabindex="0">Profile settings</div>
2366
+ <div class="wu-tabs-panel" role="tabpanel" id="panel-billing" aria-labelledby="tab-billing" tabindex="0" hidden>Billing settings</div>
2367
+ </div>
2368
+ ```
2369
+
2370
+ ```html
2371
+ <div class="wu-tabs vertical boxed">
2372
+ <div class="wu-tabs-list" role="tablist" aria-label="Settings" aria-orientation="vertical">
2373
+ <button class="wu-tabs-tab" type="button" role="tab" id="tab-general" aria-controls="panel-general" aria-selected="true">General</button>
2374
+ <button class="wu-tabs-tab" type="button" role="tab" id="tab-team" aria-controls="panel-team" aria-selected="false" tabindex="-1" disabled>Team</button>
2375
+ </div>
2376
+ <div class="wu-tabs-panel" role="tabpanel" id="panel-general" aria-labelledby="tab-general" tabindex="0">General settings</div>
2377
+ <div class="wu-tabs-panel" role="tabpanel" id="panel-team" aria-labelledby="tab-team" tabindex="0" hidden>Team settings</div>
2378
+ </div>
2379
+ ```
2380
+
2381
+ ### Modifiers
2382
+
2383
+ | Modifier | Description | Since |
2384
+ | --- | --- | --- |
2385
+ | `card` | Joined tab boxes; the selected tab opens into the panel | 1.0.0 |
2386
+ | `boxed` | Tabs and panels inside one bordered box | 1.0.0 |
2387
+ | `vertical` | Tab list at the inline start, selected bar on its inline end edge | 1.0.0 |
2388
+ | `animated` | Selected bar grows from the center and the panel fades in when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
2389
+
2390
+ ### Parts
2391
+
2392
+ | Part | Description | Since |
2393
+ | --- | --- | --- |
2394
+ | `wu-tabs-list` | Row of tabs; scrolls sideways and fades the edge with more tabs | 1.0.0 |
2395
+ | `wu-tabs-tab` | One tab button; a direct child `svg` is sized to `1em` | 1.0.0 |
2396
+ | `wu-tabs-panel` | Content shown for the selected tab | 1.0.0 |
2397
+
2398
+ ### States
2399
+
2400
+ | State | Description | Since |
2401
+ | --- | --- | --- |
2402
+ | `aria-selected="true"` | On a tab: regular text color and a brand bar | 1.0.0 |
2403
+ | `disabled`, `aria-disabled="true"` | On a tab: half opacity and a not-allowed cursor | 1.0.0 |
2404
+
2405
+ ## tag
2406
+
2407
+ Label for a category or a filter, in seven hues, as plain text, a link or a button.
2408
+
2409
+ A link or button tag gets a pointer, a hover color and the focus ring. `aria-pressed` on a button tag makes a checkable filter chip; toggling the attribute is up to your script.
2410
+
2411
+ A direct child `svg` is sized to `1em`. The remove button needs an `aria-label` that names the tag.
2412
+
2413
+ - Root class: `wu-tag`
2414
+ - Sass entry: `@use "@wovenui/components/tag";`
2415
+ - Needs: none
2416
+ - JavaScript: none, CSS only
2417
+ - Since: 1.0.0
2418
+
2419
+ ### Markup
2420
+
2421
+ ```html
2422
+ <span class="wu-tag">Design</span>
2423
+ <span class="wu-tag blue">Frontend</span>
2424
+ ```
2425
+
2426
+ ```html
2427
+ <div role="group" aria-label="Filter by topic">
2428
+ <button class="wu-tag" type="button" aria-pressed="true">Design</button>
2429
+ <button class="wu-tag" type="button" aria-pressed="false">Research</button>
2430
+ </div>
2431
+ ```
2432
+
2433
+ ```html
2434
+ <span class="wu-tag purple">Research <button class="wu-tag-remove" type="button" aria-label="Remove Research"></button></span>
2435
+ ```
2436
+
2437
+ ### Modifiers
2438
+
2439
+ | Modifier | Description | Since |
2440
+ | --- | --- | --- |
2441
+ | `gray`, `red`, `blue`, `green`, `yellow`, `purple`, `pink` | Hue color: subtle background, hue border and hue text | 1.0.0 |
2442
+
2443
+ ### Parts
2444
+
2445
+ | Part | Description | Since |
2446
+ | --- | --- | --- |
2447
+ | `wu-tag-remove` | Remove button inside the tag, its cross drawn in CSS | 1.0.0 |
2448
+
2449
+ ### States
2450
+
2451
+ | State | Description | Since |
2452
+ | --- | --- | --- |
2453
+ | `aria-pressed="true"` | On a button tag: brand tint, ring and a check mark | 1.0.0 |
2454
+
2455
+ ## text
2456
+
2457
+ Body text on any element, with relaxed leading and space between paragraphs in a row.
2458
+
2459
+ - Root class: `wu-text`
2460
+ - Sass entry: `@use "@wovenui/components/text";`
2461
+ - Needs: none
2462
+ - JavaScript: none, CSS only
2463
+ - Since: 1.0.0
2464
+
2465
+ ### Markup
2466
+
2467
+ ```html
2468
+ <p class="wu-text lg">A lead paragraph introduces the page.</p>
2469
+ <p class="wu-text">Body text follows with relaxed leading.</p>
2470
+ <p class="wu-text sm muted">Small, muted text for notes and fine print.</p>
2471
+ ```
2472
+
2473
+ ### Modifiers
2474
+
2475
+ | Modifier | Description | Since |
2476
+ | --- | --- | --- |
2477
+ | `sm` | Small size | 1.0.0 |
2478
+ | `lg` | Large size, for a lead paragraph | 1.0.0 |
2479
+ | `muted` | Muted color | 1.0.0 |
2480
+
2481
+ ## textarea
2482
+
2483
+ Multi-line field with a bounded height that grows with the content, where the browser supports it.
2484
+
2485
+ Core base already styles a `textarea` like an input (border, focus ring, invalid border, disabled). The kit adds the radius, the type size and a height between three and twelve lines.
2486
+
2487
+ Growth uses `field-sizing: content`, and `rows` is ignored there.
2488
+
2489
+ - Root class: `wu-textarea`
2490
+ - Sass entry: `@use "@wovenui/components/textarea";`
2491
+ - Needs: none
2492
+ - JavaScript: none, CSS only
2493
+ - Since: 1.0.0
2494
+
2495
+ ### Markup
2496
+
2497
+ ```html
2498
+ <div class="wu-field">
2499
+ <label class="wu-field-label" for="textarea-notes">Notes</label>
2500
+ <textarea class="wu-textarea" id="textarea-notes">Type more lines to see the field grow.</textarea>
2501
+ </div>
2502
+ ```
2503
+
2504
+ ```html
2505
+ <textarea class="wu-textarea" aria-label="Short note" style="--wu-textarea--MinHeight: 2.5rem; --wu-textarea--MaxHeight: 6rem">One line to start.</textarea>
2506
+ ```
2507
+
2508
+ ### Variables
2509
+
2510
+ | Variable | Default | Description | Since |
2511
+ | --- | --- | --- | --- |
2512
+ | `--wu-textarea--MinHeight` | `3 lines plus padding and border` | Minimum height | 1.0.0 |
2513
+ | `--wu-textarea--MaxHeight` | `12 lines plus padding and border` | Maximum height | 1.0.0 |
2514
+
2515
+ ### States
2516
+
2517
+ | State | Description | Since |
2518
+ | --- | --- | --- |
2519
+ | `:disabled` | Not-allowed cursor and no resize | 1.0.0 |
2520
+ | `readonly` | Alternate background | 1.0.0 |
2521
+ | `aria-invalid="true"`, `:user-invalid` | Danger border doubled to 2px, so the change is not color alone | 1.0.0 |
2522
+
2523
+ ## timeline
2524
+
2525
+ Ordered list of events with markers and a rail, role colors and a progress mode.
2526
+
2527
+ Role words on an item color its marker; `success`, `warning` and `danger` also change the marker shape, so color is not the only cue.
2528
+
2529
+ With `aria-current="step"` on one item the list becomes a progress rail: earlier items filled, the current one ringed, later ones hollow. Say "completed" in the item text too.
2530
+
2531
+ - Root class: `wu-timeline`
2532
+ - Sass entry: `@use "@wovenui/components/timeline";`
2533
+ - Needs: none
2534
+ - JavaScript: none, CSS only
2535
+ - Since: 1.0.0
2536
+
2537
+ ### Markup
2538
+
2539
+ ```html
2540
+ <ol class="wu-timeline">
2541
+ <li class="wu-timeline-item success">
2542
+ <p class="wu-timeline-title">Order shipped</p>
2543
+ <time class="wu-timeline-time" datetime="2026-03-02">2 March</time>
2544
+ <p class="wu-timeline-text">Handed to the carrier.</p>
2545
+ </li>
2546
+ <li class="wu-timeline-item danger">
2547
+ <p class="wu-timeline-title">Payment failed</p>
2548
+ <time class="wu-timeline-time" datetime="2026-03-01">1 March</time>
2549
+ </li>
2550
+ </ol>
2551
+ ```
2552
+
2553
+ ```html
2554
+ <ol class="wu-timeline">
2555
+ <li class="wu-timeline-item"><p class="wu-timeline-title">Cart (completed)</p></li>
2556
+ <li class="wu-timeline-item" aria-current="step"><p class="wu-timeline-title">Payment</p></li>
2557
+ <li class="wu-timeline-item"><p class="wu-timeline-title">Confirmation</p></li>
2558
+ </ol>
2559
+ ```
2560
+
2561
+ ### Modifiers
2562
+
2563
+ | Modifier | Description | Since |
2564
+ | --- | --- | --- |
2565
+ | `brand`, `info` | On an item: marker color | 1.0.0 |
2566
+ | `success`, `warning`, `danger` | On an item: marker color and a check, triangle or octagon shape | 1.0.0 |
2567
+ | `animated` | Each rail segment draws once as it scrolls into view when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
2568
+
2569
+ ### Parts
2570
+
2571
+ | Part | Description | Since |
2572
+ | --- | --- | --- |
2573
+ | `wu-timeline-item` | One event: a marker, a rail to the next item and the content | 1.0.0 |
2574
+ | `wu-timeline-title` | Event title in medium weight | 1.0.0 |
2575
+ | `wu-timeline-time` | Muted time or date line | 1.0.0 |
2576
+ | `wu-timeline-text` | Muted description | 1.0.0 |
2577
+
2578
+ ### States
2579
+
2580
+ | State | Description | Since |
2581
+ | --- | --- | --- |
2582
+ | `aria-current="step"` | On an item: the current step, ringed; earlier items fill and later ones turn hollow | 1.0.0 |
2583
+
2584
+ ## title
2585
+
2586
+ Visual heading size on any element, in four steps.
2587
+
2588
+ Pick the heading level for the document outline and the size for the look.
2589
+
2590
+ - Root class: `wu-title`
2591
+ - Sass entry: `@use "@wovenui/components/title";`
2592
+ - Needs: none
2593
+ - JavaScript: none, CSS only
2594
+ - Since: 1.0.0
2595
+
2596
+ ### Markup
2597
+
2598
+ ```html
2599
+ <h2 class="wu-title xl">Extra large title</h2>
2600
+ <h2 class="wu-title">Default title</h2>
2601
+ ```
2602
+
2603
+ ```html
2604
+ <h3 class="wu-title lg">An h3 with the large size</h3>
2605
+ ```
2606
+
2607
+ ### Modifiers
2608
+
2609
+ | Modifier | Description | Since |
2610
+ | --- | --- | --- |
2611
+ | `sm` | Large size, one step below the default | 1.0.0 |
2612
+ | `lg` | 2xl size | 1.0.0 |
2613
+ | `xl` | 3xl size | 1.0.0 |
2614
+
2615
+ ## toast
2616
+
2617
+ Short notification in a corner of the viewport, shown in a polite live region.
2618
+
2619
+ The region sits at the bottom end corner by default and scrolls when the toasts are taller than the viewport. The title text carries the meaning, not the color.
2620
+
2621
+ `toast("Saved")` from the kit script adds a toast, and the region when the page has none. It closes the toast after a delay, pauses on hover and focus, keeps at most three open and skips a repeat of an open message.
2622
+
2623
+ Create the region at page load; a region added together with its first toast may not be announced.
2624
+
2625
+ - Root class: `wu-toast`
2626
+ - Sass entry: `@use "@wovenui/components/toast";`
2627
+ - Needs: none
2628
+ - JavaScript: required: `toast`, `dismiss` from `@wovenui/components/js`
2629
+ - Since: 1.0.0
2630
+
2631
+ ### Markup
2632
+
2633
+ ```html
2634
+ <section class="wu-toast-region animated" aria-live="polite" aria-label="Notifications">
2635
+ <div class="wu-toast success">
2636
+ <p class="wu-toast-title">Saved</p>
2637
+ <p>Your changes are live.</p>
2638
+ <button class="wu-toast-close" type="button" aria-label="Close"></button>
2639
+ </div>
2640
+ </section>
2641
+ ```
2642
+
2643
+ ```html
2644
+ <section class="wu-toast-region top-end" aria-live="polite" aria-label="Notifications"></section>
2645
+ ```
2646
+
2647
+ ### Modifiers
2648
+
2649
+ | Modifier | Description | Since |
2650
+ | --- | --- | --- |
2651
+ | `brand`, `success`, `danger`, `warning`, `info` | On a toast: role color on the start edge and the title | 1.0.0 |
2652
+ | `top-start`, `top-end`, `bottom-start` | On the region: moves it to another corner; the default is the bottom end corner | 1.0.0 |
2653
+ | `animated` | On the region: toasts slide in, fade out and stack behind the newest, fanning out on hover or focus; `wu-animated` on an ancestor does the same | 1.0.0 |
2654
+
2655
+ ### Parts
2656
+
2657
+ | Part | Description | Since |
2658
+ | --- | --- | --- |
2659
+ | `wu-toast-region` | Fixed live region that holds the toasts; hidden when empty | 1.0.0 |
2660
+ | `wu-toast-title` | Toast title in semibold weight | 1.0.0 |
2661
+ | `wu-toast-close` | Close button with `aria-label`, its cross drawn in CSS | 1.0.0 |
2662
+
2663
+ ### Variables
2664
+
2665
+ | Variable | Default | Description | Since |
2666
+ | --- | --- | --- | --- |
2667
+ | `--wu-toast--Width` | `22rem` | Width of the toasts in the region | 1.0.0 |
2668
+
2669
+ ### States
2670
+
2671
+ | State | Description | Since |
2672
+ | --- | --- | --- |
2673
+ | `hidden` | On a toast: removed from layout, after a fade out with `animated` | 1.0.0 |
2674
+ | `:empty` | A region with no toasts is hidden | 1.0.0 |
2675
+
2676
+ ## toggle
2677
+
2678
+ On and off switch built on a native checkbox with `role="switch"`.
2679
+
2680
+ The track is 2.25em by 1.25em with a 1em thumb, so it follows the root type size and the `sm` and `lg` steps.
2681
+
2682
+ - Root class: `wu-toggle`
2683
+ - Sass entry: `@use "@wovenui/components/toggle";`
2684
+ - Needs: none
2685
+ - JavaScript: none, CSS only
2686
+ - Since: 1.0.0
2687
+
2688
+ ### Markup
2689
+
2690
+ ```html
2691
+ <label class="wu-toggle">
2692
+ <input class="wu-toggle-input" type="checkbox" role="switch" checked>
2693
+ <span class="wu-toggle-label">Email notifications</span>
2694
+ </label>
2695
+ ```
2696
+
2697
+ ```html
2698
+ <label class="wu-toggle lg animated">
2699
+ <input class="wu-toggle-input" type="checkbox" role="switch" aria-invalid="true">
2700
+ <span class="wu-toggle-label">Accept the terms</span>
2701
+ </label>
2702
+ ```
2703
+
2704
+ ### Modifiers
2705
+
2706
+ | Modifier | Description | Since |
2707
+ | --- | --- | --- |
2708
+ | `sm`, `lg` | Smaller or larger switch and label | 1.0.0 |
2709
+ | `animated` | Spring thumb when the user allows motion; `wu-animated` on an ancestor does the same | 1.0.0 |
2710
+
2711
+ ### Parts
2712
+
2713
+ | Part | Description | Since |
2714
+ | --- | --- | --- |
2715
+ | `wu-toggle-input` | The checkbox: track and thumb | 1.0.0 |
2716
+ | `wu-toggle-label` | Label text beside the switch | 1.0.0 |
2717
+
2718
+ ### States
2719
+
2720
+ | State | Description | Since |
2721
+ | --- | --- | --- |
2722
+ | `:checked` | Brand track and the thumb moved to the end | 1.0.0 |
2723
+ | `disabled` | Half opacity and a not-allowed cursor | 1.0.0 |
2724
+ | `aria-invalid="true"`, `:user-invalid` | Danger outline | 1.0.0 |
2725
+
2726
+ ## toolbar
2727
+
2728
+ Wrapping row of controls, with groups, separators, an end group and a boxed variant.
2729
+
2730
+ A toolbar holds controls of one height, so it lines up by itself. Status displays such as tags and badges go outside the row.
2731
+
2732
+ `role="toolbar"` promises arrow-key focus, so add it only with a script that provides it; otherwise use a plain `div`.
2733
+
2734
+ - Root class: `wu-toolbar`
2735
+ - Sass entry: `@use "@wovenui/components/toolbar";`
2736
+ - Needs: none
2737
+ - JavaScript: none, CSS only
2738
+ - Since: 1.0.0
2739
+
2740
+ ### Markup
2741
+
2742
+ ```html
2743
+ <div class="wu-toolbar">
2744
+ <div class="wu-toolbar-group">
2745
+ <button class="wu-btn sm" type="button">Bold</button>
2746
+ <button class="wu-btn sm" type="button">Italic</button>
2747
+ </div>
2748
+ <span class="wu-toolbar-separator" aria-hidden="true"></span>
2749
+ <div class="wu-toolbar-end">
2750
+ <button class="wu-btn brand sm" type="button">Publish</button>
2751
+ </div>
2752
+ </div>
2753
+ ```
2754
+
2755
+ ```html
2756
+ <div class="wu-toolbar boxed">
2757
+ <span class="wu-text sm">3 selected</span>
2758
+ <button class="wu-btn sm" type="button">Archive</button>
2759
+ </div>
2760
+ ```
2761
+
2762
+ ### Modifiers
2763
+
2764
+ | Modifier | Description | Since |
2765
+ | --- | --- | --- |
2766
+ | `boxed` | Border, padding and the alternate background | 1.0.0 |
2767
+ | `sm` | Smaller gap | 1.0.0 |
2768
+
2769
+ ### Parts
2770
+
2771
+ | Part | Description | Since |
2772
+ | --- | --- | --- |
2773
+ | `wu-toolbar-group` | Controls that stay together | 1.0.0 |
2774
+ | `wu-toolbar-separator` | Vertical line between groups | 1.0.0 |
2775
+ | `wu-toolbar-end` | Pushes itself and what follows to the end | 1.0.0 |
2776
+
2777
+ ## topbar
2778
+
2779
+ Application header with a brand, a scrolling nav, an end group, a pinned variant and a page progress line.
2780
+
2781
+ `wu-topbar-nav` works on the `nav` itself or on the `ul` inside it. When space runs out the nav scrolls first, then the brand truncates.
2782
+
2783
+ The bar stays in the flow; add `pinned` to stick it to the top. The progress line is decoration, so mark it `aria-hidden="true"`; it fills as the page scrolls and stays hidden when reduced motion is set.
2784
+
2785
+ - Root class: `wu-topbar`
2786
+ - Sass entry: `@use "@wovenui/components/topbar";`
2787
+ - Needs: none
2788
+ - JavaScript: none, CSS only
2789
+ - Since: 1.0.0
2790
+
2791
+ ### Markup
2792
+
2793
+ ```html
2794
+ <header class="wu-topbar pinned">
2795
+ <a class="wu-topbar-brand" href="/">Acme</a>
2796
+ <nav aria-label="Main">
2797
+ <ul class="wu-topbar-nav">
2798
+ <li><a class="wu-topbar-link" href="/dashboard" aria-current="page">Dashboard</a></li>
2799
+ <li><a class="wu-topbar-link" href="/billing">Billing</a></li>
2800
+ </ul>
2801
+ </nav>
2802
+ <div class="wu-topbar-end">
2803
+ <button class="wu-btn sm" type="button">Sign out</button>
2804
+ </div>
2805
+ <div class="wu-topbar-progress" aria-hidden="true"></div>
2806
+ </header>
2807
+ ```
2808
+
2809
+ ```html
2810
+ <header class="wu-topbar">
2811
+ <a class="wu-topbar-brand" href="/">Acme</a>
2812
+ <nav class="wu-topbar-nav" aria-label="Main">
2813
+ <a class="wu-topbar-link" href="/billing" aria-current="page">Billing</a>
2814
+ <a class="wu-topbar-link" href="/team">Team</a>
2815
+ </nav>
2816
+ </header>
2817
+ ```
2818
+
2819
+ ### Modifiers
2820
+
2821
+ | Modifier | Description | Since |
2822
+ | --- | --- | --- |
2823
+ | `pinned` | Sticks to the top of its scroll container, with a shadow once stuck | 1.0.0 |
2824
+
2825
+ ### Parts
2826
+
2827
+ | Part | Description | Since |
2828
+ | --- | --- | --- |
2829
+ | `wu-topbar-brand` | Brand link or text; truncates last | 1.0.0 |
2830
+ | `wu-topbar-nav` | Nav, or its list, that takes the free space and scrolls sideways | 1.0.0 |
2831
+ | `wu-topbar-link` | Nav link; a direct child `svg` is sized to `1em` | 1.0.0 |
2832
+ | `wu-topbar-end` | Group pushed to the inline end | 1.0.0 |
2833
+ | `wu-topbar-progress` | Line along the bottom edge that fills as the page scrolls | 1.0.0 |
2834
+
2835
+ ### States
2836
+
2837
+ | State | Description | Since |
2838
+ | --- | --- | --- |
2839
+ | `aria-current="page"` | On a link: current page, brand underline | 1.0.0 |