@salesql/sql_components_vue3 2.0.0 → 2.1.1

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 (79) hide show
  1. package/README.md +37 -1
  2. package/dist/constants/events.d.ts +20 -0
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +40 -6
  6. package/dist/index.es.js +2205 -618
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/sql_components_vue3.css +1 -1
  9. package/dist/ui/DsAlert/DsAlert.vue.d.ts +15 -2
  10. package/dist/ui/DsAvatar/DsAvatar.types.d.ts +6 -0
  11. package/dist/ui/DsAvatar/DsAvatar.vue.d.ts +47 -2
  12. package/dist/ui/DsButton/DsButton.types.d.ts +3 -1
  13. package/dist/ui/DsButton/DsButton.vue.d.ts +3 -0
  14. package/dist/ui/DsCell/DsCell.types.d.ts +19 -0
  15. package/dist/ui/DsCell/DsCell.vue.d.ts +71 -0
  16. package/dist/ui/DsCell/index.d.ts +2 -0
  17. package/dist/ui/DsCheckbox/DsCheckbox.types.d.ts +2 -0
  18. package/dist/ui/DsCheckbox/DsCheckbox.vue.d.ts +10 -1
  19. package/dist/ui/DsContactValue/DsContactValue.types.d.ts +4 -0
  20. package/dist/ui/DsContactValue/DsContactValue.vue.d.ts +18 -0
  21. package/dist/ui/DsCustomTag/DsCustomTag.types.d.ts +9 -0
  22. package/dist/ui/DsCustomTag/DsCustomTag.vue.d.ts +29 -0
  23. package/dist/ui/DsCustomTag/index.d.ts +2 -0
  24. package/dist/ui/DsDataCard/DsDataCard.types.d.ts +2 -0
  25. package/dist/ui/DsDataCard/DsDataCard.vue.d.ts +58 -1
  26. package/dist/ui/DsEmptyState/DsEmptyState.types.d.ts +12 -0
  27. package/dist/ui/DsEmptyState/DsEmptyState.vue.d.ts +53 -0
  28. package/dist/ui/DsEmptyState/index.d.ts +2 -0
  29. package/dist/ui/DsInput/DsInput.vue.d.ts +36 -2
  30. package/dist/ui/DsItemSelectorButton/DsItemSelectorButton.types.d.ts +20 -0
  31. package/dist/ui/DsItemSelectorButton/DsItemSelectorButton.vue.d.ts +50 -0
  32. package/dist/ui/DsItemSelectorButton/index.d.ts +2 -0
  33. package/dist/ui/DsMenuItem/DsMenuItem.types.d.ts +6 -0
  34. package/dist/ui/DsMenuItem/DsMenuItem.vue.d.ts +42 -0
  35. package/dist/ui/DsMenuItem/index.d.ts +2 -0
  36. package/dist/ui/DsModal/DsModal.types.d.ts +10 -0
  37. package/dist/ui/DsModal/DsModal.vue.d.ts +39 -0
  38. package/dist/ui/DsNotification/DsNotification.types.d.ts +7 -0
  39. package/dist/ui/DsNotification/DsNotification.vue.d.ts +14 -3
  40. package/dist/ui/DsPaginator/DsPaginator.types.d.ts +12 -0
  41. package/dist/ui/DsPaginator/DsPaginator.vue.d.ts +101 -0
  42. package/dist/ui/DsPaginator/index.d.ts +2 -0
  43. package/dist/ui/DsPill/DsPill.types.d.ts +11 -0
  44. package/dist/ui/DsPill/DsPill.vue.d.ts +46 -0
  45. package/dist/ui/DsPill/index.d.ts +2 -0
  46. package/dist/ui/DsPillWithBorder/DsPillWithBorder.types.d.ts +2 -0
  47. package/dist/ui/DsPillWithBorder/DsPillWithBorder.vue.d.ts +18 -0
  48. package/dist/ui/DsPillWithBorder/index.d.ts +2 -0
  49. package/dist/ui/DsRadio/DsRadio.types.d.ts +7 -0
  50. package/dist/ui/DsRadio/DsRadio.vue.d.ts +55 -0
  51. package/dist/ui/DsRadio/index.d.ts +2 -0
  52. package/dist/ui/DsRow/DsRow.types.d.ts +6 -0
  53. package/dist/ui/DsRow/DsRow.vue.d.ts +37 -0
  54. package/dist/ui/DsRow/index.d.ts +2 -0
  55. package/dist/ui/DsSegmentedProgressBar/DsSegmentedProgressBar.types.d.ts +5 -0
  56. package/dist/ui/DsSegmentedProgressBar/DsSegmentedProgressBar.vue.d.ts +62 -0
  57. package/dist/ui/DsSegmentedProgressBar/index.d.ts +2 -0
  58. package/dist/ui/DsSelect/DsSelect.types.d.ts +33 -0
  59. package/dist/ui/DsSelect/DsSelect.vue.d.ts +126 -0
  60. package/dist/ui/DsSelect/index.d.ts +2 -0
  61. package/dist/ui/DsSpinner/DsSpinner.types.d.ts +6 -0
  62. package/dist/ui/DsSpinner/DsSpinner.vue.d.ts +4 -4
  63. package/dist/ui/DsSwitch/DsSwitch.types.d.ts +6 -0
  64. package/dist/ui/DsSwitch/DsSwitch.vue.d.ts +30 -0
  65. package/dist/ui/DsSwitch/index.d.ts +2 -0
  66. package/dist/ui/DsTable/DsTable.types.d.ts +11 -0
  67. package/dist/ui/DsTable/DsTable.vue.d.ts +103 -10
  68. package/dist/ui/DsTag/DsTag.vue.d.ts +6 -0
  69. package/dist/ui/DsTextarea/DsTextarea.types.d.ts +8 -0
  70. package/dist/ui/DsTextarea/DsTextarea.vue.d.ts +14 -3
  71. package/dist/ui/DsTooltip/DsTooltip.types.d.ts +7 -0
  72. package/dist/ui/DsTooltip/DsTooltip.vue.d.ts +64 -0
  73. package/dist/ui/DsTooltip/index.d.ts +2 -0
  74. package/dist/ui/DsWizardStepper/DsWizardStepper.types.d.ts +13 -0
  75. package/dist/ui/DsWizardStepper/DsWizardStepper.vue.d.ts +58 -0
  76. package/dist/ui/DsWizardStepper/index.d.ts +2 -0
  77. package/llms-full.txt +1350 -0
  78. package/llms.txt +40 -0
  79. package/package.json +6 -3
package/llms-full.txt ADDED
@@ -0,0 +1,1350 @@
1
+ # @salesql/sql_components_vue3 — SalesQL Design System (v2.1.1)
2
+
3
+ > Librería de componentes Vue 3 (SalesQL) Presentational-only Vue 3 components (props in, events out; the consuming app owns state, i18n and data). This file is auto-generated from the component stories for LLM/agent consumption — do not edit by hand.
4
+
5
+ ## Using the library
6
+
7
+ ```ts
8
+ import { DsButton, DsNotification } from '@salesql/sql_components_vue3';
9
+ import '@salesql/sql_components_vue3/dist/style.css'; // once, at app entry
10
+ ```
11
+
12
+ All components are named exports with the `Ds` prefix. Variant constants and TypeScript types ship alongside each component (e.g. `DS_BUTTON_VARIANTS`, `DsButtonVariant`). Components never fetch data or hold app state — drive them with props and listen to their events.
13
+
14
+ ## Components (30)
15
+
16
+ - **DsAvatar** (Core) — Contact/user avatar: an image, generated initials with a deterministic color hash, or an
17
+ - **DsBadge** (Core) — Solid, white-text pill for value types and counts (e.g. "Work", "New"). **Presentational
18
+ - **DsButton** (Core) — Standard clickable control with the DS variant/size vocabulary, plus icon-only
19
+ - **DsSpinner** (Core) — Indeterminate loading indicator — a circular SVG spinner. **Presentational only**: no
20
+ - **DsTag** (Core) — Small bordered chip with an optional × close control. **Presentational only**: `close`
21
+ - **DsCell** (Data) — A single `<td>` cell in the `DsTable` compound API. **Presentational only**: no props
22
+ - **DsContactValue** (Data) — A single email/phone row: a verification glyph, the value itself, and a type badge
23
+ - **DsCustomTag** (Data) — Static status chip (tinted background + matching border/text), non-interactive.
24
+ - **DsDataCard** (Data) — The signature SalesQL emails/phones card: a header (icon, title, sort/edit/collapse/close
25
+ - **DsEmptyState** (Data) — Centered empty-state panel for empty tables, lists, or search results: an optional
26
+ - **DsPaginator** (Data) — Page-window pagination control: prev/next chevrons, numbered page buttons that collapse
27
+ - **DsPill** (Data) — Small solid capsule with white text and a free-form background color (e.g. a dynamic
28
+ - **DsPillWithBorder** (Data) — Bordered pill/chip rendered as a real `<button>`, with an optional leading icon slot and
29
+ - **DsRow** (Data) — A `<tr>` row for the `DsTable` compound API, with hover and selected treatment;
30
+ - **DsSegmentedProgressBar** (Data) — Horizontal, multi-segment data-viz track (e.g. a verification-quality breakdown), with an
31
+ - **DsTable** (Data) — Data-dense table with two composition modes: **declarative** (`columns` + `rows`, with
32
+ - **DsVerificationIcon** (Data) — Status glyph for a contact data point (email/phone): verified, unverified, or newly
33
+ - **DsAlert** (Feedback) — Inline banner for status/confirmation messages (success, info, warning, error). It
34
+ - **DsModal** (Feedback) — Centered dialog with an optional header (title + close button) and footer action row.
35
+ - **DsNotification** (Feedback) — Fixed-position toast card for transient feedback (saved, updated, failed…). It renders
36
+ - **DsTooltip** (Feedback) — Dark bubble that describes a wrapped trigger on hover or keyboard focus, with 4-way
37
+ - **DsCheckbox** (Forms) — Checked / unchecked / indeterminate selection control. **Presentational only**.
38
+ - **DsInput** (Forms) — Single-line text field — the DS "Input". **Presentational only**: it drives its own
39
+ - **DsItemSelectorButton** (Forms) — Pill-shaped trigger that shows the currently assigned item (folder, tag, owner…)
40
+ - **DsRadio** (Forms) — A single radio control (accent-ring dot when checked). **Presentational only**.
41
+ - **DsSelect** (Forms) — Dropdown select — a native-`<select>`-like single/multi picker with a real
42
+ - **DsSwitch** (Forms) — On/off toggle (accent track when on). **Presentational only**: it reflects the
43
+ - **DsTextarea** (Forms) — Multi-line text field — the DS "Textarea", sibling of `DsInput` with the same
44
+ - **DsMenuItem** (Navigation) — Sidebar navigation row: an icon with an optional label, usable full-width or collapsed to
45
+ - **DsWizardStepper** (Navigation) — Numbered multi-step flow: a row of step chips (completed / active / incomplete / neutral)
46
+
47
+ ---
48
+ <!-- DsAvatar · Core -->
49
+
50
+ ## DsAvatar
51
+
52
+ Contact/user avatar: an image, generated initials with a deterministic color hash, or an
53
+ empty placeholder — whichever is available, in that priority order. **Presentational
54
+ only**: no events, no interaction.
55
+
56
+ ### Props
57
+
58
+ | Prop | Type | Default | Description |
59
+ | ------------ | ------------------ | ----------- | ---------------------------------------------------------------------------------------------------- |
60
+ | `image` | `string` | `''` | Image URL. When set, renders an `<img>` (highest priority over initials/placeholder). |
61
+ | `firstName` | `string` | `''` | Used with `lastName` to compute initials and seed the color hash. |
62
+ | `lastName` | `string` | `''` | See `firstName`. |
63
+ | `letters` | `string` | `''` | Explicit initials — overrides the computed `firstName`+`lastName` initials when non-empty. |
64
+ | `uid` | `string` | `''` | Seed for the deterministic background color hash (falls back to the name, then `letters`). |
65
+ | `size` | `number` | `40` | Square box size in px (used for both width and height unless overridden). |
66
+ | `width` | `number` | `undefined` | Explicit width in px — overrides `size` on that axis. |
67
+ | `height` | `number` | `undefined` | Explicit height in px — overrides `size` on that axis. |
68
+ | `textSize` | `number` | `undefined` | Initials font-size in px — overrides the computed ~40%-of-box value. |
69
+ | `fontWeight` | `number \| string` | `undefined` | Initials font-weight — overrides the default token weight (900). |
70
+ | `circular` | `boolean` | `false` | Fully round avatar (vs the default rounded-square). Per the DS, reserve this for the logged-in user. |
71
+ | `alt` | `string` | `undefined` | Accessible label for the image variant; falls back to the derived name/letters when omitted. |
72
+
73
+ ### Accessibility
74
+
75
+ - Image variant: `<img alt>` resolves from `alt`, falling back to the derived
76
+ `"First Last"` (or `letters`) when `alt` is not provided; pass `alt=""` to mark a
77
+ purely decorative image.
78
+ - Initials variant: `role="img"` + `aria-label` with the same resolved label, so screen
79
+ readers announce the represented person.
80
+ - Placeholder variant (no image and no initials): `aria-hidden="true"`, since there is
81
+ nothing meaningful to announce.
82
+
83
+ ### When to use
84
+
85
+ - Contact/user avatars in lists, headers, and cards — pass `uid` so the color hash stays
86
+ stable per person across renders.
87
+ - Reserve `circular` for the current/logged-in user per the design system convention;
88
+ other contacts use the default rounded-square shape.
89
+
90
+ ---
91
+
92
+ <!-- DsBadge · Core -->
93
+
94
+ ## DsBadge
95
+
96
+ Solid, white-text pill for value types and counts (e.g. "Work", "New"). **Presentational
97
+ only**: no events, no interaction — it just renders its slot content on a solid tone
98
+ background.
99
+
100
+ ### Props
101
+
102
+ | Prop | Type | Default | Description |
103
+ | ------ | ------------------------------------------------------------------- | ---------- | ------------------------ |
104
+ | `tone` | `'accent' \| 'grey' \| 'info' \| 'success' \| 'error' \| 'warning'` | `'accent'` | Solid fill color family. |
105
+
106
+ ### Accessibility
107
+
108
+ - Purely presentational — no ARIA role is applied. The badge conveys its meaning
109
+ through the slotted text, not color alone, so always pass meaningful text content.
110
+
111
+ ### When to use
112
+
113
+ - A solid, high-contrast pill for value types or counts (e.g. `Work`, `Direct`, `New`).
114
+ - Not a status chip: for a tinted, bordered status label use `DsCustomTag`, and for a
115
+ removable/clickable value chip use `DsTag`.
116
+
117
+ ---
118
+
119
+ <!-- DsButton · Core -->
120
+
121
+ ## DsButton
122
+
123
+ Standard clickable control with the DS variant/size vocabulary, plus icon-only
124
+ (`circle`/`square`), `loading`, and `selected` (toggle) states. **Presentational only**:
125
+ it renders a native `<button>` and emits `click` — the consuming app owns what happens
126
+ next.
127
+
128
+ ### Props
129
+
130
+ | Prop | Type | Default | Description |
131
+ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------- |
132
+ | `variant` | `'default' \| 'primary' \| 'secondary' \| 'tertiary' \| 'quaternary' \| 'quinary' \| 'senary' \| 'dotted' \| 'danger' \| 'warning'` | `'default'` | Visual style. `warning` is a legacy solid-red alias of `danger` — prefer `danger`. |
133
+ | `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` | Control height + padding. |
134
+ | `nativeType` | `'button' \| 'submit' \| 'reset'` | `'button'` | Native `<button type>`. |
135
+ | `leftIcon` | `string` | `''` | Material Icon name rendered before the label (or use the `#leading` slot). |
136
+ | `rightIcon` | `string` | `''` | Material Icon name rendered after the label (or use the `#trailing` slot). |
137
+ | `ariaLabel` | `string` | `''` | Accessible label — required for icon-only (`circle`/`square`) buttons. |
138
+ | `disabled` | `boolean` | `false` | Disables the button and blocks `click`. |
139
+ | `loading` | `boolean` | `false` | Shows a spinner overlay, natively disables the button, and blocks `click`. |
140
+ | `circle` | `boolean` | `false` | Round icon-only button (width === height). |
141
+ | `square` | `boolean` | `false` | Flat 4px-radius icon-only button (width === height). |
142
+ | `noPadding` | `boolean` | `false` | Zero the square padding for a tight glyph (only takes effect with `square`). |
143
+ | `selected` | `boolean` | `false` | Toggle state — accent outline + fill, sets `aria-pressed`. |
144
+ | `autofocus` | `boolean` | `false` | Native `autofocus`. |
145
+
146
+ ### Events
147
+
148
+ | Event | Payload | When |
149
+ | ------- | ------------ | -------------------------------------------------------------- |
150
+ | `click` | `MouseEvent` | The button was clicked, unless `disabled` or `loading` is set. |
151
+
152
+ ### Accessibility
153
+
154
+ - `aria-busy` is set while `loading`; `aria-disabled` while `disabled` or `loading` —
155
+ and the native `disabled` attribute is also applied in both cases, so the button
156
+ drops out of the tab order.
157
+ - `aria-pressed` mirrors `selected` for toggle-button semantics.
158
+ - `ariaLabel` maps to `aria-label` — always pass it for icon-only (`circle`/`square`)
159
+ buttons, since they render no visible label text.
160
+ - While `loading`, the label/icons stay mounted but hidden (`aria-hidden`, visually
161
+ hidden) so the button keeps its width instead of jumping when the spinner appears.
162
+
163
+ ### When to use
164
+
165
+ - `primary` for the page's main call to action; `secondary`/`tertiary` for lower
166
+ emphasis; `danger` for destructive actions (prefer it over the legacy `warning`).
167
+ - Always set `ariaLabel` on `circle`/`square` icon-only buttons.
168
+ - Use `loading` for in-flight async actions instead of swapping in a separate
169
+ `DsSpinner`.
170
+
171
+ ---
172
+
173
+ <!-- DsSpinner · Core -->
174
+
175
+ ## DsSpinner
176
+
177
+ Indeterminate loading indicator — a circular SVG spinner. **Presentational only**: no
178
+ events, no timers; it spins for as long as it is mounted. Mount it while work is
179
+ pending, unmount it when the work resolves.
180
+
181
+ ### Props
182
+
183
+ | Prop | Type | Default | Description |
184
+ | ------- | ------------------------------------------ | ----------- | ------------------------------------------------------------------------ |
185
+ | `size` | `number \| 'small' \| 'medium' \| 'large'` | `'medium'` | Diameter in px, or a named size token. |
186
+ | `tone` | `'dark' \| 'light' \| 'accent'` | `'dark'` | Stroke color. Use `light` on dark surfaces, `accent` for brand emphasis. |
187
+ | `label` | `string` | `'Loading'` | Accessible label announced to screen readers (`aria-label` on the SVG). |
188
+
189
+ ### Accessibility
190
+
191
+ - The SVG carries `role="status"` + `aria-label`, so assistive tech announces the
192
+ loading state. Always pass a meaningful `label` when the default "Loading" is not
193
+ specific enough (e.g. `"Verifying email"`).
194
+ - `tone="light"` needs a dark background to stay visible — see the **Tones** variant.
195
+
196
+ ### When to use
197
+
198
+ - Inline within a button or field while an async action runs.
199
+ - As a block-level placeholder for a panel whose data is still loading.
200
+
201
+ For deterministic progress (a known percentage), use `DsSegmentedProgressBar` instead.
202
+
203
+ ---
204
+
205
+ <!-- DsTag · Core -->
206
+
207
+ ## DsTag
208
+
209
+ Small bordered chip with an optional × close control. **Presentational only**: `close`
210
+ is emitted on click, but the parent owns whether/how the tag is actually removed from
211
+ its list.
212
+
213
+ ### Props
214
+
215
+ | Prop | Type | Default | Description |
216
+ | ------------ | -------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------- |
217
+ | `tone` | `'default' \| 'grey' \| 'success' \| 'info' \| 'warning' \| 'error'` | `'default'` | Border/background color family. `grey` is a DS alias of `default`. |
218
+ | `rounded` | `boolean` | `false` | Semicircular sides (vs the default control radius). |
219
+ | `closable` | `boolean` | `false` | Show a × control that emits `close`. |
220
+ | `clickable` | `boolean` | `false` | Cursor: pointer affordance — set this when you attach a native `@click` listener. |
221
+ | `truncate` | `boolean` | `false` | Opt-in single-line ellipsis; cap the width via the `--ds-tag-max-width` CSS custom property. |
222
+ | `closeLabel` | `string` | `''` | Accessible label for the × control (icon-only). |
223
+
224
+ ### Events
225
+
226
+ | Event | Payload | When |
227
+ | ------- | ------- | -------------------------------- |
228
+ | `close` | — | The × close control was clicked. |
229
+
230
+ ### Accessibility
231
+
232
+ - The × control is icon-only, so `closeLabel` maps to its `aria-label` — always pass it
233
+ when `closable` is set.
234
+ - `clickable` only toggles the cursor style; the component can't detect an attached
235
+ native `@click` listener, so it does not add a role/tabindex/keyboard handler on its
236
+ own — if you make a tag clickable, ensure it stays keyboard-reachable on your side.
237
+
238
+ ### When to use
239
+
240
+ - Removable or editable filter/value chips (`closable`). For a static, non-removable
241
+ status label use `DsCustomTag`; for a solid count/value pill use `DsBadge`.
242
+
243
+ ---
244
+
245
+ <!-- DsCell · Data -->
246
+
247
+ ## DsCell
248
+
249
+ A single `<td>` cell in the `DsTable` compound API. **Presentational only**: no props
250
+ in, no events out — layout and state come entirely from its props, driven by the
251
+ parent `DsTable`/`DsRow`.
252
+
253
+ ### Props
254
+
255
+ | Prop | Type | Default | Description |
256
+ | -------------- | ------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
257
+ | `first` | `boolean` | `false` | First cell in the row — pairs with `selected` to draw the 3px accent left rail. |
258
+ | `selected` | `boolean` | `false` | Whether the owning row is selected (repeat it here, per the DS contract). |
259
+ | `width` | `string \| number` | `null` | Fixed cell width (number → px, or any CSS length). |
260
+ | `truncate` | `boolean` | `false` | Opt-in single-line ellipsis truncation. Off by default (the 72px cell allows multi-line). |
261
+ | `pinned` | `'left' \| 'right'` | `null` | Pins this cell to the left/right edge (`position: sticky`) for horizontal scroll. |
262
+ | `pinnedOffset` | `string \| number` | `null` | Cumulative offset from the pinned edge, for stacking several pinned cells on the same side. Defaults to `0px`. |
263
+
264
+ ### Accessibility
265
+
266
+ - Renders a plain `<td>` — accessibility for the cell's content (labels, roles) is the
267
+ responsibility of whatever is passed into the default slot.
268
+ - No interactive behavior of its own; keyboard/focus concerns belong to the controls
269
+ rendered inside it.
270
+
271
+ ### When to use
272
+
273
+ - Always inside a `DsTable` row (or a `<tr>` built by hand to mirror it) — never as a
274
+ standalone cell outside a table structure.
275
+ - Use `pinned` together with `pinnedOffset` when more than one column needs to stay
276
+ fixed on the same side during horizontal scroll.
277
+
278
+ ---
279
+
280
+ <!-- DsContactValue · Data -->
281
+
282
+ ## DsContactValue
283
+
284
+ A single email/phone row: a verification glyph, the value itself, and a type badge
285
+ that "marquees" out on hover (or keyboard focus) to reveal copy and optional rating
286
+ actions. **Presentational only**: it copies to the clipboard itself, but rating and
287
+ any other consequence stay with the consuming app via events.
288
+
289
+ ### Props
290
+
291
+ | Prop | Type | Default | Description |
292
+ | ---------------- | ----------------- | ------------- | ------------------------------------------------------------------------------- |
293
+ | `value` | `string` | — | The email address or phone number (required). |
294
+ | `valueType` | `string` | `''` | Type label shown as a badge ("Work", "Direct", "Mobile"…). |
295
+ | `verified` | `boolean \| null` | `null` | Verification state, forwarded to `DsVerificationIcon`. |
296
+ | `main` | `boolean` | `false` | Highlights as the "most likely" value (accent badge + accent hover background). |
297
+ | `isContactData` | `boolean` | `false` | True for emails (affects the unverified glyph color). |
298
+ | `showRate` | `boolean` | `false` | Show thumbs up/down rating actions on hover. |
299
+ | `correct` | `boolean \| null` | `null` | Current rating: `true` = correct, `false` = incorrect, `null` = unrated. |
300
+ | `hideValue` | `boolean` | `false` | Locks the value: a static lock glyph replaces copy + rating entirely. |
301
+ | `copyLabel` | `string` | `'Copy'` | Accessible label / tooltip for the copy button. |
302
+ | `copiedLabel` | `string` | `'Copied!'` | Accessible label / tooltip shown for ~1.2s after a successful copy. |
303
+ | `correctLabel` | `string` | `'Correct'` | Accessible label / tooltip for the thumbs-up button. |
304
+ | `incorrectLabel` | `string` | `'Incorrect'` | Accessible label / tooltip for the thumbs-down button. |
305
+ | `hideValueLabel` | `string` | `''` | Accessible name for the lock glyph. Empty → decorative (`aria-hidden`). |
306
+
307
+ ### Events
308
+
309
+ | Event | Payload | When |
310
+ | ----------- | -------- | -------------------------------------------------------------------------------- |
311
+ | `copy` | `string` | The copy button was clicked. Payload is the copied `value`. |
312
+ | `correct` | — | The thumbs-up rating button was clicked (only rendered when `showRate` is on). |
313
+ | `incorrect` | — | The thumbs-down rating button was clicked (only rendered when `showRate` is on). |
314
+
315
+ ### Accessibility
316
+
317
+ - The copy and rating buttons are icon-only; `copyLabel`/`copiedLabel`/`correctLabel`/`incorrectLabel`
318
+ drive both their `aria-label` and `title`.
319
+ - The rating buttons expose their state via `aria-pressed` (`correct === true` / `correct === false`).
320
+ - The lock glyph (`hideValue`) is `aria-hidden` unless `hideValueLabel` is supplied, in which
321
+ case it becomes `role="img"` with that `aria-label`.
322
+ - The type badge is not `aria-hidden` — it is real classification data, so screen readers
323
+ announce "value, type" even though it visually cross-fades with the actions on hover.
324
+
325
+ ### When to use
326
+
327
+ - As a row inside `DsDataCard` for an individual email or phone value — `DsDataCard` already
328
+ wires `main`, `showRate`, and the rating/copy events for you.
329
+ - Pass `hideValue` for a gated/locked datum instead of rendering the real value.
330
+
331
+ ---
332
+
333
+ <!-- DsCustomTag · Data -->
334
+
335
+ ## DsCustomTag
336
+
337
+ Static status chip (tinted background + matching border/text), non-interactive.
338
+ **Presentational only**: no events, no close control — for that, use `DsTag`.
339
+
340
+ ### Props
341
+
342
+ | Prop | Type | Default | Description |
343
+ | ------- | --------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
344
+ | `type` | `'success' \| 'warning' \| 'danger' \| 'info' \| 'draft' \| 'accent'` | `'info'` | Tint/border color family. `draft` is the only solid-fill variant; `danger`/`accent` map to the `error-*`/`info-*` token families respectively (intentional DS naming, kept as-is). |
345
+ | `round` | `boolean` | `false` | Fully rounded sides (vs the default control radius). |
346
+
347
+ ### Accessibility
348
+
349
+ - Purely presentational — no ARIA role is applied. Meaning is conveyed through the
350
+ slotted text, not color alone, so always pass meaningful text content.
351
+
352
+ ### When to use
353
+
354
+ - Static status labels (e.g. "Draft", "Verified") that never need to be dismissed or
355
+ clicked.
356
+ - For a removable/clickable chip use `DsTag`; for a solid count/value pill use
357
+ `DsBadge`.
358
+
359
+ ---
360
+
361
+ <!-- DsDataCard · Data -->
362
+
363
+ ## DsDataCard
364
+
365
+ The signature SalesQL emails/phones card: a header (icon, title, sort/edit/collapse/close
366
+ actions) over a body that renders a "most likely" `DsContactValue` plus a scrollable
367
+ "more" list — or a loading spinner, an empty state, or a locked/upsell banner instead.
368
+ **Presentational only**: loading/empty/locked/collapse are driven entirely by props;
369
+ every user action bubbles out as an event.
370
+
371
+ ### Props
372
+
373
+ | Prop | Type | Default | Description |
374
+ | ----------------- | ------------------------ | ----------------- | -------------------------------------------------------------------------------------------------------------- |
375
+ | `title` | `string` | — | Card header title (required). |
376
+ | `icon` | `string` | `'email'` | Material Icon name for the header (and the empty-state badge). |
377
+ | `loading` | `boolean` | `false` | Swaps the body for a centered spinner. Wins over `empty`/`locked`/data. |
378
+ | `loadingLabel` | `string` | `''` | Accessible name for the loading spinner (falls back to `DsSpinner`'s own default). Overridable via `#loading`. |
379
+ | `locked` | `boolean` | `false` | Locked/upsell state: shows a banner instead of content. Wins over `empty`/data (loading still wins over it). |
380
+ | `mostLikely` | `DsContactDatum \| null` | `null` | The highlighted "most likely" data point, rendered as a `main` `DsContactValue`. |
381
+ | `more` | `DsContactDatum[]` | `[]` | Additional data points, rendered in a scrollable "MORE" list. |
382
+ | `collapsible` | `boolean` | `false` | Shows a chevron toggle in the header that collapses/expands the body. |
383
+ | `showSort` | `boolean` | `true` | Show the sort icon-button in the header. |
384
+ | `showEdit` | `boolean` | `false` | Show the text "Edit" action in the header. |
385
+ | `showClose` | `boolean` | `false` | Show the × close button in the header. |
386
+ | `isContactData` | `boolean` | `false` | Forwarded to every `DsContactValue` (true for emails). |
387
+ | `showRate` | `boolean` | `false` | Forwarded to every `DsContactValue` — shows thumbs up/down rating on hover. |
388
+ | `editLabel` | `string` | `'Edit'` | Visible text for the edit action. |
389
+ | `emptyLabel` | `string` | `'No data found'` | Title shown in the empty state (no `mostLikely` and no `more`). |
390
+ | `emptySubtitle` | `string` | `''` | Optional muted line under the empty title. |
391
+ | `lockedLabel` | `string` | `'Locked'` | Fallback text for the locked banner when no `#locked` slot is provided. |
392
+ | `mostLikelyLabel` | `string` | `'MOST LIKELY'` | Section label above the "most likely" value (shown only when there are more than 3 items total). |
393
+ | `moreLabel` | `string` | `'MORE'` | Section label above the "more" list (shown only when there are more than 3 items total). |
394
+ | `sortLabel` | `string` | `'Sort'` | `aria-label` for the sort icon-button. |
395
+ | `toggleLabel` | `string` | `'Toggle'` | `aria-label` for the collapse/expand icon-button. |
396
+ | `closeLabel` | `string` | `'Close'` | `aria-label` for the × close button. |
397
+
398
+ ### Events
399
+
400
+ | Event | Payload | When |
401
+ | ----------- | ---------------- | ------------------------------------------------------------------------------ |
402
+ | `copy` | `string` | A `DsContactValue` row's copy button was clicked. Payload is the copied value. |
403
+ | `sort` | — | The header's sort icon-button was clicked. |
404
+ | `edit` | — | The header's "Edit" action was clicked. |
405
+ | `close` | — | The header's × close button was clicked. |
406
+ | `correct` | `DsContactDatum` | A row's thumbs-up rating was clicked. Payload is that row's datum. |
407
+ | `incorrect` | `DsContactDatum` | A row's thumbs-down rating was clicked. Payload is that row's datum. |
408
+
409
+ ### Accessibility
410
+
411
+ - The root `<section>` carries `aria-label` (the card `title`) and `aria-busy="true"`
412
+ while `loading`.
413
+ - The header's action row stops click-through to the collapsible header via `@click.stop`,
414
+ so toggling collapse doesn't fire when clicking sort/edit/toggle/close.
415
+ - All icon-only header buttons (`sort`, `toggle`, `close`) carry an `aria-label` from
416
+ their respective `*Label` prop.
417
+
418
+ ### When to use
419
+
420
+ - The primary card for a contact's emails or phones — pass `showRate` when the consuming
421
+ app supports data-quality feedback.
422
+ - Use `locked` for gated/upsell data instead of hiding the card entirely; drive the CTA
423
+ via the `#locked` slot.
424
+ - For a single value row outside this card layout, use `DsContactValue` directly.
425
+
426
+ ---
427
+
428
+ <!-- DsEmptyState · Data -->
429
+
430
+ ## DsEmptyState
431
+
432
+ Centered empty-state panel for empty tables, lists, or search results: an optional
433
+ illustration, a title, an optional subtitle, and slots for an action (`#button`) and a
434
+ pinned table header (`#table-header`). **Presentational only**: no events, all text
435
+ arrives via props/slots.
436
+
437
+ ### Props
438
+
439
+ | Prop | Type | Default | Description |
440
+ | --------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
441
+ | `image` | `string` | `''` | Illustration URL rendered above the title. Decorative — always renders with `alt=""`. |
442
+ | `title` | `string` | `''` | Empty-state heading. Rendered as a styled `<p>`, never an `<h1>`, so it can't corrupt the consuming page's heading outline. |
443
+ | `subtitle` | `string` | `''` | Supporting copy under the title. Supports embedded line breaks (`white-space: pre-line`). |
444
+ | `roundedBorder` | `boolean` | `false` | Rounds the panel corners (DS `radius-card-lg`) instead of the default square edge. |
445
+
446
+ ### Accessibility
447
+
448
+ - The illustration renders with `alt=""` since it is purely decorative — the title/subtitle
449
+ carry the meaning.
450
+ - The title is a plain `<p>`, not a semantic heading — if the empty state needs a real
451
+ heading in the page's outline, provide one via the `#table-header` slot or the
452
+ surrounding app markup.
453
+
454
+ ### When to use
455
+
456
+ - Tables/lists with zero results — pair with `#table-header` to keep a column header
457
+ visible above the empty message.
458
+ - First-run/onboarding panels, paired with a call-to-action via `#button`.
459
+
460
+ ---
461
+
462
+ <!-- DsPaginator · Data -->
463
+
464
+ ## DsPaginator
465
+
466
+ Page-window pagination control: prev/next chevrons, numbered page buttons that collapse
467
+ into ellipses past 7 pages, and a page-size `<select>`. **Presentational only**: it owns
468
+ no page state itself — the consuming app supplies `currentPage`/`pageSize` and reacts to
469
+ its events.
470
+
471
+ ### Props
472
+
473
+ | Prop | Type | Default | Description |
474
+ | ----------------- | ---------- | ------------------- | ----------------------------------------------------------------------------------- |
475
+ | `total` | `number` | — (required) | Total number of items being paginated; drives `pageCount` together with `pageSize`. |
476
+ | `pageSize` | `number` | `25` | Items per page. |
477
+ | `currentPage` | `number` | `1` | 1-based current page. |
478
+ | `pageSizes` | `number[]` | `[10, 25, 50, 100]` | Options rendered in the page-size `<select>`. |
479
+ | `limitMessage` | `string` | `''` | Optional message shown before the pager (e.g. `"Showing 1-25 of 500"`). |
480
+ | `pageSizeSuffix` | `string` | `''` | Appended to each page-size option label (e.g. consumer passes `'/page'`). |
481
+ | `prevLabel` | `string` | `''` | `aria-label` for the icon-only prev chevron button. |
482
+ | `nextLabel` | `string` | `''` | `aria-label` for the icon-only next chevron button. |
483
+ | `paginationLabel` | `string` | `''` | `aria-label` for the root `<nav>`. |
484
+ | `pageSizeLabel` | `string` | `''` | `aria-label` for the page-size `<select>`. |
485
+
486
+ ### Events
487
+
488
+ | Event | Payload | When |
489
+ | ------------------ | -------- | ------------------------------------------------------------------------------------------- |
490
+ | `page-change` | `number` | A page button or a prev/next chevron was activated with a valid target page (1..pageCount). |
491
+ | `page-size-change` | `number` | A new page-size option was chosen in the `<select>`. |
492
+
493
+ ### Accessibility
494
+
495
+ - The root is a `<nav>` — pass `paginationLabel` to give it an accessible name.
496
+ - Prev/next chevrons are icon-only `DsButton`s — pass `prevLabel`/`nextLabel` or they
497
+ render without an `aria-label`.
498
+ - The active page button carries `aria-current="page"`.
499
+ - Ellipsis entries are `aria-hidden="true"` (decorative, not a control).
500
+ - Prev/next disable natively (`disabled`) at the first/last page.
501
+
502
+ ### When to use
503
+
504
+ - Any paged table or list where the consumer owns the current page/page-size state.
505
+
506
+ ---
507
+
508
+ <!-- DsPill · Data -->
509
+
510
+ ## DsPill
511
+
512
+ Small solid capsule with white text and a free-form background color (e.g. a dynamic
513
+ tag/folder color), with an optional trailing icon button. **Presentational only**:
514
+ label via the default slot, intent out via `click`.
515
+
516
+ ### Props
517
+
518
+ | Prop | Type | Default | Description |
519
+ | ------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
520
+ | `icon` | `string` | `''` | Material Icons ligature name; when set, renders a trailing icon button. |
521
+ | `background` | `string` | `''` | Free-form CSS color (e.g. a dynamic tag/folder color); when empty, falls back to the default token background (`$slate-soft`). |
522
+ | `iconLabel` | `string` | `''` | Accessible name for the trailing icon button (icon-only control). |
523
+
524
+ ### Events
525
+
526
+ | Event | Payload | When |
527
+ | ------- | ------- | ----------------------------------------------------------------------------------------- |
528
+ | `click` | — | The trailing icon button was clicked. Only fires when `icon` is set — no icon, no button. |
529
+
530
+ ### Accessibility
531
+
532
+ - The trailing icon button is icon-only, so `iconLabel` maps to its `aria-label` — always
533
+ pass it when using `icon`.
534
+ - The icon glyph itself is `aria-hidden="true"`; only the button's `aria-label` is announced.
535
+ - Long text truncates with an ellipsis instead of stretching the pill — the consumer must
536
+ bound the pill's width for this to trigger.
537
+
538
+ ### When to use
539
+
540
+ - Tags/labels with a dynamic color (folder color, CRM tag color), optionally with a way
541
+ to remove/edit them via the icon button.
542
+ - For a bordered, button-styled filter chip instead, see `DsPillWithBorder`.
543
+
544
+ ---
545
+
546
+ <!-- DsPillWithBorder · Data -->
547
+
548
+ ## DsPillWithBorder
549
+
550
+ Bordered pill/chip rendered as a real `<button>`, with an optional leading icon slot and
551
+ a label from the default slot. **Presentational only**: no configurable props — content
552
+ and click intent flow entirely through slots/events.
553
+
554
+ ### Props
555
+
556
+ This component has no configurable props (its type contract is an intentionally empty
557
+ interface, kept typed so it has a public contract to extend if the DS ever adds a
558
+ variant/size). Content comes from the default slot (label) and the optional `#icon` slot
559
+ (a pre-rendered leading icon node).
560
+
561
+ ### Events
562
+
563
+ | Event | Payload | When |
564
+ | ------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
565
+ | `click` | — | The chip (the whole `<button>`) was activated — a mouse click or, via native `<button>` semantics, `Enter`/`Space`. |
566
+
567
+ ### Accessibility
568
+
569
+ - Renders a real `<button>`, replacing the underlying Design System's non-interactive
570
+ `<div onClick>` — so it is keyboard-reachable and operable out of the box.
571
+ - `:focus-visible` gets a visible outline ring.
572
+ - The optional leading icon comes through the `#icon` slot; mark the icon node
573
+ `aria-hidden="true"` in the consumer's markup (as the examples below do) since the
574
+ button's accessible name is its text content.
575
+ - Long labels truncate to a single line instead of wrapping — bound the chip's width to
576
+ trigger it.
577
+
578
+ ### When to use
579
+
580
+ - Filter/segment chips (e.g. "Location", "Industry") that toggle a panel or apply a
581
+ filter on click.
582
+ - For a solid, non-bordered tag-style pill instead, see `DsPill`.
583
+
584
+ ---
585
+
586
+ <!-- DsRow · Data -->
587
+
588
+ ## DsRow
589
+
590
+ A `<tr>` row for the `DsTable` compound API, with hover and selected treatment;
591
+ `clickable` makes the whole row a keyboard-operable click target. **Presentational
592
+ only**: always used inside a `<table>`/`<tbody>`, paired with `DsCell` children.
593
+
594
+ ### Props
595
+
596
+ | Prop | Type | Default | Description |
597
+ | ----------- | --------- | ------- | ------------------------------------------------------------------- |
598
+ | `selected` | `boolean` | `false` | Selected treatment (accent surface); wins over the hover state. |
599
+ | `clickable` | `boolean` | `false` | Makes the whole row an interactive, keyboard-operable click target. |
600
+
601
+ ### Events
602
+
603
+ | Event | Payload | When |
604
+ | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
605
+ | `click` | — | The row was clicked, or activated via `Enter`/`Space` while focused. Only fires when `clickable` is `true` — a plain row is not a click target. |
606
+
607
+ ### Accessibility
608
+
609
+ - `clickable` rows get `tabindex="0"` so they're keyboard-reachable; non-clickable rows
610
+ stay out of the tab order.
611
+ - `Enter`/`Space` on a focused clickable row trigger the same `click` event as a mouse
612
+ click (`preventDefault()` stops the page from scrolling on `Space`).
613
+ - `:focus-visible` gets a visible, inset outline ring so it doesn't clip against table
614
+ borders.
615
+ - A pinned/sticky `DsCell` inside the row mirrors the row's hover/selected background via
616
+ `:deep()`, since it paints its own opaque background and can't inherit the row's colors.
617
+
618
+ ### When to use
619
+
620
+ - Table body rows; pair `clickable` with rows that navigate to a detail view or open a
621
+ panel.
622
+ - Combine with `selected` to reflect a row chosen elsewhere (a checkbox, external state).
623
+
624
+ ---
625
+
626
+ <!-- DsSegmentedProgressBar · Data -->
627
+
628
+ ## DsSegmentedProgressBar
629
+
630
+ Horizontal, multi-segment data-viz track (e.g. a verification-quality breakdown), with an
631
+ optional legend beneath it. **Presentational only**: given `segments` and a `total`, it
632
+ renders proportional colored bars — no events.
633
+
634
+ ### Props
635
+
636
+ | Prop | Type | Default | Description |
637
+ | ------------ | -------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
638
+ | `segments` | `DsProgressSegment[]` (`{ name?, amount, color }`) | `[]` | The bar's segments; each renders as `amount / total` of the track width in `color`. |
639
+ | `total` | `number` | `100` | The denominator. Segments summing under `total` leave the remainder as track background; segments must not exceed it. |
640
+ | `height` | `number` | `8` | Track height in px. |
641
+ | `label` | `string` | `''` | Accessible name for the whole track (`aria-label` on the `role="img"` element). Omitted from the DOM when empty. |
642
+ | `showLegend` | `boolean` | `false` | Render an auto-generated color↔name legend below the track, for every _named_ segment. No-op if no segment has a `name`. |
643
+
644
+ ### Slots
645
+
646
+ | Slot | Description |
647
+ | -------- | ------------------------------------------------------------------------------------------------------------ |
648
+ | `legend` | Replaces the auto-generated legend with custom content; implies a legend is shown even without `showLegend`. |
649
+
650
+ ### Accessibility
651
+
652
+ - The track has `role="img"` and takes its accessible name entirely from the `label`
653
+ prop — always pass a summary (e.g. `"55% verified, 25% risky, 20% invalid"`), since
654
+ there is no other text alternative.
655
+ - The auto legend is `aria-hidden` (it duplicates the `label` summary visually, not
656
+ semantically); a custom `#legend` slot is not forced hidden, since the consumer owns its
657
+ semantics.
658
+
659
+ ### When to use
660
+
661
+ - A known, static percentage/quota breakdown by category (e.g. email verification quality).
662
+ - For an indeterminate loading state use `DsSpinner` instead; for a linear multi-step flow
663
+ use `DsWizardStepper`.
664
+
665
+ ---
666
+
667
+ <!-- DsTable · Data -->
668
+
669
+ ## DsTable
670
+
671
+ Data-dense table with two composition modes: **declarative** (`columns` + `rows`, with
672
+ per-cell slot overrides) and **compound** (opt-in shell where the consumer composes
673
+ `DsRow`/`DsCell` in the default slot). Supports column pinning, a fixed row height,
674
+ loading/empty states, and an optional "most likely" row highlight. **Presentational
675
+ only**: sorting, pagination, and selection state all live in the consuming app.
676
+
677
+ ### Props
678
+
679
+ | Prop | Type | Default | Description |
680
+ | ---------------- | ---------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
681
+ | `columns` | `DsTableColumn[]` (`{ key, label, align?, width?, pinned? }`) | `[]` | Declarative API: column definitions. |
682
+ | `rows` | `DsTableRow[]` (`{ id, [key]: unknown }`) | `[]` | Declarative API: row data, keyed by `id`. |
683
+ | `emptyMessage` | `string` | `''` | Text shown in the empty state (declarative API); overridable via the `empty` slot. |
684
+ | `highlightRowId` | `string \| number \| null` | `null` | Row `id` to render with the "most likely" accent surface. |
685
+ | `rowHeight` | `string \| number \| null` | `null` | Fixed row height (number → px). Pins row height and truncates overflowing cell text with an ellipsis. |
686
+ | `compound` | `boolean` | `false` | Opt into the compound shell: headers + sticky `thead` from `headers`, body from the default slot (`DsRow`/`DsCell`) instead of `rows`. |
687
+ | `headers` | `(string \| DsTableHeader)[]` (`DsTableHeader = { label, pinned?, width? }`) | `[]` | Compound API: header labels, or the object form to pin/size a header column. |
688
+ | `selectable` | `boolean` | `false` | Compound API: render a leading select-all checkbox column. |
689
+ | `allSelected` | `boolean` | `false` | Compound API: checked state of the select-all checkbox. |
690
+ | `someSelected` | `boolean` | `false` | Compound API: indeterminate state of the select-all checkbox (when not `allSelected`). |
691
+ | `selectAllLabel` | `string` | `''` | `aria-label` for the select-all checkbox (no visible text in its narrow column). |
692
+ | `height` | `string \| number \| null` | `null` | Compound API: fixed wrapper height, enabling the sticky-header scroll. |
693
+ | `loading` | `boolean` | `false` | Shows a single centered spinner row spanning every column, instead of rows/empty (wins over both). |
694
+ | `loadingLabel` | `string` | `''` | Accessible name for the loading spinner; overridable via the `loading` slot. |
695
+
696
+ ### Slots
697
+
698
+ | Slot | Description |
699
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------ |
700
+ | `cell-<column.key>` | Declarative API: custom cell content for a given column; receives `{ row }`. Default content is `row[column.key]`. |
701
+ | `empty` | Declarative API: custom empty-state content, overriding `emptyMessage`. |
702
+ | `loading` | Both APIs: custom loading content, overriding the default `DsSpinner`. |
703
+ | default | Compound API: the table body — compose `DsRow`/`DsCell` here. |
704
+
705
+ ### Events
706
+
707
+ | Event | Payload | When |
708
+ | ------------ | --------- | -------------------------------------------------- |
709
+ | `select-all` | `boolean` | Compound API: the select-all checkbox was toggled. |
710
+
711
+ ### Accessibility
712
+
713
+ - `aria-busy="true"` is set on the table root while `loading`.
714
+ - The select-all checkbox (compound API) takes its accessible name from `selectAllLabel`,
715
+ since its column has no visible text.
716
+ - Pinned columns keep an opaque background and a directional shadow so sticky content
717
+ stays legible over what scrolls beneath it.
718
+
719
+ ### When to use
720
+
721
+ - Any dense, tabular dataset — the declarative API for simple column/row data, the
722
+ compound API when rows need custom composition (checkboxes, rich cells) beyond a single
723
+ per-column slot.
724
+ - For a single deterministic quota/percentage breakdown instead of tabular rows, use
725
+ `DsSegmentedProgressBar`.
726
+
727
+ ---
728
+
729
+ <!-- DsVerificationIcon · Data -->
730
+
731
+ ## DsVerificationIcon
732
+
733
+ Status glyph for a contact data point (email/phone): verified, unverified, or newly
734
+ found. **Presentational only**: no events, its state is derived entirely from the
735
+ `verified` prop.
736
+
737
+ ### Props
738
+
739
+ | Prop | Type | Default | Description |
740
+ | --------------- | ----------------- | ------- | ---------------------------------------------------------------------------------------------------- |
741
+ | `verified` | `boolean \| null` | `null` | `true` = verified, `false` = not verified, `null`/undefined = newly found. |
742
+ | `isContactData` | `boolean` | `false` | Emails (`true`) use the coral "dangerous" glyph when unverified; phones use orange. |
743
+ | `size` | `number` | `20` | Glyph size in px (`font-size`). |
744
+ | `label` | `string` | `''` | Override for the accessible label / tooltip (the app owns i18n). Empty → English default from state. |
745
+
746
+ ### Accessibility
747
+
748
+ - Renders as `role="img"` with `aria-label` (and mirrored `title`) set from `label` or
749
+ the derived state label ("Verified", "Not verified", "Newly found").
750
+ - Color alone never carries the meaning — the underlying glyph (`verified`, `dangerous`/`error`,
751
+ `new_releases`) also changes per state.
752
+
753
+ ### When to use
754
+
755
+ - Always paired with a contact value — `DsContactValue` renders it automatically for
756
+ each email/phone row; use it standalone only when building a custom data row.
757
+
758
+ ---
759
+
760
+ <!-- DsAlert · Feedback -->
761
+
762
+ ## DsAlert
763
+
764
+ Inline banner for status/confirmation messages (success, info, warning, error). It
765
+ renders in place — not fixed to the viewport — and can be dismissed with an optional ×
766
+ button. **Presentational only**: it tracks its own open/closed state internally, but a
767
+ consumer can opt into controlling it via `v-model:visible`.
768
+
769
+ ### Props
770
+
771
+ | Prop | Type | Default | Description |
772
+ | ------------- | --------------------------------------------- | --------- | ----------------------------------------------------------------- |
773
+ | `type` | `'success' \| 'info' \| 'warning' \| 'error'` | `'info'` | Semantic style + icon. |
774
+ | `effect` | `'light' \| 'dark'` | `'light'` | `light` = tinted bg + mid border; `dark` = solid bg + white text. |
775
+ | `title` | `string` | `''` | Bold heading line (or use the `#title` slot for rich content). |
776
+ | `description` | `string` | `''` | Body copy (or use the default slot for rich content). |
777
+ | `showIcon` | `boolean` | `false` | Show the leading type icon. |
778
+ | `closable` | `boolean` | `false` | Show the × dismiss button. |
779
+ | `center` | `boolean` | `false` | Center-align the alert's content. |
780
+ | `boldTitle` | `boolean` | `false` | Force the title to bold weight even without a body. |
781
+ | `closeLabel` | `string` | `''` | `aria-label` for the × button (icon-only control). |
782
+ | `visible` | `boolean` | `true` | `v-model:visible` — opt-in controlled visibility from the parent. |
783
+
784
+ ### Events
785
+
786
+ | Event | Payload | When |
787
+ | ---------------- | --------- | --------------------------------------------------------------------------- |
788
+ | `close` | — | The × button was clicked (self-dismiss). |
789
+ | `update:visible` | `boolean` | Companion event of `v-model:visible` — fires `false` when the alert closes. |
790
+
791
+ ### Accessibility
792
+
793
+ - The root carries `role="alert"` so assistive tech announces it.
794
+ - The × button is icon-only, so `closeLabel` maps to its `aria-label` — always pass it
795
+ when `closable` is set.
796
+ - The mount/dismiss fade transition respects `prefers-reduced-motion` (no transition
797
+ when reduced motion is requested).
798
+
799
+ ### When to use
800
+
801
+ - Inline, persistent status or confirmation messages that live alongside content. For
802
+ transient, viewport-fixed toasts use `DsNotification` instead.
803
+ - Pass `closable` + `closeLabel` whenever the user should be able to dismiss it
804
+ themselves; use `v-model:visible` when the parent also needs to reopen it later.
805
+
806
+ ---
807
+
808
+ <!-- DsModal · Feedback -->
809
+
810
+ ## DsModal
811
+
812
+ Centered dialog with an optional header (title + close button) and footer action row.
813
+ Portal-based by default (`fixed`), with a focus trap, scroll lock, and Escape-to-close;
814
+ opting out of `fixed` renders it in-flow for specimen/embedded use. **Presentational
815
+ only**: it never closes itself — the consumer holds the `open` boolean and flips it off
816
+ on `close`.
817
+
818
+ ### Props
819
+
820
+ | Prop | Type | Default | Description |
821
+ | ----------------- | --------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
822
+ | `open` | `boolean` | `false` | Controls dialog visibility. |
823
+ | `title` | `string` | `''` | Optional heading text; shows the header row (with `showClose`). |
824
+ | `showClose` | `boolean` | `true` | Show the icon-only × close button in the header. |
825
+ | `width` | `string` | `'440px'` | Dialog width (any CSS length). |
826
+ | `height` | `string` | `''` | Explicit dialog height (any CSS length). Empty = content height. |
827
+ | `top` | `string` | `'15vh'` | Top offset of the dialog inside the fixed backdrop (portal mode). |
828
+ | `center` | `boolean` | `false` | Center-align the body text. |
829
+ | `fixed` | `boolean` | `true` | `true` portals to `body` with a backdrop, focus trap, scroll lock and Escape-to-close. `false` renders in-flow with none of that, for specimen/embedded use. |
830
+ | `closeOnBackdrop` | `boolean` | `true` | Clicking the backdrop emits `close` (portal mode only). |
831
+ | `closeOnEscape` | `boolean` | `true` | Pressing Escape emits `close` (portal mode only). |
832
+ | `closeLabel` | `string` | `'Close'` | `aria-label` for the icon-only close button. |
833
+
834
+ ### Slots
835
+
836
+ | Slot | Description |
837
+ | -------- | -------------------------------------------------------------------------------------- |
838
+ | default | Dialog body content. |
839
+ | `footer` | Optional action row (buttons); the footer block is omitted entirely when not provided. |
840
+
841
+ ### Events
842
+
843
+ | Event | Payload | When |
844
+ | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
845
+ | `close` | — | The × button was clicked, the backdrop was clicked (`closeOnBackdrop`), or Escape was pressed (`closeOnEscape`, portal mode only). |
846
+
847
+ > The dialog never closes itself — the consumer must set `open` to `false` on `close`.
848
+
849
+ ### Accessibility
850
+
851
+ - `role="dialog"` with `aria-modal="true"` in portal mode (`fixed`), `"false"` when in-flow.
852
+ - `aria-labelledby` points at the title when set; falls back to `aria-label="dialog"` when
853
+ there is no title.
854
+ - Focus trap (portal mode): Tab/Shift+Tab cycles within the dialog; focus moves to the
855
+ first focusable element on open and restores to the previously focused element on close.
856
+ - Escape closes the dialog when `closeOnEscape` is true (portal mode only).
857
+ - Body scroll locks (`overflow: hidden`) while a fixed/open modal is mounted.
858
+
859
+ ### When to use
860
+
861
+ - Blocking decisions or confirmations (delete, discard changes) — pair with the `footer`
862
+ slot for the action buttons.
863
+ - Use `fixed="false"` only for a static, in-flow specimen; real usage should keep the
864
+ portal, focus trap, and scroll lock.
865
+ - For transient, non-blocking feedback use `DsNotification` instead.
866
+
867
+ ---
868
+
869
+ <!-- DsNotification · Feedback -->
870
+
871
+ ## DsNotification
872
+
873
+ Fixed-position toast card for transient feedback (saved, updated, failed…). It renders
874
+ itself into one of the four viewport corners, optionally auto-dismisses, and can carry a
875
+ single action link. **Presentational only**: it owns its own visibility timer, but all
876
+ routing/consequences stay with the consuming app via events.
877
+
878
+ ### Props
879
+
880
+ | Prop | Type | Default | Description |
881
+ | ------------- | -------------------------------------------------------------- | --------------- | ----------------------------------------------------------------------------- |
882
+ | `type` | `'success' \| 'info' \| 'warning' \| 'error'` | `'info'` | Semantic style + icon. |
883
+ | `position` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | `'bottom-left'` | Viewport corner the toast fixes to. |
884
+ | `title` | `string` | — | Bold heading line. |
885
+ | `description` | `string` | — | Body copy under the title. |
886
+ | `link` | `string` | — | `href` for the optional action link. |
887
+ | `linkTitle` | `string` | — | Visible text for the link (required for the link to render). |
888
+ | `linkTarget` | `'_blank' \| '_self' \| '_parent' \| '_top'` | `'_blank'` | `<a target>`. `_blank` adds `rel="noopener noreferrer"`. |
889
+ | `showClose` | `boolean` | `true` | Show the × dismiss button. |
890
+ | `closeLabel` | `string` | `'Close'` | `aria-label` for the × button (icon-only control). |
891
+ | `duration` | `number` | `0` | Auto-dismiss delay in ms. `0` = stays until dismissed. Timer pauses on hover. |
892
+
893
+ ### Events
894
+
895
+ | Event | Payload | When |
896
+ | ---------- | ------------ | ---------------------------------------------------------------------------------------- |
897
+ | `close` | — | The toast dismissed itself (× click or auto-dismiss timer). |
898
+ | `navigate` | `MouseEvent` | The action link was clicked. `preventDefault()` it to route in-app with your own router. |
899
+
900
+ > The link is a real `<a href>` (a11y + no-JS fallback). Default behaviour opens it natively;
901
+ > listen to `navigate` and call `event.preventDefault()` to intercept and route inside the SPA.
902
+
903
+ ### Accessibility
904
+
905
+ - The × button is icon-only, so `closeLabel` maps to its `aria-label` — always pass it.
906
+ - Auto-dismiss (`duration > 0`) pauses while the pointer hovers the toast, so users get time to read.
907
+
908
+ ### When to use
909
+
910
+ - Transient, non-blocking confirmations or errors. For blocking decisions use `DsModal`;
911
+ for inline persistent messages use `DsAlert`.
912
+
913
+ ---
914
+
915
+ <!-- DsTooltip · Feedback -->
916
+
917
+ ## DsTooltip
918
+
919
+ Dark bubble that describes a wrapped trigger on hover or keyboard focus, with 4-way
920
+ placement. **Presentational only**: pure-CSS positioning, no floating-ui dependency, no
921
+ events.
922
+
923
+ ### Props
924
+
925
+ | Prop | Type | Default | Description |
926
+ | ----------- | ---------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
927
+ | `content` | `string` | `''` | Bubble text. Use the `content` slot instead for rich content. |
928
+ | `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` | Side of the trigger the bubble anchors to. |
929
+ | `delay` | `number` | `300` | Hover delay before showing, in ms. Keyboard focus always shows immediately. |
930
+ | `block` | `boolean` | `false` | Wrap a full-width/block trigger (e.g. `DsInput`) instead of the default `inline-flex`, so it doesn't get squashed. |
931
+ | `disabled` | `boolean` | `false` | Force the tooltip off regardless of `content`/slot — lets a consumer show it only under a condition (e.g. only when truncated). |
932
+
933
+ ### Slots
934
+
935
+ | Slot | Description |
936
+ | --------- | --------------------------------------------------- |
937
+ | default | The trigger element(s) the tooltip wraps. |
938
+ | `content` | Rich bubble content, overriding the `content` prop. |
939
+
940
+ ### Accessibility
941
+
942
+ - The bubble has `role="tooltip"`; the focused/hovered trigger gets `aria-describedby`
943
+ pointing at it (any pre-existing `aria-describedby` value is preserved and restored on hide).
944
+ - Hover and keyboard focus are tracked independently — the tooltip stays open as long as
945
+ either is active, so moving the mouse away while still focused (or vice versa) doesn't hide it.
946
+ - Escape hides an open tooltip.
947
+ - Respects `prefers-reduced-motion` (the fade transition is skipped).
948
+
949
+ ### When to use
950
+
951
+ - Short, supplementary hints on icon-only controls or truncated text — icon-only triggers
952
+ still need their own `aria-label`, since the tooltip is not a substitute for an accessible name.
953
+ - For a persistent inline message use `DsAlert`; for a blocking one use `DsModal`.
954
+
955
+ ---
956
+
957
+ <!-- DsCheckbox · Forms -->
958
+
959
+ ## DsCheckbox
960
+
961
+ Checked / unchecked / indeterminate selection control. **Presentational only**.
962
+ Binds via `v-model` (its `modelValue` is the checked boolean).
963
+
964
+ ### Props
965
+
966
+ | Prop | Type | Default | Description |
967
+ | --------------- | --------- | ------- | -------------------------------------------------------------------------------------------- |
968
+ | `modelValue` | `boolean` | `false` | v-model — checked state. |
969
+ | `indeterminate` | `boolean` | `false` | Visual "mixed" state (dash icon); kept in sync with the native DOM `indeterminate` property. |
970
+ | `disabled` | `boolean` | `false` | Disables the control and dims the whole label to 50% opacity. |
971
+ | `name` | `string` | `''` | Native `name` attribute. |
972
+ | `label` | `string` | `''` | Visible label text; overridden by the default slot when present. |
973
+ | `ariaLabel` | `string` | `''` | Accessible name when there is no visible label (e.g. a "select all" checkbox in a table). |
974
+
975
+ ### Events
976
+
977
+ | Event | Payload | When |
978
+ | ------------------- | --------- | --------------------------------------------------------- |
979
+ | `update:modelValue` | `boolean` | The checkbox was toggled (click, or Space while focused). |
980
+
981
+ ### Accessibility
982
+
983
+ - The native `<input type="checkbox">` is visually hidden (clip-rect pattern) but drives state, keyboard activation (Space), and focus — the DS box and check/dash mark are purely decorative (`aria-hidden`).
984
+ - A focus ring (`:focus-visible`) renders on the decorative box when the native input is focused.
985
+ - `indeterminate` is set as a DOM **property** (`el.indeterminate`), matching how assistive tech and CSS `:indeterminate` expect it — it is not exposed as an HTML attribute.
986
+ - Pass `ariaLabel` (or wrap in your own `<label>`) whenever there is no visible `label`/slot text.
987
+
988
+ ### When to use
989
+
990
+ - Independent on/off choices, or multi-select in a list/table (e.g. bulk selection with an indeterminate "select all").
991
+ - For mutually exclusive single-choice options use `DsRadio`; for a single setting that takes effect immediately (not a form field to submit) consider `DsSwitch`.
992
+
993
+ ---
994
+
995
+ <!-- DsInput · Forms -->
996
+
997
+ ## DsInput
998
+
999
+ Single-line text field — the DS "Input". **Presentational only**: it drives its own
1000
+ password-reveal state and field-exit detection, but no store/HTTP access. Binds
1001
+ via `v-model` (its `modelValue` is the current string value).
1002
+
1003
+ ### Props
1004
+
1005
+ | Prop | Type | Default | Description |
1006
+ | --------------- | --------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
1007
+ | `modelValue` | `string` | `''` | v-model — current text value. |
1008
+ | `type` | `'text' \| 'email' \| 'password' \| 'tel' \| 'number' \| 'search' \| 'url'` | `'text'` | Native input type. `password` renders a reveal toggle when `toggleLabel` is set; `search` always renders as a styled text input with a left search icon. |
1009
+ | `size` | `'sm' \| 'md'` | `'sm'` | Control height (36px / 48px). |
1010
+ | `placeholder` | `string` | `''` | Native placeholder text. |
1011
+ | `disabled` | `boolean` | `false` | Disables the control. |
1012
+ | `error` | `boolean` | `false` | Error visual state (`aria-invalid`, coral border). |
1013
+ | `errorMessage` | `string` | `''` | Message shown under the field when `error` is true. |
1014
+ | `showErrorIcon` | `boolean` | `false` | Shows a warning icon in the suffix when `error` is true. |
1015
+ | `name` | `string` | `''` | Native `name` attribute. |
1016
+ | `id` | `string` | `''` | Native `id`; also links the error message via `aria-describedby`. |
1017
+ | `autocomplete` | `string` | `''` | Native `autocomplete` attribute. |
1018
+ | `toggleLabel` | `string` | `''` | Accessible name for the password-reveal toggle button (`type="password"` only). No label → no toggle. |
1019
+ | `min` | `number \| string` | `undefined` | Native `min` (relevant for `type="number"`). |
1020
+ | `max` | `number \| string` | `undefined` | Native `max` (relevant for `type="number"`). |
1021
+ | `step` | `number \| string` | `undefined` | Native `step` (relevant for `type="number"`). |
1022
+
1023
+ ### Events
1024
+
1025
+ | Event | Payload | When |
1026
+ | ------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------- |
1027
+ | `update:modelValue` | `string` | Emitted continuously as the user types (v-model). |
1028
+ | `focus` | `FocusEvent` | The native input gained focus. |
1029
+ | `blur` | `FocusEvent` | The native input lost focus — fires even when focus only moved to the component's own password toggle. |
1030
+ | `change` | `Event` | Native `change` — fires on blur/Enter once the value actually changed (distinct from the continuous `update:modelValue`). |
1031
+ | `field-blur` | `FocusEvent` | Focus left the **whole** component (not just moved to the internal toggle) — the "real field exit" signal. |
1032
+
1033
+ ### Accessibility
1034
+
1035
+ - `aria-invalid` is set when `error` is true; `aria-describedby` links to the error message (`{id}-error`) when both `id` and `errorMessage` are present.
1036
+ - The password-reveal button is icon-only, so it only renders once `toggleLabel` is supplied (its `aria-label`), and exposes `aria-pressed` for its toggled state.
1037
+ - Text goes bold on focus (except `type="search"`, which stays regular) — a DS-specific affordance, not just a border change.
1038
+ - Exposes `focus()` and `focusAtEnd()` via `defineExpose` for imperative control (e.g. focusing a field after a panel opens) from the consuming app.
1039
+
1040
+ ### When to use
1041
+
1042
+ - Any single-line text/email/password/tel/number/url/search field in a form.
1043
+ - Use `DsTextarea` instead for multi-line content.
1044
+
1045
+ ---
1046
+
1047
+ <!-- DsItemSelectorButton · Forms -->
1048
+
1049
+ ## DsItemSelectorButton
1050
+
1051
+ Pill-shaped trigger that shows the currently assigned item (folder, tag, owner…)
1052
+ or an empty-state prompt, and emits `click` to open an external picker.
1053
+ **Presentational only** — it holds no v-model and owns no picker/menu of its own,
1054
+ only the current selection it's handed via `item`.
1055
+
1056
+ ### Props
1057
+
1058
+ | Prop | Type | Default | Description |
1059
+ | ------------ | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1060
+ | `item` | `DsSelectorItem \| null` | `null` | Selected item (`{ name, color? }`); `null`/absent renders the empty state. |
1061
+ | `icon` | `string` | `''` | Material Icons ligature (e.g. `folder`); omitted entirely (no icon rendered) when unset. |
1062
+ | `emptyTitle` | `string` | `''` | Visible label shown when `item` is empty. |
1063
+ | `ariaLabel` | `string` | `''` | Accessible name for the button. Required when the empty state has no `emptyTitle` (otherwise the button would have no name); when `item.name`/`emptyTitle` render, that visible text already names it. |
1064
+ | `disabled` | `boolean` | `false` | Disables the button (opacity 0.6) and blocks the click handler. |
1065
+
1066
+ ### Events
1067
+
1068
+ | Event | Payload | When |
1069
+ | ------- | ------------ | ---------------------------------------------------------------------------------------- |
1070
+ | `click` | `MouseEvent` | The button was clicked (to open an external item picker) — not emitted while `disabled`. |
1071
+
1072
+ ### Accessibility
1073
+
1074
+ - `aria-haspopup="listbox"` signals that activating the button opens a picker.
1075
+ - The label truncates with ellipsis at ~200px so long names stay legible; the full name is still the button's accessible name via the visible text node.
1076
+ - Pass `ariaLabel` whenever the empty state has no `emptyTitle` — otherwise the button has no accessible name.
1077
+ - Native `<button>` gets Enter/Space activation and a focus ring (`:focus-visible`) for free.
1078
+
1079
+ ### When to use
1080
+
1081
+ - A compact trigger showing "what's currently assigned" (folder, tag, owner) that opens an external picker/menu on click.
1082
+ - The picker/menu itself is not part of this component — pair it with `DsSelect`, a custom dropdown, or a modal owned by the consuming app.
1083
+
1084
+ ---
1085
+
1086
+ <!-- DsRadio · Forms -->
1087
+
1088
+ ## DsRadio
1089
+
1090
+ A single radio control (accent-ring dot when checked). **Presentational only**.
1091
+ Binds via `v-model` (its `modelValue` is the checked boolean of **this** radio —
1092
+ not a shared group value).
1093
+
1094
+ ### Props
1095
+
1096
+ | Prop | Type | Default | Description |
1097
+ | ------------ | --------- | ------- | -------------------------------------------------------------------------------------------- |
1098
+ | `modelValue` | `boolean` | `false` | v-model — checked state of this radio. |
1099
+ | `disabled` | `boolean` | `false` | Disables the control (opacity 0.6). |
1100
+ | `label` | `string` | `''` | Visible label text; overridden by the default slot when present. |
1101
+ | `name` | `string` | `''` | Shared across a set of `DsRadio` to get native radio-group keyboard navigation (arrow keys). |
1102
+ | `value` | `string` | `''` | Identifies this radio within its native `name` group. |
1103
+
1104
+ ### Events
1105
+
1106
+ | Event | Payload | When |
1107
+ | ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1108
+ | `update:modelValue` | `boolean` | Always `true` — native radios never emit `false` on themselves; the app drives exclusivity by setting every other radio in the group's `model-value` to `false`. |
1109
+
1110
+ ### Accessibility
1111
+
1112
+ - The native `<input type="radio">` is visually hidden but drives keyboard/focus/state; the DS dot is decorative (`aria-hidden`).
1113
+ - Sharing `name` across instances enables native arrow-key navigation between radios in the group — the app still owns which one reads as "selected" via each instance's `model-value`.
1114
+ - A focus ring (`:focus-visible`) renders on the decorative dot.
1115
+ - Pass `label`/slot text (or wrap in your own `<label>`) so the control has an accessible name.
1116
+
1117
+ ### When to use
1118
+
1119
+ - A small, fixed set of mutually exclusive options where every choice should stay visible (contact type, plan tier).
1120
+ - For a longer list, or when only one option needs to be visible at a time, prefer `DsSelect`.
1121
+
1122
+ ---
1123
+
1124
+ <!-- DsSelect · Forms -->
1125
+
1126
+ ## DsSelect
1127
+
1128
+ Dropdown select — a native-`<select>`-like single/multi picker with a real
1129
+ interaction model (button trigger, `listbox`/`option` ARIA roles, full keyboard
1130
+ support) per the WAI-ARIA APG "Collapsible Dropdown Listbox" pattern.
1131
+ **Presentational only**: it owns its own open/closed and keyboard-navigation
1132
+ state, but no store/HTTP access — remote filtering and async loading are driven
1133
+ entirely by props the app passes in. Binds via `v-model`: scalar when `multiple`
1134
+ is false, an array of values when `multiple` is true.
1135
+
1136
+ ### Props
1137
+
1138
+ | Prop | Type | Default | Description |
1139
+ | ------------------- | ------------------------------------------------------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
1140
+ | `modelValue` | `string \| number \| (string \| number)[] \| null` | `null` | v-model. Scalar when `multiple` is false, array of selected values when `multiple` is true. |
1141
+ | `options` | `DsSelectOption[]` (required) | — | The option list. Each option: `{ value, label, subLabel?, icon?, color?, group? }`. |
1142
+ | `placeholder` | `string` | `'Select an option'` | Text shown when nothing is selected (and the label prefix for `filterSummary` mode). |
1143
+ | `multiple` | `boolean` | `false` | Multi-select — the modelValue becomes an array and the dropdown stays open between picks. |
1144
+ | `selectType` | `'default' \| 'filterSummary'` | `'default'` | Visual style: grey pill, or the accent "filter chip" that flips color once something is selected. |
1145
+ | `showCheckbox` | `boolean` | `false` | Renders a (decorative, disabled) `DsCheckbox` next to each option; in single-select this also lets clicking the selected option deselect it. |
1146
+ | `width` | `'auto' \| 'small' \| 'medium' \| 'large' \| 'full' \| number \| string` | `'auto'` | A named width step (fixed/stable), `'full'` (100% of the container), a custom number (px), or any CSS width string. `'auto'` shrinks to the label content. |
1147
+ | `disabled` | `boolean` | `false` | Disables the trigger and closes the dropdown if it was open. |
1148
+ | `filterable` | `boolean` | `false` | Renders a type-to-filter search input inside the open dropdown. |
1149
+ | `remote` | `boolean` | `false` | Skips the built-in local filter (no effect unless `filterable` is also set) — pair with the `search` event to filter server-side without double-filtering. |
1150
+ | `loading` | `boolean` | `false` | Shows a spinner (`#loading` slot to override) instead of options/empty state, with `aria-busy` on the listbox. |
1151
+ | `searchLabel` | `string` | `'Filter options'` | Accessible name (`aria-label`) for the filter search input. |
1152
+ | `searchPlaceholder` | `string` | `'Search'` | Visible placeholder for the filter search input. |
1153
+ | `clearLabel` | `string` | `'Clear selection'` | Accessible name for the `filterSummary` clear (×) button. |
1154
+ | `emptyText` | `string` | `'No options'` | Shown when the (possibly filtered) option list is empty; overridable via the `#empty` slot. |
1155
+
1156
+ ### Events
1157
+
1158
+ | Event | Payload | When |
1159
+ | ------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
1160
+ | `update:modelValue` | Current selection (scalar or array, per `multiple`) | An option was picked/toggled, or the clear (×) button ran. |
1161
+ | `search` | `string` (the typed filter text) | The filter input changed while `filterable`, or the dropdown closed with a non-empty query (emits `''` so a `remote` consumer can reset). |
1162
+
1163
+ ### Slots
1164
+
1165
+ | Slot | Scope | Description |
1166
+ | --------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |
1167
+ | `option` | `{ option, selected, active }` | Custom per-option content (avatar, arbitrary markup); the default renders icon + label + subLabel. |
1168
+ | `loading` | — | Overrides the default `DsSpinner` shown while `loading` is true. |
1169
+ | `empty` | — | Overrides the default `emptyText` message. |
1170
+
1171
+ ### Accessibility
1172
+
1173
+ - Implements the WAI-ARIA APG "Collapsible Dropdown Listbox" (select-only combobox) pattern: `aria-haspopup="listbox"`, `aria-expanded`, `aria-controls`, `role="listbox"`/`role="option"`, `aria-selected`, `aria-activedescendant`.
1174
+ - Full keyboard support on the trigger: `ArrowUp`/`ArrowDown` open/navigate, `Home`/`End` jump, `Enter`/`Space` select, `Escape` closes (and stops propagation, so an ancestor `DsModal` doesn't also close), `Tab` collapses without stealing focus.
1175
+ - Grouped options (`option.group`) render as real `role="group"` regions labelled by their header; headers are never focusable/selectable.
1176
+ - When `filterable`, the search input carries `role="combobox"` and owns `aria-activedescendant` while open (focus moves there instead of staying on the trigger).
1177
+ - The `filterSummary` clear button is icon-only, so `clearLabel` maps to its `aria-label`.
1178
+
1179
+ ### When to use
1180
+
1181
+ - A single- or multi-value picker from a short-to-medium list of options (with optional grouping, icons, or search).
1182
+ - For a small, fixed set of mutually exclusive choices that should all stay visible at once, prefer `DsRadio`.
1183
+
1184
+ ---
1185
+
1186
+ <!-- DsSwitch · Forms -->
1187
+
1188
+ ## DsSwitch
1189
+
1190
+ On/off toggle (accent track when on). **Presentational only**: it reflects the
1191
+ `modelValue`/`loading` it's given and owns no async logic itself. Binds via
1192
+ `v-model` (its `modelValue` is the on/off boolean).
1193
+
1194
+ ### Props
1195
+
1196
+ | Prop | Type | Default | Description |
1197
+ | ------------ | --------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1198
+ | `modelValue` | `boolean` | `false` | v-model — on/off state. |
1199
+ | `disabled` | `boolean` | `false` | Disables interaction (opacity 0.6). |
1200
+ | `loading` | `boolean` | `false` | Async "busy/pending" state: blocks interaction and shows a spinner in the thumb while a value change is pending (e.g. a server round-trip that may revert). Sets `aria-busy`. The app owns the optimistic update and any revert — this prop is purely reflective. |
1201
+ | `ariaLabel` | `string` | `undefined` | Accessible name — a switch has no visible text. Pass this, or an `aria-label` attribute directly (both land on the root `<button>`). |
1202
+
1203
+ ### Events
1204
+
1205
+ | Event | Payload | When |
1206
+ | ------------------- | --------- | ----------------------------------------------------------------------------------------------------- |
1207
+ | `update:modelValue` | `boolean` | The switch was toggled (click, or Enter/Space while focused) — blocked while `disabled` or `loading`. |
1208
+
1209
+ ### Accessibility
1210
+
1211
+ - Native `<button role="switch">` with `aria-checked` reflecting state — gets Enter/Space activation for free.
1212
+ - `aria-busy` is set while `loading`; `aria-disabled` is set while `disabled` **or** `loading`.
1213
+ - A focus ring (`:focus-visible`) renders on the track.
1214
+ - Always pass `ariaLabel` (or an `aria-label` attribute) since the control renders no visible text of its own.
1215
+
1216
+ ### When to use
1217
+
1218
+ - An immediate on/off setting that takes effect right away (notification toggle, feature flag) — pair `loading` with an app-owned optimistic update when the effect is async.
1219
+ - For a value that must be explicitly submitted as part of a form, consider `DsCheckbox` instead.
1220
+
1221
+ ---
1222
+
1223
+ <!-- DsTextarea · Forms -->
1224
+
1225
+ ## DsTextarea
1226
+
1227
+ Multi-line text field — the DS "Textarea", sibling of `DsInput` with the same
1228
+ border treatment. **Presentational only**. Binds via `v-model` (its `modelValue`
1229
+ is the current string value).
1230
+
1231
+ ### Props
1232
+
1233
+ | Prop | Type | Default | Description |
1234
+ | -------------- | ------------------------------------------------ | ------------ | ----------------------------------------------------------------- |
1235
+ | `modelValue` | `string` | `''` | v-model — current text value. |
1236
+ | `placeholder` | `string` | `''` | Native placeholder text. |
1237
+ | `rows` | `number` | `4` | Native `rows` attribute (visible line count). |
1238
+ | `disabled` | `boolean` | `false` | Disables the control. |
1239
+ | `error` | `boolean` | `false` | Error visual state (`aria-invalid`, coral border). |
1240
+ | `errorMessage` | `string` | `''` | Message shown under the field when `error` is true. |
1241
+ | `name` | `string` | `''` | Native `name` attribute. |
1242
+ | `id` | `string` | `''` | Native `id`; also links the error message via `aria-describedby`. |
1243
+ | `resize` | `'none' \| 'vertical' \| 'horizontal' \| 'both'` | `'vertical'` | CSS `resize` behavior of the control. |
1244
+
1245
+ ### Events
1246
+
1247
+ | Event | Payload | When |
1248
+ | ------------------- | ------------ | ------------------------------------------------- |
1249
+ | `update:modelValue` | `string` | Emitted continuously as the user types (v-model). |
1250
+ | `focus` | `FocusEvent` | The textarea gained focus. |
1251
+ | `blur` | `FocusEvent` | The textarea lost focus. |
1252
+
1253
+ ### Accessibility
1254
+
1255
+ - `aria-invalid` is set when `error` is true; `aria-describedby` links the error message (`{id}-error`) when both `id` and `errorMessage` are present.
1256
+ - `class`/`style`/`data-*` fall through to the root wrapper; every other attribute (e.g. `maxlength`, `autocomplete`, `spellcheck`, native listeners) forwards straight to the real `<textarea>` (`inheritAttrs: false` plus a manual split) — so ARIA and native control attributes land where assistive tech expects them.
1257
+ - Border transitions through rest/focus/disabled/error states like `DsInput`, though — unlike `DsInput` — text does **not** go bold on focus.
1258
+
1259
+ ### When to use
1260
+
1261
+ - Multi-line free text: notes, descriptions, messages.
1262
+ - Use `DsInput` for single-line fields.
1263
+
1264
+ ---
1265
+
1266
+ <!-- DsMenuItem · Navigation -->
1267
+
1268
+ ## DsMenuItem
1269
+
1270
+ Sidebar navigation row: an icon with an optional label, usable full-width or collapsed to
1271
+ an icon-only rail. **Presentational only**: it renders as a real `<button>` and emits
1272
+ `select` — the consuming app owns the active-route logic.
1273
+
1274
+ ### Props
1275
+
1276
+ | Prop | Type | Default | Description |
1277
+ | ----------- | --------- | ------------ | --------------------------------------------------------------------------------------------------------- |
1278
+ | `icon` | `string` | — (required) | Material Icons ligature name. |
1279
+ | `text` | `string` | `''` | Row label; a default slot overrides it. Stays in the DOM (visually hidden, not removed) when `collapsed`. |
1280
+ | `active` | `boolean` | `false` | Marks the current row: white surface, tinted border, `aria-current="page"`. |
1281
+ | `collapsed` | `boolean` | `false` | Icon-only collapsed rail. |
1282
+
1283
+ ### Slots
1284
+
1285
+ | Slot | Description |
1286
+ | ------- | ----------------------------------- |
1287
+ | default | Overrides `text` for the row label. |
1288
+
1289
+ ### Events
1290
+
1291
+ | Event | Payload | When |
1292
+ | -------- | ------------ | -------------------- |
1293
+ | `select` | `MouseEvent` | The row was clicked. |
1294
+
1295
+ ### Accessibility
1296
+
1297
+ - Renders a real `<button type="button">` (keyboard-reachable, visible focus ring)
1298
+ instead of the design source's non-interactive `<div onClick>`.
1299
+ - `aria-current="page"` is set on the active row.
1300
+ - The label stays in the DOM when `collapsed` (visually hidden via clip, not
1301
+ `display: none`), so screen readers still announce it; sighted hover users get the
1302
+ native `title` fallback.
1303
+
1304
+ ### When to use
1305
+
1306
+ - Sidebar/rail navigation items, both expanded (icon + label) and collapsed (icon-only)
1307
+ layouts.
1308
+ - For a numbered multi-step flow instead of a persistent nav rail, use `DsWizardStepper`.
1309
+
1310
+ ---
1311
+
1312
+ <!-- DsWizardStepper · Navigation -->
1313
+
1314
+ ## DsWizardStepper
1315
+
1316
+ Numbered multi-step flow: a row of step chips (completed / active / incomplete / neutral)
1317
+ plus a trailing "next"/"final" action button. **Presentational only**: the consumer owns
1318
+ the current step (`value`) and the button copy.
1319
+
1320
+ ### Props
1321
+
1322
+ | Prop | Type | Default | Description |
1323
+ | ---------------- | ---------------- | ------- | --------------------------------------------------------------------------------------------- |
1324
+ | `steps` | `DsWizardStep[]` | `[]` | Step definitions: `{ key?, label, completed?, visited? }`. |
1325
+ | `value` | `number` | `1` | Current step, 1-based. |
1326
+ | `nextLabel` | `string` | `''` | Label for the "advance" button (hidden without one, and never on the last step). |
1327
+ | `finalStepLabel` | `string` | `''` | Label for the final-step action button (hidden without one, and only shown on the last step). |
1328
+ | `clickable` | `boolean` | `true` | Whether the step chips themselves are clickable. The advance/final buttons are always active. |
1329
+
1330
+ ### Events
1331
+
1332
+ | Event | Payload | When |
1333
+ | ------------ | ------------------ | ------------------------------------------------------------------------------------------------ |
1334
+ | `step` | `number` (1-based) | A step chip was clicked (only when `clickable`), or the "advance" button moved to the next step. |
1335
+ | `final-step` | — | The final-step action button was clicked (last step only). |
1336
+
1337
+ ### Accessibility
1338
+
1339
+ - The current step carries `aria-current="step"`.
1340
+ - Step chips render as real `<button>`s when `clickable` (keyboard-reachable, focus ring);
1341
+ as plain `<div>`s when not, since they aren't interactive.
1342
+ - An `incomplete` (visited-but-unfinished) step is marked with a red "!" marker, so it's
1343
+ distinguishable from `completed`/`neutral` beyond color alone.
1344
+
1345
+ ### When to use
1346
+
1347
+ - Linear, numbered flows (e.g. a multi-step form: Compose → Recipients → Send) where the
1348
+ user needs to see progress and jump back to prior steps.
1349
+ - For a non-linear sidebar/rail instead, use `DsMenuItem`; for an unlabeled
1350
+ percentage/quota breakdown use `DsSegmentedProgressBar`.