@chiralkit/design-system 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/chiralkit-ds/chiralkit-ds.css +1 -0
- package/dist/chiralkit-ds/chiralkit-ds.esm.js +1 -0
- package/dist/chiralkit-ds/index.esm.js +1 -0
- package/dist/chiralkit-ds/p-038fa008.entry.js +1 -0
- package/dist/chiralkit-ds/p-2fdc1012.entry.js +1 -0
- package/dist/chiralkit-ds/p-306ec33b.entry.js +1 -0
- package/dist/chiralkit-ds/p-3f704698.entry.js +1 -0
- package/dist/chiralkit-ds/p-41081d22.entry.js +1 -0
- package/dist/chiralkit-ds/p-7d7e5aac.entry.js +1 -0
- package/dist/chiralkit-ds/p-84b801f8.entry.js +1 -0
- package/dist/chiralkit-ds/p-a28c71b4.entry.js +1 -0
- package/dist/chiralkit-ds/p-be84312f.entry.js +1 -0
- package/dist/chiralkit-ds/p-c8015fe6.entry.js +1 -0
- package/dist/chiralkit-ds/p-d36a6bea.entry.js +1 -0
- package/dist/chiralkit-ds/p-e-glNjK6.js +2 -0
- package/dist/chiralkit-ds/p-f0f9ff61.entry.js +1 -0
- package/dist/cjs/chiralkit-ds.cjs.js +24 -0
- package/dist/cjs/ds-badge.cjs.entry.js +18 -0
- package/dist/cjs/ds-button.cjs.entry.js +26 -0
- package/dist/cjs/ds-card.cjs.entry.js +17 -0
- package/dist/cjs/ds-checkbox.cjs.entry.js +67 -0
- package/dist/cjs/ds-code-block.cjs.entry.js +39 -0
- package/dist/cjs/ds-dialog.cjs.entry.js +67 -0
- package/dist/cjs/ds-input.cjs.entry.js +50 -0
- package/dist/cjs/ds-loader.cjs.entry.js +26 -0
- package/dist/cjs/ds-select.cjs.entry.js +85 -0
- package/dist/cjs/ds-tabs.cjs.entry.js +138 -0
- package/dist/cjs/ds-textarea.cjs.entry.js +50 -0
- package/dist/cjs/ds-tooltip.cjs.entry.js +233 -0
- package/dist/cjs/index-mMvyq7U_.js +2018 -0
- package/dist/cjs/index.cjs.js +10 -0
- package/dist/cjs/loader.cjs.js +12 -0
- package/dist/collection/collection-manifest.json +24 -0
- package/dist/collection/components/ds-badge/ds-badge.css +36 -0
- package/dist/collection/components/ds-badge/ds-badge.js +93 -0
- package/dist/collection/components/ds-button/ds-button.css +66 -0
- package/dist/collection/components/ds-button/ds-button.js +197 -0
- package/dist/collection/components/ds-card/ds-card.css +12 -0
- package/dist/collection/components/ds-card/ds-card.js +68 -0
- package/dist/collection/components/ds-checkbox/ds-checkbox.css +22 -0
- package/dist/collection/components/ds-checkbox/ds-checkbox.js +339 -0
- package/dist/collection/components/ds-code-block/ds-code-block.css +65 -0
- package/dist/collection/components/ds-code-block/ds-code-block.js +162 -0
- package/dist/collection/components/ds-dialog/ds-dialog.css +30 -0
- package/dist/collection/components/ds-dialog/ds-dialog.js +313 -0
- package/dist/collection/components/ds-input/ds-input.css +38 -0
- package/dist/collection/components/ds-input/ds-input.js +353 -0
- package/dist/collection/components/ds-loader/ds-loader.css +94 -0
- package/dist/collection/components/ds-loader/ds-loader.js +149 -0
- package/dist/collection/components/ds-select/ds-select.css +69 -0
- package/dist/collection/components/ds-select/ds-select.js +235 -0
- package/dist/collection/components/ds-tabs/ds-tabs.css +59 -0
- package/dist/collection/components/ds-tabs/ds-tabs.js +248 -0
- package/dist/collection/components/ds-textarea/ds-textarea.css +41 -0
- package/dist/collection/components/ds-textarea/ds-textarea.js +342 -0
- package/dist/collection/components/ds-tooltip/ds-tooltip.css +31 -0
- package/dist/collection/components/ds-tooltip/ds-tooltip.js +318 -0
- package/dist/collection/components.js +2 -0
- package/dist/collection/index.js +1 -0
- package/dist/components/ds-badge.d.ts +11 -0
- package/dist/components/ds-badge.js +1 -0
- package/dist/components/ds-button.d.ts +11 -0
- package/dist/components/ds-button.js +1 -0
- package/dist/components/ds-card.d.ts +11 -0
- package/dist/components/ds-card.js +1 -0
- package/dist/components/ds-checkbox.d.ts +11 -0
- package/dist/components/ds-checkbox.js +1 -0
- package/dist/components/ds-code-block.d.ts +11 -0
- package/dist/components/ds-code-block.js +1 -0
- package/dist/components/ds-dialog.d.ts +11 -0
- package/dist/components/ds-dialog.js +1 -0
- package/dist/components/ds-input.d.ts +11 -0
- package/dist/components/ds-input.js +1 -0
- package/dist/components/ds-loader.d.ts +11 -0
- package/dist/components/ds-loader.js +1 -0
- package/dist/components/ds-select.d.ts +11 -0
- package/dist/components/ds-select.js +1 -0
- package/dist/components/ds-tabs.d.ts +11 -0
- package/dist/components/ds-tabs.js +1 -0
- package/dist/components/ds-textarea.d.ts +11 -0
- package/dist/components/ds-textarea.js +1 -0
- package/dist/components/ds-tooltip.d.ts +11 -0
- package/dist/components/ds-tooltip.js +1 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +1 -0
- package/dist/components/p-DDUZow8F.js +1 -0
- package/dist/components/p-ZsYP0VBq.js +1 -0
- package/dist/components/p-k0ywxWpC.js +1 -0
- package/dist/esm/chiralkit-ds.js +20 -0
- package/dist/esm/ds-badge.entry.js +16 -0
- package/dist/esm/ds-button.entry.js +24 -0
- package/dist/esm/ds-card.entry.js +15 -0
- package/dist/esm/ds-checkbox.entry.js +65 -0
- package/dist/esm/ds-code-block.entry.js +37 -0
- package/dist/esm/ds-dialog.entry.js +65 -0
- package/dist/esm/ds-input.entry.js +48 -0
- package/dist/esm/ds-loader.entry.js +24 -0
- package/dist/esm/ds-select.entry.js +83 -0
- package/dist/esm/ds-tabs.entry.js +136 -0
- package/dist/esm/ds-textarea.entry.js +48 -0
- package/dist/esm/ds-tooltip.entry.js +231 -0
- package/dist/esm/index-e-glNjK6.js +2008 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/loader.js +10 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/layouts/app-shell.css +84 -0
- package/dist/tests/a11y.js +55 -0
- package/dist/types/Users/alex/projects/MyGitHub/chiralkit/packages/design-system/.stencil/tests/a11y.d.ts +17 -0
- package/dist/types/components/ds-badge/ds-badge.d.ts +54 -0
- package/dist/types/components/ds-button/ds-button.d.ts +75 -0
- package/dist/types/components/ds-card/ds-card.d.ts +53 -0
- package/dist/types/components/ds-checkbox/ds-checkbox.d.ts +94 -0
- package/dist/types/components/ds-code-block/ds-code-block.d.ts +83 -0
- package/dist/types/components/ds-dialog/ds-dialog.d.ts +148 -0
- package/dist/types/components/ds-input/ds-input.d.ts +104 -0
- package/dist/types/components/ds-loader/ds-loader.d.ts +64 -0
- package/dist/types/components/ds-select/ds-select.d.ts +123 -0
- package/dist/types/components/ds-tabs/ds-tabs.d.ts +155 -0
- package/dist/types/components/ds-textarea/ds-textarea.d.ts +93 -0
- package/dist/types/components/ds-tooltip/ds-tooltip.d.ts +26 -0
- package/dist/types/components.d.ts +3068 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/stencil-public-runtime.d.ts +1861 -0
- package/docs.json +2112 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +78 -0
- package/react/components/ds-badge.tsx +25 -0
- package/react/components/ds-button.tsx +25 -0
- package/react/components/ds-card.tsx +25 -0
- package/react/components/ds-checkbox.tsx +25 -0
- package/react/components/ds-code-block.tsx +25 -0
- package/react/components/ds-dialog.tsx +31 -0
- package/react/components/ds-input.tsx +25 -0
- package/react/components/ds-loader.tsx +25 -0
- package/react/components/ds-select.tsx +25 -0
- package/react/components/ds-tabs.tsx +25 -0
- package/react/components/ds-textarea.tsx +25 -0
- package/react/components/ds-tooltip.tsx +25 -0
- package/react/components.ts +162 -0
|
@@ -0,0 +1,3068 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/* tslint:disable */
|
|
3
|
+
/**
|
|
4
|
+
* This is an autogenerated file created by the Stencil compiler.
|
|
5
|
+
* It contains typing information for all components that exist in this project.
|
|
6
|
+
*/
|
|
7
|
+
import { HTMLStencilElement, JSXBase } from "./stencil-public-runtime";
|
|
8
|
+
import { DsSelectOption } from "./components/ds-select/ds-select";
|
|
9
|
+
import { DsTabItem } from "./components/ds-tabs/ds-tabs";
|
|
10
|
+
export { DsSelectOption } from "./components/ds-select/ds-select";
|
|
11
|
+
export { DsTabItem } from "./components/ds-tabs/ds-tabs";
|
|
12
|
+
export namespace Components {
|
|
13
|
+
/**
|
|
14
|
+
* ## Accessibility
|
|
15
|
+
* **Category:** static-display
|
|
16
|
+
* **APG pattern:** N/A — non-interactive text label.
|
|
17
|
+
* **Keyboard interactions:**
|
|
18
|
+
* - Tab: N/A — the badge is not focusable and does not appear in tab order.
|
|
19
|
+
* - Enter: N/A.
|
|
20
|
+
* - Escape: N/A.
|
|
21
|
+
* - Arrow keys: N/A.
|
|
22
|
+
* - Printable characters: N/A.
|
|
23
|
+
* **ARIA:**
|
|
24
|
+
* - Roles used: none — renders a plain `<span>` around a `<slot />`. The
|
|
25
|
+
* `variant` prop is a purely visual axis and MUST NOT be the sole carrier
|
|
26
|
+
* of information (color/style alone fails WCAG 1.4.1 Use of Color); the
|
|
27
|
+
* textual content inside the slot must convey the same meaning.
|
|
28
|
+
* - State attributes: none.
|
|
29
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers place the
|
|
30
|
+
* readable label directly inside the tag.
|
|
31
|
+
* **Focus:**
|
|
32
|
+
* - On mount: nothing auto-focuses.
|
|
33
|
+
* - On activate/open: N/A — non-interactive.
|
|
34
|
+
* - On close/deactivate: N/A.
|
|
35
|
+
* - Focus indicator: N/A — the host is not focusable. If a consumer wraps
|
|
36
|
+
* `ds-badge` in an interactive element (e.g. `<button>`, `<a>`), that
|
|
37
|
+
* wrapper owns the focus indicator via the DS `--ds-color-focus` token
|
|
38
|
+
* and its own `:focus-visible` rule.
|
|
39
|
+
* **Motion:**
|
|
40
|
+
* - Animations: none.
|
|
41
|
+
* - Reduced-motion behavior: N/A.
|
|
42
|
+
* **Contrast:**
|
|
43
|
+
* - Text: inherits `color: inherit` / `currentColor` from the host page and
|
|
44
|
+
* the variant-specific style; not verified in isolation here. Consumers
|
|
45
|
+
* are responsible for confirming ≥ 4.5:1 (WCAG 1.4.3) against the surface
|
|
46
|
+
* the badge is placed on.
|
|
47
|
+
* - Non-text: no state-conveying icons or borders that need the 3:1
|
|
48
|
+
* non-text contrast threshold.
|
|
49
|
+
* **Screen reader announcement:**
|
|
50
|
+
* - Rendered state: announces the slot text as inline content within its
|
|
51
|
+
* surrounding context (no role means AT reads it as plain text). The
|
|
52
|
+
* `variant` value is not announced — that is intentional; the label text
|
|
53
|
+
* is the source of truth for meaning.
|
|
54
|
+
* - State change: N/A — no dynamic state owned by this component.
|
|
55
|
+
*/
|
|
56
|
+
interface DsBadge {
|
|
57
|
+
/**
|
|
58
|
+
* @default "neutral"
|
|
59
|
+
*/
|
|
60
|
+
"variant": | "neutral"
|
|
61
|
+
| "ui-component"
|
|
62
|
+
| "recipe"
|
|
63
|
+
| "agent"
|
|
64
|
+
| "config";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* ## Accessibility
|
|
68
|
+
* **Category:** interactive-display
|
|
69
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
70
|
+
* (satisfied natively by the underlying `<button>` element). When `href` is
|
|
71
|
+
* set, the component renders `<a href>` instead — a link, not a button — and
|
|
72
|
+
* that surface follows native `<a>` semantics rather than the Button pattern.
|
|
73
|
+
* **Keyboard interactions:**
|
|
74
|
+
* - Tab: moves focus to the internal `<button>` or `<a>` (shadow DOM does not
|
|
75
|
+
* interrupt tab order).
|
|
76
|
+
* - Enter: activates (fires click on `<button>`, navigates on `<a>`).
|
|
77
|
+
* - Space: activates the `<button>` variant only (native behavior); on the
|
|
78
|
+
* `<a>` variant Space scrolls the page as usual.
|
|
79
|
+
* - Escape: N/A — no dismissible state owned by this component.
|
|
80
|
+
* - Arrow keys: N/A.
|
|
81
|
+
* - Printable characters: N/A.
|
|
82
|
+
* **ARIA:**
|
|
83
|
+
* - Roles used: none explicit — the native `<button>` / `<a href>` supplies
|
|
84
|
+
* the implicit `button` / `link` role.
|
|
85
|
+
* - State attributes: `disabled` (native attribute on `<button>`, not
|
|
86
|
+
* `aria-disabled`). The `<a>` variant does not accept `disabled` — a
|
|
87
|
+
* disabled navigation is a spec smell (see spec §"Button vs Link"); if a
|
|
88
|
+
* link needs a disabled state, model it as a `<button>` action instead.
|
|
89
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers MUST place
|
|
90
|
+
* readable text (or a labeled icon) inside the tag. An empty slot leaves
|
|
91
|
+
* the button/link without an accessible name and fails
|
|
92
|
+
* `button-name` / `link-name` axe rules. Icon-only usage is out of contract
|
|
93
|
+
* for `ds-button`; use a future `ds-icon-button` with a required
|
|
94
|
+
* `aria-label` prop when that lands.
|
|
95
|
+
* **Focus:**
|
|
96
|
+
* - On mount: nothing auto-focuses.
|
|
97
|
+
* - On activate: focus stays on the element (native click behavior; the
|
|
98
|
+
* component does not move focus on its own).
|
|
99
|
+
* - On close/deactivate: N/A.
|
|
100
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
101
|
+
* `outline-offset`, applied via `:focus-visible` — replaces the browser
|
|
102
|
+
* default so both `<button>` and `<a>` variants use the same DS ring
|
|
103
|
+
* (spec universal rule 3, WCAG 1.4.11 non-text contrast ≥ 3:1).
|
|
104
|
+
* **Motion:**
|
|
105
|
+
* - Animations: 150ms `background-color` ease-in-out transition on
|
|
106
|
+
* `primary`/`secondary` variants (hover).
|
|
107
|
+
* - Reduced-motion behavior: N/A — 150ms is below the spec's 200ms threshold
|
|
108
|
+
* for `prefers-reduced-motion` handling, and the transition animates color
|
|
109
|
+
* only (no motion of position, scale, or opacity).
|
|
110
|
+
* **Contrast:**
|
|
111
|
+
* - Text: `primary` and `secondary` variants pair `--ds-color-{primary,secondary}`
|
|
112
|
+
* with `--ds-color-text-on-{primary,secondary}` — the token pairs are
|
|
113
|
+
* authored to meet WCAG 2.1 AA (≥ 4.5:1). The `ghost` variant inherits
|
|
114
|
+
* `color: inherit` and pins `opacity: 0.85`, so its effective contrast
|
|
115
|
+
* depends on the consumer's surface — verify per site.
|
|
116
|
+
* - Non-text (border on `ghost`): 1px `currentColor`; contrast tracks the
|
|
117
|
+
* text color 1:1, subject to the same host-page caveat.
|
|
118
|
+
* **Screen reader announcement:**
|
|
119
|
+
* - Rendered state: announces the slot text followed by "button" (or "link"
|
|
120
|
+
* for the `href` variant), plus "dimmed" / "unavailable" when `disabled`
|
|
121
|
+
* is set (browser/AT dependent wording).
|
|
122
|
+
* - State change: N/A — this component does not own toggle state. A pressed
|
|
123
|
+
* toggle needs `aria-pressed`, which is out of scope here; model that as
|
|
124
|
+
* a distinct component or extend the API deliberately per the spec.
|
|
125
|
+
*/
|
|
126
|
+
interface DsButton {
|
|
127
|
+
/**
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
"disabled": boolean;
|
|
131
|
+
"href"?: string;
|
|
132
|
+
/**
|
|
133
|
+
* @default "md"
|
|
134
|
+
*/
|
|
135
|
+
"size": "md" | "lg";
|
|
136
|
+
/**
|
|
137
|
+
* @default "button"
|
|
138
|
+
*/
|
|
139
|
+
"type": "button" | "submit" | "reset";
|
|
140
|
+
/**
|
|
141
|
+
* @default "primary"
|
|
142
|
+
*/
|
|
143
|
+
"variant": "primary" | "secondary" | "ghost";
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* ## Accessibility
|
|
147
|
+
* **Category:** static-display
|
|
148
|
+
* **APG pattern:** N/A — a container surface with no interactive
|
|
149
|
+
* behavior of its own. If a consumer nests interactive elements
|
|
150
|
+
* inside (button, link, form field), those elements satisfy their
|
|
151
|
+
* own a11y bucket independently.
|
|
152
|
+
* **Keyboard interactions:**
|
|
153
|
+
* - Tab / Shift+Tab: N/A — the card itself is not focusable. Any
|
|
154
|
+
* interactive descendant carries its own tab semantics.
|
|
155
|
+
* - Enter / Space / Arrow / Escape / Printable: N/A.
|
|
156
|
+
* **ARIA:**
|
|
157
|
+
* - Roles used: none. `<ds-card>` is a purely visual grouping and
|
|
158
|
+
* deliberately does not carry `role="region"` — that would demand
|
|
159
|
+
* a labelledby target, which would only be correct in a subset of
|
|
160
|
+
* uses. Consumers that need a labeled landmark should render a
|
|
161
|
+
* `<section aria-labelledby>` around the card.
|
|
162
|
+
* - State attributes: none.
|
|
163
|
+
* - Labeling strategy: none applied by the component; consumer's
|
|
164
|
+
* heading structure inside the slot supplies the accessible name
|
|
165
|
+
* when needed.
|
|
166
|
+
* **Focus:**
|
|
167
|
+
* - On mount: nothing auto-focuses.
|
|
168
|
+
* - Focus indicator: N/A — the card itself is not focusable.
|
|
169
|
+
* **Motion:**
|
|
170
|
+
* - Animations: none.
|
|
171
|
+
* - Reduced-motion behavior: N/A.
|
|
172
|
+
* **Contrast:**
|
|
173
|
+
* - Text: inherits `color: inherit` / `currentColor`; the card does
|
|
174
|
+
* not impose a text color, so contrast is the consumer theme's
|
|
175
|
+
* responsibility.
|
|
176
|
+
* - Non-text (border, subtle background tint): both derived from
|
|
177
|
+
* `color-mix(in oklch, currentColor N%, transparent)`, matching
|
|
178
|
+
* the pattern used by `ds-input` and `ds-select`. WCAG 1.4.11
|
|
179
|
+
* (3:1 non-text contrast) is met in themes where the surface has
|
|
180
|
+
* enough delta against the page background; the component does
|
|
181
|
+
* not override the consumer's palette to force it.
|
|
182
|
+
* **Screen reader announcement:**
|
|
183
|
+
* - Rendered state: no announcement of the card itself. AT
|
|
184
|
+
* navigates to the slotted content directly, using whatever
|
|
185
|
+
* heading and landmark structure the consumer places inside.
|
|
186
|
+
* - State change: N/A.
|
|
187
|
+
*/
|
|
188
|
+
interface DsCard {
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* ## Accessibility
|
|
192
|
+
* **Category:** form-field
|
|
193
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
194
|
+
* `<input type="checkbox">`; the browser already implements the
|
|
195
|
+
* checkbox role and its state semantics. Reference:
|
|
196
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
|
197
|
+
* Note: `ds-checkbox` intentionally does NOT reach for the WAI-ARIA
|
|
198
|
+
* APG "tri-state checkbox" pattern (indeterminate + on + off) as a
|
|
199
|
+
* scratch-built ARIA widget. The native input already exposes the
|
|
200
|
+
* `indeterminate` DOM property and browsers report it to AT
|
|
201
|
+
* correctly; the `indeterminate` prop here forwards to that DOM
|
|
202
|
+
* property so the native mechanism carries the semantics.
|
|
203
|
+
* **Keyboard interactions:**
|
|
204
|
+
* - Tab: moves focus to the native `<input type="checkbox">`.
|
|
205
|
+
* - Space: toggles the checked state (native behavior).
|
|
206
|
+
* - Enter: no native effect on a checkbox (unlike button). Form
|
|
207
|
+
* submission triggered by Enter comes from the enclosing form's
|
|
208
|
+
* default submit button, not from the checkbox itself.
|
|
209
|
+
* **ARIA:**
|
|
210
|
+
* - Roles used: none explicit — the native checkbox supplies the
|
|
211
|
+
* implicit `checkbox` role.
|
|
212
|
+
* - State attributes: `checked` (DOM property, kept in sync via
|
|
213
|
+
* componentDidRender because Stencil does not JSX-forward the
|
|
214
|
+
* attribute cleanly for controlled use), `disabled` (native
|
|
215
|
+
* attribute, not `aria-disabled`), `aria-invalid` (from the
|
|
216
|
+
* `invalid` prop), `aria-required` (from the `required` prop),
|
|
217
|
+
* `aria-describedby` (from the `describedBy` prop).
|
|
218
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
219
|
+
* the internal checkbox. This is the **canonical, recommended**
|
|
220
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
221
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
222
|
+
* same secondary mode documented on `ds-input`.
|
|
223
|
+
* **Focus:**
|
|
224
|
+
* - On mount: nothing auto-focuses.
|
|
225
|
+
* - On toggle: focus remains on the native checkbox.
|
|
226
|
+
* - Focus indicator: browser default `:focus-visible` ring, kept on
|
|
227
|
+
* purpose until the DS ships a themed focus utility using
|
|
228
|
+
* `--ds-color-focus`. Not overridden by `ds-checkbox.css`.
|
|
229
|
+
* **Motion:**
|
|
230
|
+
* - Animations: none.
|
|
231
|
+
* - Reduced-motion behavior: N/A.
|
|
232
|
+
* **Contrast:**
|
|
233
|
+
* - Text: N/A — the checkbox itself renders no text (labels are
|
|
234
|
+
* supplied by the consumer via `label` prop or wrapping <label>).
|
|
235
|
+
* - Non-text (the box and its check glyph): browser default rendering
|
|
236
|
+
* is kept on purpose (no `appearance: none` override); this
|
|
237
|
+
* preserves the OS/browser accessibility rendering that AT and
|
|
238
|
+
* forced-color modes rely on. A themed override is a future
|
|
239
|
+
* decision once the DS commits to a checkbox visual language that
|
|
240
|
+
* still passes 3:1 non-text contrast in every theme.
|
|
241
|
+
* **Screen reader announcement:**
|
|
242
|
+
* - Rendered state: announces the `label` prop text as the
|
|
243
|
+
* accessible name, followed by "checkbox", followed by "checked"
|
|
244
|
+
* / "not checked" / "mixed" per the current DOM state.
|
|
245
|
+
* - State change: on toggle, AT announces the new state (native
|
|
246
|
+
* behavior); no `aria-live` added.
|
|
247
|
+
* **Consumer contract:**
|
|
248
|
+
* - `checked` is the source of truth for controlled use. Reading
|
|
249
|
+
* `event.detail` from `dsChange` gives the new state; the internal
|
|
250
|
+
* input is kept in sync via a Watch + a re-sync in componentDidRender.
|
|
251
|
+
* - `indeterminate` is a visual/AT state, not a value — it is NOT
|
|
252
|
+
* submitted with a form and clears on the next user toggle.
|
|
253
|
+
*/
|
|
254
|
+
interface DsCheckbox {
|
|
255
|
+
/**
|
|
256
|
+
* @default false
|
|
257
|
+
*/
|
|
258
|
+
"checked": boolean;
|
|
259
|
+
"describedBy"?: string;
|
|
260
|
+
/**
|
|
261
|
+
* @default false
|
|
262
|
+
*/
|
|
263
|
+
"disabled": boolean;
|
|
264
|
+
/**
|
|
265
|
+
* @default false
|
|
266
|
+
*/
|
|
267
|
+
"indeterminate": boolean;
|
|
268
|
+
/**
|
|
269
|
+
* @default false
|
|
270
|
+
*/
|
|
271
|
+
"invalid": boolean;
|
|
272
|
+
"label"?: string;
|
|
273
|
+
"name"?: string;
|
|
274
|
+
/**
|
|
275
|
+
* @default false
|
|
276
|
+
*/
|
|
277
|
+
"required": boolean;
|
|
278
|
+
"value"?: string;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* ## Accessibility
|
|
282
|
+
* **Category:** interactive-display
|
|
283
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
284
|
+
* (for the internal copy control; the code readout itself is static text).
|
|
285
|
+
* **Keyboard interactions:**
|
|
286
|
+
* - Tab: moves focus into the internal copy `<button>`; the code readout
|
|
287
|
+
* (`<pre><code>`) is not focusable itself, but its container is
|
|
288
|
+
* horizontally scrollable and remains reachable via the browser's caret
|
|
289
|
+
* browsing / find-in-page path.
|
|
290
|
+
* - Enter: activates the copy button (writes `code` to the clipboard).
|
|
291
|
+
* - Space: activates the copy button (native `<button>` behavior).
|
|
292
|
+
* - Escape: N/A — the "Copied" state auto-clears after 1.4s; no dismissible
|
|
293
|
+
* modal state.
|
|
294
|
+
* - Arrow keys: N/A.
|
|
295
|
+
* - Printable characters: N/A.
|
|
296
|
+
* **ARIA:**
|
|
297
|
+
* - Roles used: none explicit — the native `<button>` supplies the implicit
|
|
298
|
+
* `button` role; `<pre>` and `<code>` supply their semantic roles.
|
|
299
|
+
* - State attributes: none. The `copied` flag lives only in the button's
|
|
300
|
+
* accessible name (see below); it is intentionally not surfaced via
|
|
301
|
+
* `aria-pressed` because it is not a toggle — it is a transient
|
|
302
|
+
* post-action confirmation.
|
|
303
|
+
* - Labeling strategy: `aria-label` on the copy button — "Copy code" in the
|
|
304
|
+
* resting state, "Copied" while the confirmation is visible. This is
|
|
305
|
+
* authoritative because the button carries both an icon and the words
|
|
306
|
+
* "Copy" / "Copied", and mixed icon+text needs an explicit accessible
|
|
307
|
+
* name to avoid AT reading the SVG as a separate node.
|
|
308
|
+
* **Focus:**
|
|
309
|
+
* - On mount: nothing auto-focuses.
|
|
310
|
+
* - On activate: focus stays on the copy button (clipboard write is
|
|
311
|
+
* synchronous from the user's perspective; no focus is moved).
|
|
312
|
+
* - On close/deactivate: N/A — no popup or overlay to close.
|
|
313
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
314
|
+
* `outline-offset`, applied to `.ds-code-block__copy:focus-visible`. The
|
|
315
|
+
* readout itself is not focusable and has no focus indicator by design.
|
|
316
|
+
* **Motion:**
|
|
317
|
+
* - Animations: 120ms `color` / `border-color` ease transition on the copy
|
|
318
|
+
* button (hover/focus).
|
|
319
|
+
* - Reduced-motion behavior: N/A — 120ms is below the spec's 200ms
|
|
320
|
+
* threshold, and the transition animates color only.
|
|
321
|
+
* **Contrast:**
|
|
322
|
+
* - Text (code readout): inherits `color: currentColor`; contrast depends
|
|
323
|
+
* on the host page's text color vs. the block's tinted background
|
|
324
|
+
* (`color-mix(currentColor 4%, transparent)`). Verify per site.
|
|
325
|
+
* - Text (copy button): `color-mix(in oklch, currentColor 70%, transparent)`
|
|
326
|
+
* at rest, `currentColor` on hover. The 70%-mixed resting color can drop
|
|
327
|
+
* below 4.5:1 on low-contrast surfaces — audit per usage site, and lean
|
|
328
|
+
* on the hover/focus states (both restore full `currentColor`) as the
|
|
329
|
+
* accessible fallback.
|
|
330
|
+
* - Non-text (button border, language tag border-bottom): both use
|
|
331
|
+
* `color-mix(currentColor <15|12>%, transparent)` — decorative separators,
|
|
332
|
+
* they do not convey state, so the 3:1 non-text contrast rule does not
|
|
333
|
+
* apply. Focus and hover borders shift up to 30% for clearer edges.
|
|
334
|
+
* **Screen reader announcement:**
|
|
335
|
+
* - Rendered state: announces "Copy code, button" (the `aria-label`), then
|
|
336
|
+
* the code content when the user navigates into the `<pre><code>` block
|
|
337
|
+
* (usually announced as a code block on VoiceOver).
|
|
338
|
+
* - State change: after clicking Copy, the button's `aria-label` flips to
|
|
339
|
+
* "Copied"; AT will announce this on the next focus or re-read of the
|
|
340
|
+
* button, but NOT proactively — there is no `aria-live` region. This is
|
|
341
|
+
* deliberate per spec §"What we explicitly do NOT do" (avoid live-region
|
|
342
|
+
* overuse), and the trade-off is that a screen-reader-only user does not
|
|
343
|
+
* get an unsolicited confirmation. If a future review decides that
|
|
344
|
+
* confirmation must be audible, add a visually-hidden
|
|
345
|
+
* `aria-live="polite"` element whose text toggles on `copied`.
|
|
346
|
+
*/
|
|
347
|
+
interface DsCodeBlock {
|
|
348
|
+
"code": string;
|
|
349
|
+
/**
|
|
350
|
+
* @default "bash"
|
|
351
|
+
*/
|
|
352
|
+
"language": string;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* ## Accessibility
|
|
356
|
+
* **Category:** state-machine
|
|
357
|
+
* **APG pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
|
|
358
|
+
* This component wraps the **native `<dialog>` element** rather than
|
|
359
|
+
* scratch-building the APG pattern on top of a `<div role="dialog">`.
|
|
360
|
+
* `<dialog>` opened via `showModal()` gives us — from the browser,
|
|
361
|
+
* verified by the platform, not by us:
|
|
362
|
+
* - Focus trap: Tab / Shift+Tab cycle only through focusable
|
|
363
|
+
* descendants (including slotted light-DOM descendants; the flattened
|
|
364
|
+
* accessibility tree is what the trap operates on).
|
|
365
|
+
* - Escape: closes the dialog and returns focus to the previously
|
|
366
|
+
* focused element (native, not implemented here).
|
|
367
|
+
* - Inert background: everything outside the dialog is inert while it
|
|
368
|
+
* is open — no mouse, no keyboard, not part of the a11y tree.
|
|
369
|
+
* - Return focus: on close, focus returns to whatever element had it
|
|
370
|
+
* before `showModal()` — native, not implemented here.
|
|
371
|
+
* - Initial focus: browser auto-focuses the first `autofocus` element
|
|
372
|
+
* inside, or the first focusable if none — native.
|
|
373
|
+
* - `role="dialog"` and `aria-modal="true"` semantics: implicit on
|
|
374
|
+
* `<dialog>`, no ARIA needed from us.
|
|
375
|
+
* This is the deliberate answer to the a11y spec §D warning about
|
|
376
|
+
* scratch-building Tier 2 components: the native element removes the
|
|
377
|
+
* "compiles, axe passes, screen-reader user gets trapped" failure
|
|
378
|
+
* mode by delegating the state machine to the platform.
|
|
379
|
+
* **Keyboard interactions (all handled by the native element unless
|
|
380
|
+
* noted otherwise):**
|
|
381
|
+
* - Tab: cycles focus within the dialog's focusable descendants
|
|
382
|
+
* (native focus trap).
|
|
383
|
+
* - Shift+Tab: reverse cycle (native).
|
|
384
|
+
* - Escape: closes the dialog and fires the `close` event (native).
|
|
385
|
+
* Consumers who need to prevent dismissal on Escape (destructive
|
|
386
|
+
* flows: e.g. an unsaved-changes guard) must call
|
|
387
|
+
* `event.preventDefault()` inside their own `keydown` listener on
|
|
388
|
+
* the dialog before it reaches the browser — that is an escape
|
|
389
|
+
* hatch, not a default we impose.
|
|
390
|
+
* - Enter / Space: no special dialog behavior; forwarded to
|
|
391
|
+
* whatever descendant has focus (a button will activate).
|
|
392
|
+
* - Home / End / Arrow keys: no special dialog behavior; forwarded
|
|
393
|
+
* to descendants.
|
|
394
|
+
* - Printable characters: forwarded to descendants (e.g. typing
|
|
395
|
+
* inside a slotted `<ds-input>`).
|
|
396
|
+
* **ARIA attributes and value triggers:**
|
|
397
|
+
* - Implicit `role="dialog"` on the native `<dialog>`.
|
|
398
|
+
* - Implicit `aria-modal="true"` when opened via `showModal()`
|
|
399
|
+
* (which is what this component always uses; `show()` — the
|
|
400
|
+
* non-modal form — is intentionally not exposed in v1).
|
|
401
|
+
* - `aria-label` set from the `label` prop onto the `<dialog>`.
|
|
402
|
+
* Consumers who prefer an in-content title can pass a
|
|
403
|
+
* `labelledBy` prop instead, whose value is the id of a heading
|
|
404
|
+
* element they render inside the slot — `aria-labelledby`
|
|
405
|
+
* resolves that id through the flattened tree.
|
|
406
|
+
* - `aria-describedby` from the `describedBy` prop, resolving a
|
|
407
|
+
* light-DOM id of a descriptive block (e.g. the dialog body copy
|
|
408
|
+
* when the label is short).
|
|
409
|
+
* **Focus lifecycle:**
|
|
410
|
+
* - Opening: `showModal()` moves focus to the first `autofocus`
|
|
411
|
+
* element inside, or the first focusable if none. Consumers who
|
|
412
|
+
* want a specific initial focus target should mark that element
|
|
413
|
+
* with `autofocus`.
|
|
414
|
+
* - While open: focus is trapped within the flattened tree of the
|
|
415
|
+
* `<dialog>`; Tab / Shift+Tab cycle through slotted descendants.
|
|
416
|
+
* Focus cannot escape into the inert background.
|
|
417
|
+
* - Closing: `close()` (either from Escape, from a form-submit with
|
|
418
|
+
* `method="dialog"`, from a light-dismiss backdrop click handler
|
|
419
|
+
* the consumer wires up, or from `open=false`) returns focus to
|
|
420
|
+
* the element that had it immediately before `showModal()` was
|
|
421
|
+
* called. The `dsClose` event fires after focus has been restored.
|
|
422
|
+
* **ARIA labelling contract (choose one):**
|
|
423
|
+
* - **`label`** (string) — sets `aria-label` on the dialog. Simplest
|
|
424
|
+
* for short titles.
|
|
425
|
+
* - **`labelledBy`** (id) — sets `aria-labelledby` to reference a
|
|
426
|
+
* heading rendered inside the slot. Preferred when the dialog has
|
|
427
|
+
* a visible title so the accessible name matches the visible name.
|
|
428
|
+
* If both are omitted, axe reports `dialog-name` — the dialog has no
|
|
429
|
+
* accessible name. The a11y test for this component asserts that at
|
|
430
|
+
* least one of the two is provided.
|
|
431
|
+
* **Motion:**
|
|
432
|
+
* - Animations: none applied by the component. Consumers who want an
|
|
433
|
+
* entrance transition can style `::backdrop` and `dialog[open]`
|
|
434
|
+
* with their own animation, gated on `@media
|
|
435
|
+
* (prefers-reduced-motion: no-preference)`.
|
|
436
|
+
* - Reduced-motion behavior: N/A — no animation to collapse. If a
|
|
437
|
+
* consumer adds one, they own the reduced-motion fallback.
|
|
438
|
+
* **Contrast:**
|
|
439
|
+
* - Text: inherits `color: inherit`; consumer theme owns contrast.
|
|
440
|
+
* - Non-text: subtle bordered surface via
|
|
441
|
+
* `color-mix(in oklch, currentColor N%, transparent)` matching
|
|
442
|
+
* `ds-card`. Backdrop is a semi-transparent black by default.
|
|
443
|
+
* **Screen reader announcement:**
|
|
444
|
+
* - Rendered state (closed): dialog is not in the a11y tree.
|
|
445
|
+
* - Opened: AT announces "dialog" plus the accessible name from
|
|
446
|
+
* `label` / `labelledBy`, then reads the content following focus
|
|
447
|
+
* (or the descriptive block referenced by `describedBy` first,
|
|
448
|
+
* depending on AT).
|
|
449
|
+
* - State change: on close, AT returns to the element that regained
|
|
450
|
+
* focus (the trigger); no `aria-live` announcement is added.
|
|
451
|
+
* **Consumer contract:**
|
|
452
|
+
* - Set `open` to true to show; set to false to close. The component
|
|
453
|
+
* syncs the DOM state via `showModal()` / `close()` in a Watch.
|
|
454
|
+
* - Listen to `dsClose` to know when the dialog was dismissed (by
|
|
455
|
+
* Escape, by `open=false`, or by a form submit with
|
|
456
|
+
* `method="dialog"`). Listen to `dsOpen` for the mirror event.
|
|
457
|
+
* - The default backdrop is opaque and NOT click-to-dismiss. Consumers
|
|
458
|
+
* who want light-dismiss can add a click listener on the dialog
|
|
459
|
+
* that closes when the click target is the dialog itself (not a
|
|
460
|
+
* descendant): `if (e.target === dialog) dialog.close()`.
|
|
461
|
+
* - Put focus targets (form fields, primary action button) inside
|
|
462
|
+
* the slot. Use `autofocus` on the element that should receive
|
|
463
|
+
* focus first.
|
|
464
|
+
* **Pending manual verification (see commit message TODO):**
|
|
465
|
+
* - VoiceOver on macOS: confirm the dialog announces its accessible
|
|
466
|
+
* name + "dialog" on open, focus lands on the correct element,
|
|
467
|
+
* Escape closes and focus returns to the trigger.
|
|
468
|
+
* - Chromium high-contrast / forced-colors: confirm the surface
|
|
469
|
+
* still reads as a bordered container with the native ::backdrop
|
|
470
|
+
* dim.
|
|
471
|
+
*/
|
|
472
|
+
interface DsDialog {
|
|
473
|
+
"describedBy"?: string;
|
|
474
|
+
"label"?: string;
|
|
475
|
+
"labelledBy"?: string;
|
|
476
|
+
/**
|
|
477
|
+
* @default false
|
|
478
|
+
*/
|
|
479
|
+
"open": boolean;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* ## Accessibility
|
|
483
|
+
* **Category:** form-field
|
|
484
|
+
* **APG pattern:** N/A — native element. This wraps a native `<input>`;
|
|
485
|
+
* there is no WAI-ARIA APG pattern for a plain text input because the
|
|
486
|
+
* browser already implements it natively. Reference:
|
|
487
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
|
|
488
|
+
* **Keyboard interactions:**
|
|
489
|
+
* - Tab: moves focus to the native `<input>` (shadow DOM does not
|
|
490
|
+
* interrupt tab order).
|
|
491
|
+
* - Printable characters: insert at the caret position (native).
|
|
492
|
+
* - Backspace / Delete: native editing.
|
|
493
|
+
* - Arrow keys: move the caret within the value (native); for
|
|
494
|
+
* `type="number"` Up/Down step the numeric value (native).
|
|
495
|
+
* - Enter: on `type="search"` may submit the enclosing form (native);
|
|
496
|
+
* no custom handling.
|
|
497
|
+
* - Escape: no native meaning for a text input.
|
|
498
|
+
* **ARIA:**
|
|
499
|
+
* - Roles used: none explicit — the native `<input>` supplies its
|
|
500
|
+
* implicit role (`textbox`, `searchbox`, etc. depending on `type`).
|
|
501
|
+
* - State attributes: `disabled` (native attribute, not `aria-disabled`),
|
|
502
|
+
* `aria-invalid` (from the `invalid` prop), `aria-required` (from the
|
|
503
|
+
* `required` prop), `aria-describedby` (from the `describedBy` prop,
|
|
504
|
+
* expected to reference a light-DOM node id — typically a hint or
|
|
505
|
+
* error message rendered by the consumer alongside the field).
|
|
506
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto the
|
|
507
|
+
* internal `<input>`. This is the **canonical, recommended** mode —
|
|
508
|
+
* explicit and independent of DOM structure. Consumers MUST pass
|
|
509
|
+
* `label` (or otherwise give the field an accessible name) — an
|
|
510
|
+
* `<input>` with no accessible name is a WCAG failure (axe rule
|
|
511
|
+
* `label`). Wrapping the host in a light-DOM `<label>` (e.g.
|
|
512
|
+
* `<label>Email <ds-input></ds-input></label>`) also produces an
|
|
513
|
+
* accessible name in Chromium via flattened-tree resolution — the
|
|
514
|
+
* same secondary mode documented on `ds-select`, verified in this
|
|
515
|
+
* component's e2e tests. Setting `aria-label` directly on the
|
|
516
|
+
* `<ds-input>` host does NOT work: shadow-DOM attribute forwarding
|
|
517
|
+
* would be required — use the `label` prop instead.
|
|
518
|
+
* **Focus:**
|
|
519
|
+
* - On mount: nothing auto-focuses.
|
|
520
|
+
* - On value change: focus remains on the native `<input>`.
|
|
521
|
+
* - Focus indicator: the browser default `:focus-visible` ring is kept
|
|
522
|
+
* on purpose — a themed focus ring using `--ds-color-focus` is a
|
|
523
|
+
* future decision once the DS ships a shared focus utility. Not
|
|
524
|
+
* overridden by `ds-input.css`.
|
|
525
|
+
* **Motion:**
|
|
526
|
+
* - Animations: none.
|
|
527
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
528
|
+
* **Contrast:**
|
|
529
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends on
|
|
530
|
+
* the consumer's theme, not verified in isolation here.
|
|
531
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
532
|
+
* matching `ds-select` — inherits whatever contrast the host page
|
|
533
|
+
* sets. WCAG 1.4.11 (3:1 non-text) is the consumer's responsibility
|
|
534
|
+
* in themes that override the surface.
|
|
535
|
+
* **Screen reader announcement:**
|
|
536
|
+
* - Rendered state: announces the `label` prop text as the accessible
|
|
537
|
+
* name, followed by the browser/AT's role announcement for the
|
|
538
|
+
* input type ("edit text", "search text", "password", "phone
|
|
539
|
+
* number", "email"…). When `required=true` the announcement
|
|
540
|
+
* includes "required"; when `invalid=true` it includes "invalid".
|
|
541
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
542
|
+
* character (native input behavior); no `aria-live` added, per the
|
|
543
|
+
* spec's guidance against live-region overuse.
|
|
544
|
+
* **Consumer contract:**
|
|
545
|
+
* - `placeholder` is a hint, not a label. It disappears on focus in
|
|
546
|
+
* most browsers and is not reliably announced by every AT — always
|
|
547
|
+
* pair with `label`.
|
|
548
|
+
* - For `type="password"` in a real form, pair with a light-DOM
|
|
549
|
+
* `<input type="hidden">` or a form-associated ancestor that gives
|
|
550
|
+
* the browser's password manager an `autocomplete` hint. This
|
|
551
|
+
* component intentionally does not expose `autocomplete` in v1;
|
|
552
|
+
* consumers that need it can render a native `<input>` for that
|
|
553
|
+
* field until the prop is added.
|
|
554
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
555
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
556
|
+
* status but not the reason.
|
|
557
|
+
*/
|
|
558
|
+
interface DsInput {
|
|
559
|
+
"describedBy"?: string;
|
|
560
|
+
/**
|
|
561
|
+
* @default false
|
|
562
|
+
*/
|
|
563
|
+
"disabled": boolean;
|
|
564
|
+
/**
|
|
565
|
+
* @default false
|
|
566
|
+
*/
|
|
567
|
+
"invalid": boolean;
|
|
568
|
+
"label"?: string;
|
|
569
|
+
"name"?: string;
|
|
570
|
+
"placeholder"?: string;
|
|
571
|
+
/**
|
|
572
|
+
* @default false
|
|
573
|
+
*/
|
|
574
|
+
"required": boolean;
|
|
575
|
+
/**
|
|
576
|
+
* @default "md"
|
|
577
|
+
*/
|
|
578
|
+
"size": "md" | "lg";
|
|
579
|
+
/**
|
|
580
|
+
* @default "text"
|
|
581
|
+
*/
|
|
582
|
+
"type": | "text"
|
|
583
|
+
| "email"
|
|
584
|
+
| "password"
|
|
585
|
+
| "search"
|
|
586
|
+
| "tel"
|
|
587
|
+
| "url"
|
|
588
|
+
| "number";
|
|
589
|
+
"value"?: string;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* ## Accessibility
|
|
593
|
+
* **Category:** static-display (indeterminate progress indicator)
|
|
594
|
+
* **APG pattern:** N/A — the loader is a passive presence indicator, not a
|
|
595
|
+
* `progressbar` (no measurable value). Uses `role="status"` per WAI-ARIA
|
|
596
|
+
* live-region semantics for indeterminate loading states.
|
|
597
|
+
* **Keyboard interactions:**
|
|
598
|
+
* - Tab: N/A — not focusable.
|
|
599
|
+
* - Enter: N/A.
|
|
600
|
+
* - Escape: N/A.
|
|
601
|
+
* - Arrow keys: N/A.
|
|
602
|
+
* - Printable characters: N/A.
|
|
603
|
+
* **ARIA:**
|
|
604
|
+
* - Roles used: `role="status"` on the host — an implicit polite live
|
|
605
|
+
* region ARIA lists as valid for "advisory information" including
|
|
606
|
+
* in-progress operations. The inner `<svg>` is decorative and carries
|
|
607
|
+
* `aria-hidden="true"` + `focusable="false"` so it never becomes a
|
|
608
|
+
* parallel target for screen readers.
|
|
609
|
+
* - State attributes: none. The loader has no state changes to announce
|
|
610
|
+
* beyond its own presence; the polite live region on mount is the
|
|
611
|
+
* announcement.
|
|
612
|
+
* - Labeling strategy: `aria-label` from the `label` prop (default
|
|
613
|
+
* "Loading"). Consumers pass a more specific string when the context
|
|
614
|
+
* warrants ("Loading ceremonies", "Signing in", …).
|
|
615
|
+
* **Focus:**
|
|
616
|
+
* - On mount: nothing auto-focuses.
|
|
617
|
+
* - On activate/open: N/A — not interactive.
|
|
618
|
+
* - On close/deactivate: N/A.
|
|
619
|
+
* - Focus indicator: N/A — the host is not focusable.
|
|
620
|
+
* **Motion:**
|
|
621
|
+
* - Animations: two mirrored halves of the logo path oscillate
|
|
622
|
+
* symmetrically around the visual center, meeting at the ring midpoint
|
|
623
|
+
* and separating again. Cycle 1.4s by default (`speed="normal"`);
|
|
624
|
+
* `speed="slow"` = 2.2s, `speed="fast"` = 0.9s.
|
|
625
|
+
* - Reduced-motion behavior: `@media (prefers-reduced-motion: reduce)`
|
|
626
|
+
* collapses both halves to a static rendering with a slow opacity
|
|
627
|
+
* pulse (2s ease-in-out). No rotation, no lateral movement.
|
|
628
|
+
* **Contrast:**
|
|
629
|
+
* - Text: N/A — the visible surface is the SVG stroke, not text.
|
|
630
|
+
* - Non-text: stroke inherits `currentColor` from the host. Consumers are
|
|
631
|
+
* responsible for confirming the stroke against its background meets
|
|
632
|
+
* the ≥ 3:1 non-text contrast threshold (WCAG 1.4.11) where the loader
|
|
633
|
+
* conveys state (e.g. distinguishing "still loading" from "idle").
|
|
634
|
+
* **Screen reader announcement:**
|
|
635
|
+
* - Rendered state: the polite live region announces the label
|
|
636
|
+
* (e.g. "Loading") when the loader mounts.
|
|
637
|
+
* - State change: N/A — the loader itself has no state that changes.
|
|
638
|
+
* Consumers unmount the loader when the awaited work completes; that
|
|
639
|
+
* removal is silent, which is the intended behavior (the arriving
|
|
640
|
+
* content is the announcement).
|
|
641
|
+
*/
|
|
642
|
+
interface DsLoader {
|
|
643
|
+
/**
|
|
644
|
+
* @default "Loading"
|
|
645
|
+
*/
|
|
646
|
+
"label": string;
|
|
647
|
+
/**
|
|
648
|
+
* @default "md"
|
|
649
|
+
*/
|
|
650
|
+
"size": "sm" | "md" | "lg";
|
|
651
|
+
/**
|
|
652
|
+
* @default "normal"
|
|
653
|
+
*/
|
|
654
|
+
"speed": "slow" | "normal" | "fast";
|
|
655
|
+
}
|
|
656
|
+
interface DsSelect {
|
|
657
|
+
/**
|
|
658
|
+
* @default false
|
|
659
|
+
*/
|
|
660
|
+
"disabled": boolean;
|
|
661
|
+
"label"?: string;
|
|
662
|
+
"name"?: string;
|
|
663
|
+
/**
|
|
664
|
+
* Options rendered inside the internal shadow `<select>`. Accepts either: - a JSON string (SSR-friendly — server-rendered HTML can only set string attributes, and Stencil does not JSON-parse array attributes), e.g. `options='[{"value":"en","label":"EN"}]'` - an array property set from client-side JS, e.g. `el.options = [{ value: 'en', label: 'EN' }]` Malformed JSON fails safe to an empty list rather than throwing, so the component still renders a visible (if empty) select.
|
|
665
|
+
* @default "[]"
|
|
666
|
+
*/
|
|
667
|
+
"options": string | ReadonlyArray<DsSelectOption>;
|
|
668
|
+
/**
|
|
669
|
+
* @default "md"
|
|
670
|
+
*/
|
|
671
|
+
"size": "md" | "lg";
|
|
672
|
+
"value"?: string;
|
|
673
|
+
}
|
|
674
|
+
interface DsTabs {
|
|
675
|
+
"active"?: string;
|
|
676
|
+
/**
|
|
677
|
+
* @default "[]"
|
|
678
|
+
*/
|
|
679
|
+
"items": string | ReadonlyArray<DsTabItem>;
|
|
680
|
+
"label"?: string;
|
|
681
|
+
/**
|
|
682
|
+
* @default "horizontal"
|
|
683
|
+
*/
|
|
684
|
+
"orientation": "horizontal" | "vertical";
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* ## Accessibility
|
|
688
|
+
* **Category:** form-field
|
|
689
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
690
|
+
* `<textarea>`; there is no WAI-ARIA APG pattern for a plain
|
|
691
|
+
* multi-line text field because the browser already implements it
|
|
692
|
+
* natively. Reference:
|
|
693
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
|
|
694
|
+
* **Keyboard interactions:**
|
|
695
|
+
* - Tab: moves focus to the native `<textarea>`; Tab inside the
|
|
696
|
+
* textarea inserts a tab character only when the browser is
|
|
697
|
+
* configured to do so (default behavior: Tab moves focus out).
|
|
698
|
+
* - Printable characters: insert at the caret position (native).
|
|
699
|
+
* - Enter: inserts a newline within the value (native), does not
|
|
700
|
+
* submit the enclosing form (unlike a single-line input).
|
|
701
|
+
* - Backspace / Delete: native editing.
|
|
702
|
+
* - Arrow keys: move the caret within the value (native).
|
|
703
|
+
* - Escape: no native meaning for a text area.
|
|
704
|
+
* **ARIA:**
|
|
705
|
+
* - Roles used: none explicit — the native `<textarea>` supplies
|
|
706
|
+
* the implicit `textbox` role with multiline behavior.
|
|
707
|
+
* - State attributes: `disabled` (native attribute, not
|
|
708
|
+
* `aria-disabled`), `aria-invalid` (from the `invalid` prop),
|
|
709
|
+
* `aria-required` (from the `required` prop), `aria-describedby`
|
|
710
|
+
* (from the `describedBy` prop, expected to reference a light-DOM
|
|
711
|
+
* node id — typically a hint or error message rendered by the
|
|
712
|
+
* consumer alongside the field).
|
|
713
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
714
|
+
* the internal `<textarea>`. This is the **canonical, recommended**
|
|
715
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
716
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
717
|
+
* same secondary mode documented on `ds-input` and `ds-select`,
|
|
718
|
+
* verified in this component's e2e tests. Setting `aria-label`
|
|
719
|
+
* directly on the `<ds-textarea>` host does NOT work: shadow-DOM
|
|
720
|
+
* attribute forwarding would be required — use the `label` prop.
|
|
721
|
+
* **Focus:**
|
|
722
|
+
* - On mount: nothing auto-focuses.
|
|
723
|
+
* - On value change: focus remains on the native `<textarea>`.
|
|
724
|
+
* - Focus indicator: the browser default `:focus-visible` ring is
|
|
725
|
+
* kept on purpose — a themed focus ring using `--ds-color-focus`
|
|
726
|
+
* is a future decision once the DS ships a shared focus utility.
|
|
727
|
+
* **Motion:**
|
|
728
|
+
* - Animations: none.
|
|
729
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
730
|
+
* **Contrast:**
|
|
731
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends
|
|
732
|
+
* on the consumer's theme, not verified in isolation.
|
|
733
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
734
|
+
* matching `ds-input`.
|
|
735
|
+
* **Screen reader announcement:**
|
|
736
|
+
* - Rendered state: announces the `label` prop text as the
|
|
737
|
+
* accessible name, followed by "edit text, multiline" (browser/AT
|
|
738
|
+
* wording varies). When `required=true` the announcement includes
|
|
739
|
+
* "required"; when `invalid=true` it includes "invalid".
|
|
740
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
741
|
+
* character (native behavior); no `aria-live` added.
|
|
742
|
+
* **Consumer contract:**
|
|
743
|
+
* - `placeholder` is a hint, not a label — always pair with `label`.
|
|
744
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
745
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
746
|
+
* status but not the reason.
|
|
747
|
+
* - `rows` sets the initial visible line count. Vertical resize is
|
|
748
|
+
* left to the native default (`resize: vertical`) so users can
|
|
749
|
+
* grow the field for long entries; the consumer's CSS can pin it
|
|
750
|
+
* with `resize: none` when a fixed height matters.
|
|
751
|
+
*/
|
|
752
|
+
interface DsTextarea {
|
|
753
|
+
"describedBy"?: string;
|
|
754
|
+
/**
|
|
755
|
+
* @default false
|
|
756
|
+
*/
|
|
757
|
+
"disabled": boolean;
|
|
758
|
+
/**
|
|
759
|
+
* @default false
|
|
760
|
+
*/
|
|
761
|
+
"invalid": boolean;
|
|
762
|
+
"label"?: string;
|
|
763
|
+
"name"?: string;
|
|
764
|
+
"placeholder"?: string;
|
|
765
|
+
/**
|
|
766
|
+
* @default false
|
|
767
|
+
*/
|
|
768
|
+
"required": boolean;
|
|
769
|
+
/**
|
|
770
|
+
* @default 3
|
|
771
|
+
*/
|
|
772
|
+
"rows": number;
|
|
773
|
+
/**
|
|
774
|
+
* @default "md"
|
|
775
|
+
*/
|
|
776
|
+
"size": "md" | "lg";
|
|
777
|
+
"value"?: string;
|
|
778
|
+
}
|
|
779
|
+
interface DsTooltip {
|
|
780
|
+
/**
|
|
781
|
+
* @default 100
|
|
782
|
+
*/
|
|
783
|
+
"closeDelay": number;
|
|
784
|
+
"label": string;
|
|
785
|
+
/**
|
|
786
|
+
* @default 250
|
|
787
|
+
*/
|
|
788
|
+
"openDelay": number;
|
|
789
|
+
/**
|
|
790
|
+
* @default "bottom"
|
|
791
|
+
*/
|
|
792
|
+
"placement": "top" | "bottom";
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
export interface DsCheckboxCustomEvent<T> extends CustomEvent<T> {
|
|
796
|
+
detail: T;
|
|
797
|
+
target: HTMLDsCheckboxElement;
|
|
798
|
+
}
|
|
799
|
+
export interface DsDialogCustomEvent<T> extends CustomEvent<T> {
|
|
800
|
+
detail: T;
|
|
801
|
+
target: HTMLDsDialogElement;
|
|
802
|
+
}
|
|
803
|
+
export interface DsInputCustomEvent<T> extends CustomEvent<T> {
|
|
804
|
+
detail: T;
|
|
805
|
+
target: HTMLDsInputElement;
|
|
806
|
+
}
|
|
807
|
+
export interface DsSelectCustomEvent<T> extends CustomEvent<T> {
|
|
808
|
+
detail: T;
|
|
809
|
+
target: HTMLDsSelectElement;
|
|
810
|
+
}
|
|
811
|
+
export interface DsTabsCustomEvent<T> extends CustomEvent<T> {
|
|
812
|
+
detail: T;
|
|
813
|
+
target: HTMLDsTabsElement;
|
|
814
|
+
}
|
|
815
|
+
export interface DsTextareaCustomEvent<T> extends CustomEvent<T> {
|
|
816
|
+
detail: T;
|
|
817
|
+
target: HTMLDsTextareaElement;
|
|
818
|
+
}
|
|
819
|
+
declare global {
|
|
820
|
+
/**
|
|
821
|
+
* ## Accessibility
|
|
822
|
+
* **Category:** static-display
|
|
823
|
+
* **APG pattern:** N/A — non-interactive text label.
|
|
824
|
+
* **Keyboard interactions:**
|
|
825
|
+
* - Tab: N/A — the badge is not focusable and does not appear in tab order.
|
|
826
|
+
* - Enter: N/A.
|
|
827
|
+
* - Escape: N/A.
|
|
828
|
+
* - Arrow keys: N/A.
|
|
829
|
+
* - Printable characters: N/A.
|
|
830
|
+
* **ARIA:**
|
|
831
|
+
* - Roles used: none — renders a plain `<span>` around a `<slot />`. The
|
|
832
|
+
* `variant` prop is a purely visual axis and MUST NOT be the sole carrier
|
|
833
|
+
* of information (color/style alone fails WCAG 1.4.1 Use of Color); the
|
|
834
|
+
* textual content inside the slot must convey the same meaning.
|
|
835
|
+
* - State attributes: none.
|
|
836
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers place the
|
|
837
|
+
* readable label directly inside the tag.
|
|
838
|
+
* **Focus:**
|
|
839
|
+
* - On mount: nothing auto-focuses.
|
|
840
|
+
* - On activate/open: N/A — non-interactive.
|
|
841
|
+
* - On close/deactivate: N/A.
|
|
842
|
+
* - Focus indicator: N/A — the host is not focusable. If a consumer wraps
|
|
843
|
+
* `ds-badge` in an interactive element (e.g. `<button>`, `<a>`), that
|
|
844
|
+
* wrapper owns the focus indicator via the DS `--ds-color-focus` token
|
|
845
|
+
* and its own `:focus-visible` rule.
|
|
846
|
+
* **Motion:**
|
|
847
|
+
* - Animations: none.
|
|
848
|
+
* - Reduced-motion behavior: N/A.
|
|
849
|
+
* **Contrast:**
|
|
850
|
+
* - Text: inherits `color: inherit` / `currentColor` from the host page and
|
|
851
|
+
* the variant-specific style; not verified in isolation here. Consumers
|
|
852
|
+
* are responsible for confirming ≥ 4.5:1 (WCAG 1.4.3) against the surface
|
|
853
|
+
* the badge is placed on.
|
|
854
|
+
* - Non-text: no state-conveying icons or borders that need the 3:1
|
|
855
|
+
* non-text contrast threshold.
|
|
856
|
+
* **Screen reader announcement:**
|
|
857
|
+
* - Rendered state: announces the slot text as inline content within its
|
|
858
|
+
* surrounding context (no role means AT reads it as plain text). The
|
|
859
|
+
* `variant` value is not announced — that is intentional; the label text
|
|
860
|
+
* is the source of truth for meaning.
|
|
861
|
+
* - State change: N/A — no dynamic state owned by this component.
|
|
862
|
+
*/
|
|
863
|
+
interface HTMLDsBadgeElement extends Components.DsBadge, HTMLStencilElement {
|
|
864
|
+
}
|
|
865
|
+
var HTMLDsBadgeElement: {
|
|
866
|
+
prototype: HTMLDsBadgeElement;
|
|
867
|
+
new (): HTMLDsBadgeElement;
|
|
868
|
+
};
|
|
869
|
+
/**
|
|
870
|
+
* ## Accessibility
|
|
871
|
+
* **Category:** interactive-display
|
|
872
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
873
|
+
* (satisfied natively by the underlying `<button>` element). When `href` is
|
|
874
|
+
* set, the component renders `<a href>` instead — a link, not a button — and
|
|
875
|
+
* that surface follows native `<a>` semantics rather than the Button pattern.
|
|
876
|
+
* **Keyboard interactions:**
|
|
877
|
+
* - Tab: moves focus to the internal `<button>` or `<a>` (shadow DOM does not
|
|
878
|
+
* interrupt tab order).
|
|
879
|
+
* - Enter: activates (fires click on `<button>`, navigates on `<a>`).
|
|
880
|
+
* - Space: activates the `<button>` variant only (native behavior); on the
|
|
881
|
+
* `<a>` variant Space scrolls the page as usual.
|
|
882
|
+
* - Escape: N/A — no dismissible state owned by this component.
|
|
883
|
+
* - Arrow keys: N/A.
|
|
884
|
+
* - Printable characters: N/A.
|
|
885
|
+
* **ARIA:**
|
|
886
|
+
* - Roles used: none explicit — the native `<button>` / `<a href>` supplies
|
|
887
|
+
* the implicit `button` / `link` role.
|
|
888
|
+
* - State attributes: `disabled` (native attribute on `<button>`, not
|
|
889
|
+
* `aria-disabled`). The `<a>` variant does not accept `disabled` — a
|
|
890
|
+
* disabled navigation is a spec smell (see spec §"Button vs Link"); if a
|
|
891
|
+
* link needs a disabled state, model it as a `<button>` action instead.
|
|
892
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers MUST place
|
|
893
|
+
* readable text (or a labeled icon) inside the tag. An empty slot leaves
|
|
894
|
+
* the button/link without an accessible name and fails
|
|
895
|
+
* `button-name` / `link-name` axe rules. Icon-only usage is out of contract
|
|
896
|
+
* for `ds-button`; use a future `ds-icon-button` with a required
|
|
897
|
+
* `aria-label` prop when that lands.
|
|
898
|
+
* **Focus:**
|
|
899
|
+
* - On mount: nothing auto-focuses.
|
|
900
|
+
* - On activate: focus stays on the element (native click behavior; the
|
|
901
|
+
* component does not move focus on its own).
|
|
902
|
+
* - On close/deactivate: N/A.
|
|
903
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
904
|
+
* `outline-offset`, applied via `:focus-visible` — replaces the browser
|
|
905
|
+
* default so both `<button>` and `<a>` variants use the same DS ring
|
|
906
|
+
* (spec universal rule 3, WCAG 1.4.11 non-text contrast ≥ 3:1).
|
|
907
|
+
* **Motion:**
|
|
908
|
+
* - Animations: 150ms `background-color` ease-in-out transition on
|
|
909
|
+
* `primary`/`secondary` variants (hover).
|
|
910
|
+
* - Reduced-motion behavior: N/A — 150ms is below the spec's 200ms threshold
|
|
911
|
+
* for `prefers-reduced-motion` handling, and the transition animates color
|
|
912
|
+
* only (no motion of position, scale, or opacity).
|
|
913
|
+
* **Contrast:**
|
|
914
|
+
* - Text: `primary` and `secondary` variants pair `--ds-color-{primary,secondary}`
|
|
915
|
+
* with `--ds-color-text-on-{primary,secondary}` — the token pairs are
|
|
916
|
+
* authored to meet WCAG 2.1 AA (≥ 4.5:1). The `ghost` variant inherits
|
|
917
|
+
* `color: inherit` and pins `opacity: 0.85`, so its effective contrast
|
|
918
|
+
* depends on the consumer's surface — verify per site.
|
|
919
|
+
* - Non-text (border on `ghost`): 1px `currentColor`; contrast tracks the
|
|
920
|
+
* text color 1:1, subject to the same host-page caveat.
|
|
921
|
+
* **Screen reader announcement:**
|
|
922
|
+
* - Rendered state: announces the slot text followed by "button" (or "link"
|
|
923
|
+
* for the `href` variant), plus "dimmed" / "unavailable" when `disabled`
|
|
924
|
+
* is set (browser/AT dependent wording).
|
|
925
|
+
* - State change: N/A — this component does not own toggle state. A pressed
|
|
926
|
+
* toggle needs `aria-pressed`, which is out of scope here; model that as
|
|
927
|
+
* a distinct component or extend the API deliberately per the spec.
|
|
928
|
+
*/
|
|
929
|
+
interface HTMLDsButtonElement extends Components.DsButton, HTMLStencilElement {
|
|
930
|
+
}
|
|
931
|
+
var HTMLDsButtonElement: {
|
|
932
|
+
prototype: HTMLDsButtonElement;
|
|
933
|
+
new (): HTMLDsButtonElement;
|
|
934
|
+
};
|
|
935
|
+
/**
|
|
936
|
+
* ## Accessibility
|
|
937
|
+
* **Category:** static-display
|
|
938
|
+
* **APG pattern:** N/A — a container surface with no interactive
|
|
939
|
+
* behavior of its own. If a consumer nests interactive elements
|
|
940
|
+
* inside (button, link, form field), those elements satisfy their
|
|
941
|
+
* own a11y bucket independently.
|
|
942
|
+
* **Keyboard interactions:**
|
|
943
|
+
* - Tab / Shift+Tab: N/A — the card itself is not focusable. Any
|
|
944
|
+
* interactive descendant carries its own tab semantics.
|
|
945
|
+
* - Enter / Space / Arrow / Escape / Printable: N/A.
|
|
946
|
+
* **ARIA:**
|
|
947
|
+
* - Roles used: none. `<ds-card>` is a purely visual grouping and
|
|
948
|
+
* deliberately does not carry `role="region"` — that would demand
|
|
949
|
+
* a labelledby target, which would only be correct in a subset of
|
|
950
|
+
* uses. Consumers that need a labeled landmark should render a
|
|
951
|
+
* `<section aria-labelledby>` around the card.
|
|
952
|
+
* - State attributes: none.
|
|
953
|
+
* - Labeling strategy: none applied by the component; consumer's
|
|
954
|
+
* heading structure inside the slot supplies the accessible name
|
|
955
|
+
* when needed.
|
|
956
|
+
* **Focus:**
|
|
957
|
+
* - On mount: nothing auto-focuses.
|
|
958
|
+
* - Focus indicator: N/A — the card itself is not focusable.
|
|
959
|
+
* **Motion:**
|
|
960
|
+
* - Animations: none.
|
|
961
|
+
* - Reduced-motion behavior: N/A.
|
|
962
|
+
* **Contrast:**
|
|
963
|
+
* - Text: inherits `color: inherit` / `currentColor`; the card does
|
|
964
|
+
* not impose a text color, so contrast is the consumer theme's
|
|
965
|
+
* responsibility.
|
|
966
|
+
* - Non-text (border, subtle background tint): both derived from
|
|
967
|
+
* `color-mix(in oklch, currentColor N%, transparent)`, matching
|
|
968
|
+
* the pattern used by `ds-input` and `ds-select`. WCAG 1.4.11
|
|
969
|
+
* (3:1 non-text contrast) is met in themes where the surface has
|
|
970
|
+
* enough delta against the page background; the component does
|
|
971
|
+
* not override the consumer's palette to force it.
|
|
972
|
+
* **Screen reader announcement:**
|
|
973
|
+
* - Rendered state: no announcement of the card itself. AT
|
|
974
|
+
* navigates to the slotted content directly, using whatever
|
|
975
|
+
* heading and landmark structure the consumer places inside.
|
|
976
|
+
* - State change: N/A.
|
|
977
|
+
*/
|
|
978
|
+
interface HTMLDsCardElement extends Components.DsCard, HTMLStencilElement {
|
|
979
|
+
}
|
|
980
|
+
var HTMLDsCardElement: {
|
|
981
|
+
prototype: HTMLDsCardElement;
|
|
982
|
+
new (): HTMLDsCardElement;
|
|
983
|
+
};
|
|
984
|
+
interface HTMLDsCheckboxElementEventMap {
|
|
985
|
+
"dsChange": boolean;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* ## Accessibility
|
|
989
|
+
* **Category:** form-field
|
|
990
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
991
|
+
* `<input type="checkbox">`; the browser already implements the
|
|
992
|
+
* checkbox role and its state semantics. Reference:
|
|
993
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
|
994
|
+
* Note: `ds-checkbox` intentionally does NOT reach for the WAI-ARIA
|
|
995
|
+
* APG "tri-state checkbox" pattern (indeterminate + on + off) as a
|
|
996
|
+
* scratch-built ARIA widget. The native input already exposes the
|
|
997
|
+
* `indeterminate` DOM property and browsers report it to AT
|
|
998
|
+
* correctly; the `indeterminate` prop here forwards to that DOM
|
|
999
|
+
* property so the native mechanism carries the semantics.
|
|
1000
|
+
* **Keyboard interactions:**
|
|
1001
|
+
* - Tab: moves focus to the native `<input type="checkbox">`.
|
|
1002
|
+
* - Space: toggles the checked state (native behavior).
|
|
1003
|
+
* - Enter: no native effect on a checkbox (unlike button). Form
|
|
1004
|
+
* submission triggered by Enter comes from the enclosing form's
|
|
1005
|
+
* default submit button, not from the checkbox itself.
|
|
1006
|
+
* **ARIA:**
|
|
1007
|
+
* - Roles used: none explicit — the native checkbox supplies the
|
|
1008
|
+
* implicit `checkbox` role.
|
|
1009
|
+
* - State attributes: `checked` (DOM property, kept in sync via
|
|
1010
|
+
* componentDidRender because Stencil does not JSX-forward the
|
|
1011
|
+
* attribute cleanly for controlled use), `disabled` (native
|
|
1012
|
+
* attribute, not `aria-disabled`), `aria-invalid` (from the
|
|
1013
|
+
* `invalid` prop), `aria-required` (from the `required` prop),
|
|
1014
|
+
* `aria-describedby` (from the `describedBy` prop).
|
|
1015
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
1016
|
+
* the internal checkbox. This is the **canonical, recommended**
|
|
1017
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
1018
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
1019
|
+
* same secondary mode documented on `ds-input`.
|
|
1020
|
+
* **Focus:**
|
|
1021
|
+
* - On mount: nothing auto-focuses.
|
|
1022
|
+
* - On toggle: focus remains on the native checkbox.
|
|
1023
|
+
* - Focus indicator: browser default `:focus-visible` ring, kept on
|
|
1024
|
+
* purpose until the DS ships a themed focus utility using
|
|
1025
|
+
* `--ds-color-focus`. Not overridden by `ds-checkbox.css`.
|
|
1026
|
+
* **Motion:**
|
|
1027
|
+
* - Animations: none.
|
|
1028
|
+
* - Reduced-motion behavior: N/A.
|
|
1029
|
+
* **Contrast:**
|
|
1030
|
+
* - Text: N/A — the checkbox itself renders no text (labels are
|
|
1031
|
+
* supplied by the consumer via `label` prop or wrapping <label>).
|
|
1032
|
+
* - Non-text (the box and its check glyph): browser default rendering
|
|
1033
|
+
* is kept on purpose (no `appearance: none` override); this
|
|
1034
|
+
* preserves the OS/browser accessibility rendering that AT and
|
|
1035
|
+
* forced-color modes rely on. A themed override is a future
|
|
1036
|
+
* decision once the DS commits to a checkbox visual language that
|
|
1037
|
+
* still passes 3:1 non-text contrast in every theme.
|
|
1038
|
+
* **Screen reader announcement:**
|
|
1039
|
+
* - Rendered state: announces the `label` prop text as the
|
|
1040
|
+
* accessible name, followed by "checkbox", followed by "checked"
|
|
1041
|
+
* / "not checked" / "mixed" per the current DOM state.
|
|
1042
|
+
* - State change: on toggle, AT announces the new state (native
|
|
1043
|
+
* behavior); no `aria-live` added.
|
|
1044
|
+
* **Consumer contract:**
|
|
1045
|
+
* - `checked` is the source of truth for controlled use. Reading
|
|
1046
|
+
* `event.detail` from `dsChange` gives the new state; the internal
|
|
1047
|
+
* input is kept in sync via a Watch + a re-sync in componentDidRender.
|
|
1048
|
+
* - `indeterminate` is a visual/AT state, not a value — it is NOT
|
|
1049
|
+
* submitted with a form and clears on the next user toggle.
|
|
1050
|
+
*/
|
|
1051
|
+
interface HTMLDsCheckboxElement extends Components.DsCheckbox, HTMLStencilElement {
|
|
1052
|
+
addEventListener<K extends keyof HTMLDsCheckboxElementEventMap>(type: K, listener: (this: HTMLDsCheckboxElement, ev: DsCheckboxCustomEvent<HTMLDsCheckboxElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1053
|
+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1054
|
+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1055
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
1056
|
+
removeEventListener<K extends keyof HTMLDsCheckboxElementEventMap>(type: K, listener: (this: HTMLDsCheckboxElement, ev: DsCheckboxCustomEvent<HTMLDsCheckboxElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
|
1057
|
+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1058
|
+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1059
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
1060
|
+
}
|
|
1061
|
+
var HTMLDsCheckboxElement: {
|
|
1062
|
+
prototype: HTMLDsCheckboxElement;
|
|
1063
|
+
new (): HTMLDsCheckboxElement;
|
|
1064
|
+
};
|
|
1065
|
+
/**
|
|
1066
|
+
* ## Accessibility
|
|
1067
|
+
* **Category:** interactive-display
|
|
1068
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
1069
|
+
* (for the internal copy control; the code readout itself is static text).
|
|
1070
|
+
* **Keyboard interactions:**
|
|
1071
|
+
* - Tab: moves focus into the internal copy `<button>`; the code readout
|
|
1072
|
+
* (`<pre><code>`) is not focusable itself, but its container is
|
|
1073
|
+
* horizontally scrollable and remains reachable via the browser's caret
|
|
1074
|
+
* browsing / find-in-page path.
|
|
1075
|
+
* - Enter: activates the copy button (writes `code` to the clipboard).
|
|
1076
|
+
* - Space: activates the copy button (native `<button>` behavior).
|
|
1077
|
+
* - Escape: N/A — the "Copied" state auto-clears after 1.4s; no dismissible
|
|
1078
|
+
* modal state.
|
|
1079
|
+
* - Arrow keys: N/A.
|
|
1080
|
+
* - Printable characters: N/A.
|
|
1081
|
+
* **ARIA:**
|
|
1082
|
+
* - Roles used: none explicit — the native `<button>` supplies the implicit
|
|
1083
|
+
* `button` role; `<pre>` and `<code>` supply their semantic roles.
|
|
1084
|
+
* - State attributes: none. The `copied` flag lives only in the button's
|
|
1085
|
+
* accessible name (see below); it is intentionally not surfaced via
|
|
1086
|
+
* `aria-pressed` because it is not a toggle — it is a transient
|
|
1087
|
+
* post-action confirmation.
|
|
1088
|
+
* - Labeling strategy: `aria-label` on the copy button — "Copy code" in the
|
|
1089
|
+
* resting state, "Copied" while the confirmation is visible. This is
|
|
1090
|
+
* authoritative because the button carries both an icon and the words
|
|
1091
|
+
* "Copy" / "Copied", and mixed icon+text needs an explicit accessible
|
|
1092
|
+
* name to avoid AT reading the SVG as a separate node.
|
|
1093
|
+
* **Focus:**
|
|
1094
|
+
* - On mount: nothing auto-focuses.
|
|
1095
|
+
* - On activate: focus stays on the copy button (clipboard write is
|
|
1096
|
+
* synchronous from the user's perspective; no focus is moved).
|
|
1097
|
+
* - On close/deactivate: N/A — no popup or overlay to close.
|
|
1098
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
1099
|
+
* `outline-offset`, applied to `.ds-code-block__copy:focus-visible`. The
|
|
1100
|
+
* readout itself is not focusable and has no focus indicator by design.
|
|
1101
|
+
* **Motion:**
|
|
1102
|
+
* - Animations: 120ms `color` / `border-color` ease transition on the copy
|
|
1103
|
+
* button (hover/focus).
|
|
1104
|
+
* - Reduced-motion behavior: N/A — 120ms is below the spec's 200ms
|
|
1105
|
+
* threshold, and the transition animates color only.
|
|
1106
|
+
* **Contrast:**
|
|
1107
|
+
* - Text (code readout): inherits `color: currentColor`; contrast depends
|
|
1108
|
+
* on the host page's text color vs. the block's tinted background
|
|
1109
|
+
* (`color-mix(currentColor 4%, transparent)`). Verify per site.
|
|
1110
|
+
* - Text (copy button): `color-mix(in oklch, currentColor 70%, transparent)`
|
|
1111
|
+
* at rest, `currentColor` on hover. The 70%-mixed resting color can drop
|
|
1112
|
+
* below 4.5:1 on low-contrast surfaces — audit per usage site, and lean
|
|
1113
|
+
* on the hover/focus states (both restore full `currentColor`) as the
|
|
1114
|
+
* accessible fallback.
|
|
1115
|
+
* - Non-text (button border, language tag border-bottom): both use
|
|
1116
|
+
* `color-mix(currentColor <15|12>%, transparent)` — decorative separators,
|
|
1117
|
+
* they do not convey state, so the 3:1 non-text contrast rule does not
|
|
1118
|
+
* apply. Focus and hover borders shift up to 30% for clearer edges.
|
|
1119
|
+
* **Screen reader announcement:**
|
|
1120
|
+
* - Rendered state: announces "Copy code, button" (the `aria-label`), then
|
|
1121
|
+
* the code content when the user navigates into the `<pre><code>` block
|
|
1122
|
+
* (usually announced as a code block on VoiceOver).
|
|
1123
|
+
* - State change: after clicking Copy, the button's `aria-label` flips to
|
|
1124
|
+
* "Copied"; AT will announce this on the next focus or re-read of the
|
|
1125
|
+
* button, but NOT proactively — there is no `aria-live` region. This is
|
|
1126
|
+
* deliberate per spec §"What we explicitly do NOT do" (avoid live-region
|
|
1127
|
+
* overuse), and the trade-off is that a screen-reader-only user does not
|
|
1128
|
+
* get an unsolicited confirmation. If a future review decides that
|
|
1129
|
+
* confirmation must be audible, add a visually-hidden
|
|
1130
|
+
* `aria-live="polite"` element whose text toggles on `copied`.
|
|
1131
|
+
*/
|
|
1132
|
+
interface HTMLDsCodeBlockElement extends Components.DsCodeBlock, HTMLStencilElement {
|
|
1133
|
+
}
|
|
1134
|
+
var HTMLDsCodeBlockElement: {
|
|
1135
|
+
prototype: HTMLDsCodeBlockElement;
|
|
1136
|
+
new (): HTMLDsCodeBlockElement;
|
|
1137
|
+
};
|
|
1138
|
+
interface HTMLDsDialogElementEventMap {
|
|
1139
|
+
"dsOpen": void;
|
|
1140
|
+
"dsClose": void;
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* ## Accessibility
|
|
1144
|
+
* **Category:** state-machine
|
|
1145
|
+
* **APG pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
|
|
1146
|
+
* This component wraps the **native `<dialog>` element** rather than
|
|
1147
|
+
* scratch-building the APG pattern on top of a `<div role="dialog">`.
|
|
1148
|
+
* `<dialog>` opened via `showModal()` gives us — from the browser,
|
|
1149
|
+
* verified by the platform, not by us:
|
|
1150
|
+
* - Focus trap: Tab / Shift+Tab cycle only through focusable
|
|
1151
|
+
* descendants (including slotted light-DOM descendants; the flattened
|
|
1152
|
+
* accessibility tree is what the trap operates on).
|
|
1153
|
+
* - Escape: closes the dialog and returns focus to the previously
|
|
1154
|
+
* focused element (native, not implemented here).
|
|
1155
|
+
* - Inert background: everything outside the dialog is inert while it
|
|
1156
|
+
* is open — no mouse, no keyboard, not part of the a11y tree.
|
|
1157
|
+
* - Return focus: on close, focus returns to whatever element had it
|
|
1158
|
+
* before `showModal()` — native, not implemented here.
|
|
1159
|
+
* - Initial focus: browser auto-focuses the first `autofocus` element
|
|
1160
|
+
* inside, or the first focusable if none — native.
|
|
1161
|
+
* - `role="dialog"` and `aria-modal="true"` semantics: implicit on
|
|
1162
|
+
* `<dialog>`, no ARIA needed from us.
|
|
1163
|
+
* This is the deliberate answer to the a11y spec §D warning about
|
|
1164
|
+
* scratch-building Tier 2 components: the native element removes the
|
|
1165
|
+
* "compiles, axe passes, screen-reader user gets trapped" failure
|
|
1166
|
+
* mode by delegating the state machine to the platform.
|
|
1167
|
+
* **Keyboard interactions (all handled by the native element unless
|
|
1168
|
+
* noted otherwise):**
|
|
1169
|
+
* - Tab: cycles focus within the dialog's focusable descendants
|
|
1170
|
+
* (native focus trap).
|
|
1171
|
+
* - Shift+Tab: reverse cycle (native).
|
|
1172
|
+
* - Escape: closes the dialog and fires the `close` event (native).
|
|
1173
|
+
* Consumers who need to prevent dismissal on Escape (destructive
|
|
1174
|
+
* flows: e.g. an unsaved-changes guard) must call
|
|
1175
|
+
* `event.preventDefault()` inside their own `keydown` listener on
|
|
1176
|
+
* the dialog before it reaches the browser — that is an escape
|
|
1177
|
+
* hatch, not a default we impose.
|
|
1178
|
+
* - Enter / Space: no special dialog behavior; forwarded to
|
|
1179
|
+
* whatever descendant has focus (a button will activate).
|
|
1180
|
+
* - Home / End / Arrow keys: no special dialog behavior; forwarded
|
|
1181
|
+
* to descendants.
|
|
1182
|
+
* - Printable characters: forwarded to descendants (e.g. typing
|
|
1183
|
+
* inside a slotted `<ds-input>`).
|
|
1184
|
+
* **ARIA attributes and value triggers:**
|
|
1185
|
+
* - Implicit `role="dialog"` on the native `<dialog>`.
|
|
1186
|
+
* - Implicit `aria-modal="true"` when opened via `showModal()`
|
|
1187
|
+
* (which is what this component always uses; `show()` — the
|
|
1188
|
+
* non-modal form — is intentionally not exposed in v1).
|
|
1189
|
+
* - `aria-label` set from the `label` prop onto the `<dialog>`.
|
|
1190
|
+
* Consumers who prefer an in-content title can pass a
|
|
1191
|
+
* `labelledBy` prop instead, whose value is the id of a heading
|
|
1192
|
+
* element they render inside the slot — `aria-labelledby`
|
|
1193
|
+
* resolves that id through the flattened tree.
|
|
1194
|
+
* - `aria-describedby` from the `describedBy` prop, resolving a
|
|
1195
|
+
* light-DOM id of a descriptive block (e.g. the dialog body copy
|
|
1196
|
+
* when the label is short).
|
|
1197
|
+
* **Focus lifecycle:**
|
|
1198
|
+
* - Opening: `showModal()` moves focus to the first `autofocus`
|
|
1199
|
+
* element inside, or the first focusable if none. Consumers who
|
|
1200
|
+
* want a specific initial focus target should mark that element
|
|
1201
|
+
* with `autofocus`.
|
|
1202
|
+
* - While open: focus is trapped within the flattened tree of the
|
|
1203
|
+
* `<dialog>`; Tab / Shift+Tab cycle through slotted descendants.
|
|
1204
|
+
* Focus cannot escape into the inert background.
|
|
1205
|
+
* - Closing: `close()` (either from Escape, from a form-submit with
|
|
1206
|
+
* `method="dialog"`, from a light-dismiss backdrop click handler
|
|
1207
|
+
* the consumer wires up, or from `open=false`) returns focus to
|
|
1208
|
+
* the element that had it immediately before `showModal()` was
|
|
1209
|
+
* called. The `dsClose` event fires after focus has been restored.
|
|
1210
|
+
* **ARIA labelling contract (choose one):**
|
|
1211
|
+
* - **`label`** (string) — sets `aria-label` on the dialog. Simplest
|
|
1212
|
+
* for short titles.
|
|
1213
|
+
* - **`labelledBy`** (id) — sets `aria-labelledby` to reference a
|
|
1214
|
+
* heading rendered inside the slot. Preferred when the dialog has
|
|
1215
|
+
* a visible title so the accessible name matches the visible name.
|
|
1216
|
+
* If both are omitted, axe reports `dialog-name` — the dialog has no
|
|
1217
|
+
* accessible name. The a11y test for this component asserts that at
|
|
1218
|
+
* least one of the two is provided.
|
|
1219
|
+
* **Motion:**
|
|
1220
|
+
* - Animations: none applied by the component. Consumers who want an
|
|
1221
|
+
* entrance transition can style `::backdrop` and `dialog[open]`
|
|
1222
|
+
* with their own animation, gated on `@media
|
|
1223
|
+
* (prefers-reduced-motion: no-preference)`.
|
|
1224
|
+
* - Reduced-motion behavior: N/A — no animation to collapse. If a
|
|
1225
|
+
* consumer adds one, they own the reduced-motion fallback.
|
|
1226
|
+
* **Contrast:**
|
|
1227
|
+
* - Text: inherits `color: inherit`; consumer theme owns contrast.
|
|
1228
|
+
* - Non-text: subtle bordered surface via
|
|
1229
|
+
* `color-mix(in oklch, currentColor N%, transparent)` matching
|
|
1230
|
+
* `ds-card`. Backdrop is a semi-transparent black by default.
|
|
1231
|
+
* **Screen reader announcement:**
|
|
1232
|
+
* - Rendered state (closed): dialog is not in the a11y tree.
|
|
1233
|
+
* - Opened: AT announces "dialog" plus the accessible name from
|
|
1234
|
+
* `label` / `labelledBy`, then reads the content following focus
|
|
1235
|
+
* (or the descriptive block referenced by `describedBy` first,
|
|
1236
|
+
* depending on AT).
|
|
1237
|
+
* - State change: on close, AT returns to the element that regained
|
|
1238
|
+
* focus (the trigger); no `aria-live` announcement is added.
|
|
1239
|
+
* **Consumer contract:**
|
|
1240
|
+
* - Set `open` to true to show; set to false to close. The component
|
|
1241
|
+
* syncs the DOM state via `showModal()` / `close()` in a Watch.
|
|
1242
|
+
* - Listen to `dsClose` to know when the dialog was dismissed (by
|
|
1243
|
+
* Escape, by `open=false`, or by a form submit with
|
|
1244
|
+
* `method="dialog"`). Listen to `dsOpen` for the mirror event.
|
|
1245
|
+
* - The default backdrop is opaque and NOT click-to-dismiss. Consumers
|
|
1246
|
+
* who want light-dismiss can add a click listener on the dialog
|
|
1247
|
+
* that closes when the click target is the dialog itself (not a
|
|
1248
|
+
* descendant): `if (e.target === dialog) dialog.close()`.
|
|
1249
|
+
* - Put focus targets (form fields, primary action button) inside
|
|
1250
|
+
* the slot. Use `autofocus` on the element that should receive
|
|
1251
|
+
* focus first.
|
|
1252
|
+
* **Pending manual verification (see commit message TODO):**
|
|
1253
|
+
* - VoiceOver on macOS: confirm the dialog announces its accessible
|
|
1254
|
+
* name + "dialog" on open, focus lands on the correct element,
|
|
1255
|
+
* Escape closes and focus returns to the trigger.
|
|
1256
|
+
* - Chromium high-contrast / forced-colors: confirm the surface
|
|
1257
|
+
* still reads as a bordered container with the native ::backdrop
|
|
1258
|
+
* dim.
|
|
1259
|
+
*/
|
|
1260
|
+
interface HTMLDsDialogElement extends Components.DsDialog, HTMLStencilElement {
|
|
1261
|
+
addEventListener<K extends keyof HTMLDsDialogElementEventMap>(type: K, listener: (this: HTMLDsDialogElement, ev: DsDialogCustomEvent<HTMLDsDialogElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1262
|
+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1263
|
+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1264
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
1265
|
+
removeEventListener<K extends keyof HTMLDsDialogElementEventMap>(type: K, listener: (this: HTMLDsDialogElement, ev: DsDialogCustomEvent<HTMLDsDialogElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
|
1266
|
+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1267
|
+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1268
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
1269
|
+
}
|
|
1270
|
+
var HTMLDsDialogElement: {
|
|
1271
|
+
prototype: HTMLDsDialogElement;
|
|
1272
|
+
new (): HTMLDsDialogElement;
|
|
1273
|
+
};
|
|
1274
|
+
interface HTMLDsInputElementEventMap {
|
|
1275
|
+
"dsInput": string;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* ## Accessibility
|
|
1279
|
+
* **Category:** form-field
|
|
1280
|
+
* **APG pattern:** N/A — native element. This wraps a native `<input>`;
|
|
1281
|
+
* there is no WAI-ARIA APG pattern for a plain text input because the
|
|
1282
|
+
* browser already implements it natively. Reference:
|
|
1283
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
|
|
1284
|
+
* **Keyboard interactions:**
|
|
1285
|
+
* - Tab: moves focus to the native `<input>` (shadow DOM does not
|
|
1286
|
+
* interrupt tab order).
|
|
1287
|
+
* - Printable characters: insert at the caret position (native).
|
|
1288
|
+
* - Backspace / Delete: native editing.
|
|
1289
|
+
* - Arrow keys: move the caret within the value (native); for
|
|
1290
|
+
* `type="number"` Up/Down step the numeric value (native).
|
|
1291
|
+
* - Enter: on `type="search"` may submit the enclosing form (native);
|
|
1292
|
+
* no custom handling.
|
|
1293
|
+
* - Escape: no native meaning for a text input.
|
|
1294
|
+
* **ARIA:**
|
|
1295
|
+
* - Roles used: none explicit — the native `<input>` supplies its
|
|
1296
|
+
* implicit role (`textbox`, `searchbox`, etc. depending on `type`).
|
|
1297
|
+
* - State attributes: `disabled` (native attribute, not `aria-disabled`),
|
|
1298
|
+
* `aria-invalid` (from the `invalid` prop), `aria-required` (from the
|
|
1299
|
+
* `required` prop), `aria-describedby` (from the `describedBy` prop,
|
|
1300
|
+
* expected to reference a light-DOM node id — typically a hint or
|
|
1301
|
+
* error message rendered by the consumer alongside the field).
|
|
1302
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto the
|
|
1303
|
+
* internal `<input>`. This is the **canonical, recommended** mode —
|
|
1304
|
+
* explicit and independent of DOM structure. Consumers MUST pass
|
|
1305
|
+
* `label` (or otherwise give the field an accessible name) — an
|
|
1306
|
+
* `<input>` with no accessible name is a WCAG failure (axe rule
|
|
1307
|
+
* `label`). Wrapping the host in a light-DOM `<label>` (e.g.
|
|
1308
|
+
* `<label>Email <ds-input></ds-input></label>`) also produces an
|
|
1309
|
+
* accessible name in Chromium via flattened-tree resolution — the
|
|
1310
|
+
* same secondary mode documented on `ds-select`, verified in this
|
|
1311
|
+
* component's e2e tests. Setting `aria-label` directly on the
|
|
1312
|
+
* `<ds-input>` host does NOT work: shadow-DOM attribute forwarding
|
|
1313
|
+
* would be required — use the `label` prop instead.
|
|
1314
|
+
* **Focus:**
|
|
1315
|
+
* - On mount: nothing auto-focuses.
|
|
1316
|
+
* - On value change: focus remains on the native `<input>`.
|
|
1317
|
+
* - Focus indicator: the browser default `:focus-visible` ring is kept
|
|
1318
|
+
* on purpose — a themed focus ring using `--ds-color-focus` is a
|
|
1319
|
+
* future decision once the DS ships a shared focus utility. Not
|
|
1320
|
+
* overridden by `ds-input.css`.
|
|
1321
|
+
* **Motion:**
|
|
1322
|
+
* - Animations: none.
|
|
1323
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
1324
|
+
* **Contrast:**
|
|
1325
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends on
|
|
1326
|
+
* the consumer's theme, not verified in isolation here.
|
|
1327
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
1328
|
+
* matching `ds-select` — inherits whatever contrast the host page
|
|
1329
|
+
* sets. WCAG 1.4.11 (3:1 non-text) is the consumer's responsibility
|
|
1330
|
+
* in themes that override the surface.
|
|
1331
|
+
* **Screen reader announcement:**
|
|
1332
|
+
* - Rendered state: announces the `label` prop text as the accessible
|
|
1333
|
+
* name, followed by the browser/AT's role announcement for the
|
|
1334
|
+
* input type ("edit text", "search text", "password", "phone
|
|
1335
|
+
* number", "email"…). When `required=true` the announcement
|
|
1336
|
+
* includes "required"; when `invalid=true` it includes "invalid".
|
|
1337
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
1338
|
+
* character (native input behavior); no `aria-live` added, per the
|
|
1339
|
+
* spec's guidance against live-region overuse.
|
|
1340
|
+
* **Consumer contract:**
|
|
1341
|
+
* - `placeholder` is a hint, not a label. It disappears on focus in
|
|
1342
|
+
* most browsers and is not reliably announced by every AT — always
|
|
1343
|
+
* pair with `label`.
|
|
1344
|
+
* - For `type="password"` in a real form, pair with a light-DOM
|
|
1345
|
+
* `<input type="hidden">` or a form-associated ancestor that gives
|
|
1346
|
+
* the browser's password manager an `autocomplete` hint. This
|
|
1347
|
+
* component intentionally does not expose `autocomplete` in v1;
|
|
1348
|
+
* consumers that need it can render a native `<input>` for that
|
|
1349
|
+
* field until the prop is added.
|
|
1350
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
1351
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
1352
|
+
* status but not the reason.
|
|
1353
|
+
*/
|
|
1354
|
+
interface HTMLDsInputElement extends Components.DsInput, HTMLStencilElement {
|
|
1355
|
+
addEventListener<K extends keyof HTMLDsInputElementEventMap>(type: K, listener: (this: HTMLDsInputElement, ev: DsInputCustomEvent<HTMLDsInputElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1356
|
+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1357
|
+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1358
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
1359
|
+
removeEventListener<K extends keyof HTMLDsInputElementEventMap>(type: K, listener: (this: HTMLDsInputElement, ev: DsInputCustomEvent<HTMLDsInputElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
|
1360
|
+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1361
|
+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1362
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
1363
|
+
}
|
|
1364
|
+
var HTMLDsInputElement: {
|
|
1365
|
+
prototype: HTMLDsInputElement;
|
|
1366
|
+
new (): HTMLDsInputElement;
|
|
1367
|
+
};
|
|
1368
|
+
/**
|
|
1369
|
+
* ## Accessibility
|
|
1370
|
+
* **Category:** static-display (indeterminate progress indicator)
|
|
1371
|
+
* **APG pattern:** N/A — the loader is a passive presence indicator, not a
|
|
1372
|
+
* `progressbar` (no measurable value). Uses `role="status"` per WAI-ARIA
|
|
1373
|
+
* live-region semantics for indeterminate loading states.
|
|
1374
|
+
* **Keyboard interactions:**
|
|
1375
|
+
* - Tab: N/A — not focusable.
|
|
1376
|
+
* - Enter: N/A.
|
|
1377
|
+
* - Escape: N/A.
|
|
1378
|
+
* - Arrow keys: N/A.
|
|
1379
|
+
* - Printable characters: N/A.
|
|
1380
|
+
* **ARIA:**
|
|
1381
|
+
* - Roles used: `role="status"` on the host — an implicit polite live
|
|
1382
|
+
* region ARIA lists as valid for "advisory information" including
|
|
1383
|
+
* in-progress operations. The inner `<svg>` is decorative and carries
|
|
1384
|
+
* `aria-hidden="true"` + `focusable="false"` so it never becomes a
|
|
1385
|
+
* parallel target for screen readers.
|
|
1386
|
+
* - State attributes: none. The loader has no state changes to announce
|
|
1387
|
+
* beyond its own presence; the polite live region on mount is the
|
|
1388
|
+
* announcement.
|
|
1389
|
+
* - Labeling strategy: `aria-label` from the `label` prop (default
|
|
1390
|
+
* "Loading"). Consumers pass a more specific string when the context
|
|
1391
|
+
* warrants ("Loading ceremonies", "Signing in", …).
|
|
1392
|
+
* **Focus:**
|
|
1393
|
+
* - On mount: nothing auto-focuses.
|
|
1394
|
+
* - On activate/open: N/A — not interactive.
|
|
1395
|
+
* - On close/deactivate: N/A.
|
|
1396
|
+
* - Focus indicator: N/A — the host is not focusable.
|
|
1397
|
+
* **Motion:**
|
|
1398
|
+
* - Animations: two mirrored halves of the logo path oscillate
|
|
1399
|
+
* symmetrically around the visual center, meeting at the ring midpoint
|
|
1400
|
+
* and separating again. Cycle 1.4s by default (`speed="normal"`);
|
|
1401
|
+
* `speed="slow"` = 2.2s, `speed="fast"` = 0.9s.
|
|
1402
|
+
* - Reduced-motion behavior: `@media (prefers-reduced-motion: reduce)`
|
|
1403
|
+
* collapses both halves to a static rendering with a slow opacity
|
|
1404
|
+
* pulse (2s ease-in-out). No rotation, no lateral movement.
|
|
1405
|
+
* **Contrast:**
|
|
1406
|
+
* - Text: N/A — the visible surface is the SVG stroke, not text.
|
|
1407
|
+
* - Non-text: stroke inherits `currentColor` from the host. Consumers are
|
|
1408
|
+
* responsible for confirming the stroke against its background meets
|
|
1409
|
+
* the ≥ 3:1 non-text contrast threshold (WCAG 1.4.11) where the loader
|
|
1410
|
+
* conveys state (e.g. distinguishing "still loading" from "idle").
|
|
1411
|
+
* **Screen reader announcement:**
|
|
1412
|
+
* - Rendered state: the polite live region announces the label
|
|
1413
|
+
* (e.g. "Loading") when the loader mounts.
|
|
1414
|
+
* - State change: N/A — the loader itself has no state that changes.
|
|
1415
|
+
* Consumers unmount the loader when the awaited work completes; that
|
|
1416
|
+
* removal is silent, which is the intended behavior (the arriving
|
|
1417
|
+
* content is the announcement).
|
|
1418
|
+
*/
|
|
1419
|
+
interface HTMLDsLoaderElement extends Components.DsLoader, HTMLStencilElement {
|
|
1420
|
+
}
|
|
1421
|
+
var HTMLDsLoaderElement: {
|
|
1422
|
+
prototype: HTMLDsLoaderElement;
|
|
1423
|
+
new (): HTMLDsLoaderElement;
|
|
1424
|
+
};
|
|
1425
|
+
interface HTMLDsSelectElementEventMap {
|
|
1426
|
+
"dsChange": string;
|
|
1427
|
+
}
|
|
1428
|
+
interface HTMLDsSelectElement extends Components.DsSelect, HTMLStencilElement {
|
|
1429
|
+
addEventListener<K extends keyof HTMLDsSelectElementEventMap>(type: K, listener: (this: HTMLDsSelectElement, ev: DsSelectCustomEvent<HTMLDsSelectElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1430
|
+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1431
|
+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1432
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
1433
|
+
removeEventListener<K extends keyof HTMLDsSelectElementEventMap>(type: K, listener: (this: HTMLDsSelectElement, ev: DsSelectCustomEvent<HTMLDsSelectElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
|
1434
|
+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1435
|
+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1436
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
1437
|
+
}
|
|
1438
|
+
var HTMLDsSelectElement: {
|
|
1439
|
+
prototype: HTMLDsSelectElement;
|
|
1440
|
+
new (): HTMLDsSelectElement;
|
|
1441
|
+
};
|
|
1442
|
+
interface HTMLDsTabsElementEventMap {
|
|
1443
|
+
"dsChange": string;
|
|
1444
|
+
}
|
|
1445
|
+
interface HTMLDsTabsElement extends Components.DsTabs, HTMLStencilElement {
|
|
1446
|
+
addEventListener<K extends keyof HTMLDsTabsElementEventMap>(type: K, listener: (this: HTMLDsTabsElement, ev: DsTabsCustomEvent<HTMLDsTabsElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1447
|
+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1448
|
+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1449
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
1450
|
+
removeEventListener<K extends keyof HTMLDsTabsElementEventMap>(type: K, listener: (this: HTMLDsTabsElement, ev: DsTabsCustomEvent<HTMLDsTabsElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
|
1451
|
+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1452
|
+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1453
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
1454
|
+
}
|
|
1455
|
+
var HTMLDsTabsElement: {
|
|
1456
|
+
prototype: HTMLDsTabsElement;
|
|
1457
|
+
new (): HTMLDsTabsElement;
|
|
1458
|
+
};
|
|
1459
|
+
interface HTMLDsTextareaElementEventMap {
|
|
1460
|
+
"dsInput": string;
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* ## Accessibility
|
|
1464
|
+
* **Category:** form-field
|
|
1465
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
1466
|
+
* `<textarea>`; there is no WAI-ARIA APG pattern for a plain
|
|
1467
|
+
* multi-line text field because the browser already implements it
|
|
1468
|
+
* natively. Reference:
|
|
1469
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
|
|
1470
|
+
* **Keyboard interactions:**
|
|
1471
|
+
* - Tab: moves focus to the native `<textarea>`; Tab inside the
|
|
1472
|
+
* textarea inserts a tab character only when the browser is
|
|
1473
|
+
* configured to do so (default behavior: Tab moves focus out).
|
|
1474
|
+
* - Printable characters: insert at the caret position (native).
|
|
1475
|
+
* - Enter: inserts a newline within the value (native), does not
|
|
1476
|
+
* submit the enclosing form (unlike a single-line input).
|
|
1477
|
+
* - Backspace / Delete: native editing.
|
|
1478
|
+
* - Arrow keys: move the caret within the value (native).
|
|
1479
|
+
* - Escape: no native meaning for a text area.
|
|
1480
|
+
* **ARIA:**
|
|
1481
|
+
* - Roles used: none explicit — the native `<textarea>` supplies
|
|
1482
|
+
* the implicit `textbox` role with multiline behavior.
|
|
1483
|
+
* - State attributes: `disabled` (native attribute, not
|
|
1484
|
+
* `aria-disabled`), `aria-invalid` (from the `invalid` prop),
|
|
1485
|
+
* `aria-required` (from the `required` prop), `aria-describedby`
|
|
1486
|
+
* (from the `describedBy` prop, expected to reference a light-DOM
|
|
1487
|
+
* node id — typically a hint or error message rendered by the
|
|
1488
|
+
* consumer alongside the field).
|
|
1489
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
1490
|
+
* the internal `<textarea>`. This is the **canonical, recommended**
|
|
1491
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
1492
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
1493
|
+
* same secondary mode documented on `ds-input` and `ds-select`,
|
|
1494
|
+
* verified in this component's e2e tests. Setting `aria-label`
|
|
1495
|
+
* directly on the `<ds-textarea>` host does NOT work: shadow-DOM
|
|
1496
|
+
* attribute forwarding would be required — use the `label` prop.
|
|
1497
|
+
* **Focus:**
|
|
1498
|
+
* - On mount: nothing auto-focuses.
|
|
1499
|
+
* - On value change: focus remains on the native `<textarea>`.
|
|
1500
|
+
* - Focus indicator: the browser default `:focus-visible` ring is
|
|
1501
|
+
* kept on purpose — a themed focus ring using `--ds-color-focus`
|
|
1502
|
+
* is a future decision once the DS ships a shared focus utility.
|
|
1503
|
+
* **Motion:**
|
|
1504
|
+
* - Animations: none.
|
|
1505
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
1506
|
+
* **Contrast:**
|
|
1507
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends
|
|
1508
|
+
* on the consumer's theme, not verified in isolation.
|
|
1509
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
1510
|
+
* matching `ds-input`.
|
|
1511
|
+
* **Screen reader announcement:**
|
|
1512
|
+
* - Rendered state: announces the `label` prop text as the
|
|
1513
|
+
* accessible name, followed by "edit text, multiline" (browser/AT
|
|
1514
|
+
* wording varies). When `required=true` the announcement includes
|
|
1515
|
+
* "required"; when `invalid=true` it includes "invalid".
|
|
1516
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
1517
|
+
* character (native behavior); no `aria-live` added.
|
|
1518
|
+
* **Consumer contract:**
|
|
1519
|
+
* - `placeholder` is a hint, not a label — always pair with `label`.
|
|
1520
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
1521
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
1522
|
+
* status but not the reason.
|
|
1523
|
+
* - `rows` sets the initial visible line count. Vertical resize is
|
|
1524
|
+
* left to the native default (`resize: vertical`) so users can
|
|
1525
|
+
* grow the field for long entries; the consumer's CSS can pin it
|
|
1526
|
+
* with `resize: none` when a fixed height matters.
|
|
1527
|
+
*/
|
|
1528
|
+
interface HTMLDsTextareaElement extends Components.DsTextarea, HTMLStencilElement {
|
|
1529
|
+
addEventListener<K extends keyof HTMLDsTextareaElementEventMap>(type: K, listener: (this: HTMLDsTextareaElement, ev: DsTextareaCustomEvent<HTMLDsTextareaElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1530
|
+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1531
|
+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
1532
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
1533
|
+
removeEventListener<K extends keyof HTMLDsTextareaElementEventMap>(type: K, listener: (this: HTMLDsTextareaElement, ev: DsTextareaCustomEvent<HTMLDsTextareaElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
|
1534
|
+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1535
|
+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
|
1536
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
1537
|
+
}
|
|
1538
|
+
var HTMLDsTextareaElement: {
|
|
1539
|
+
prototype: HTMLDsTextareaElement;
|
|
1540
|
+
new (): HTMLDsTextareaElement;
|
|
1541
|
+
};
|
|
1542
|
+
interface HTMLDsTooltipElement extends Components.DsTooltip, HTMLStencilElement {
|
|
1543
|
+
}
|
|
1544
|
+
var HTMLDsTooltipElement: {
|
|
1545
|
+
prototype: HTMLDsTooltipElement;
|
|
1546
|
+
new (): HTMLDsTooltipElement;
|
|
1547
|
+
};
|
|
1548
|
+
interface HTMLElementTagNameMap {
|
|
1549
|
+
"ds-badge": HTMLDsBadgeElement;
|
|
1550
|
+
"ds-button": HTMLDsButtonElement;
|
|
1551
|
+
"ds-card": HTMLDsCardElement;
|
|
1552
|
+
"ds-checkbox": HTMLDsCheckboxElement;
|
|
1553
|
+
"ds-code-block": HTMLDsCodeBlockElement;
|
|
1554
|
+
"ds-dialog": HTMLDsDialogElement;
|
|
1555
|
+
"ds-input": HTMLDsInputElement;
|
|
1556
|
+
"ds-loader": HTMLDsLoaderElement;
|
|
1557
|
+
"ds-select": HTMLDsSelectElement;
|
|
1558
|
+
"ds-tabs": HTMLDsTabsElement;
|
|
1559
|
+
"ds-textarea": HTMLDsTextareaElement;
|
|
1560
|
+
"ds-tooltip": HTMLDsTooltipElement;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
declare namespace LocalJSX {
|
|
1564
|
+
type OneOf<K extends string, PropT, AttrT = PropT> = { [P in K]: PropT } & { [P in `attr:${K}` | `prop:${K}`]?: never } | { [P in `attr:${K}`]: AttrT } & { [P in K | `prop:${K}`]?: never } | { [P in `prop:${K}`]: PropT } & { [P in K | `attr:${K}`]?: never };
|
|
1565
|
+
|
|
1566
|
+
/**
|
|
1567
|
+
* ## Accessibility
|
|
1568
|
+
* **Category:** static-display
|
|
1569
|
+
* **APG pattern:** N/A — non-interactive text label.
|
|
1570
|
+
* **Keyboard interactions:**
|
|
1571
|
+
* - Tab: N/A — the badge is not focusable and does not appear in tab order.
|
|
1572
|
+
* - Enter: N/A.
|
|
1573
|
+
* - Escape: N/A.
|
|
1574
|
+
* - Arrow keys: N/A.
|
|
1575
|
+
* - Printable characters: N/A.
|
|
1576
|
+
* **ARIA:**
|
|
1577
|
+
* - Roles used: none — renders a plain `<span>` around a `<slot />`. The
|
|
1578
|
+
* `variant` prop is a purely visual axis and MUST NOT be the sole carrier
|
|
1579
|
+
* of information (color/style alone fails WCAG 1.4.1 Use of Color); the
|
|
1580
|
+
* textual content inside the slot must convey the same meaning.
|
|
1581
|
+
* - State attributes: none.
|
|
1582
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers place the
|
|
1583
|
+
* readable label directly inside the tag.
|
|
1584
|
+
* **Focus:**
|
|
1585
|
+
* - On mount: nothing auto-focuses.
|
|
1586
|
+
* - On activate/open: N/A — non-interactive.
|
|
1587
|
+
* - On close/deactivate: N/A.
|
|
1588
|
+
* - Focus indicator: N/A — the host is not focusable. If a consumer wraps
|
|
1589
|
+
* `ds-badge` in an interactive element (e.g. `<button>`, `<a>`), that
|
|
1590
|
+
* wrapper owns the focus indicator via the DS `--ds-color-focus` token
|
|
1591
|
+
* and its own `:focus-visible` rule.
|
|
1592
|
+
* **Motion:**
|
|
1593
|
+
* - Animations: none.
|
|
1594
|
+
* - Reduced-motion behavior: N/A.
|
|
1595
|
+
* **Contrast:**
|
|
1596
|
+
* - Text: inherits `color: inherit` / `currentColor` from the host page and
|
|
1597
|
+
* the variant-specific style; not verified in isolation here. Consumers
|
|
1598
|
+
* are responsible for confirming ≥ 4.5:1 (WCAG 1.4.3) against the surface
|
|
1599
|
+
* the badge is placed on.
|
|
1600
|
+
* - Non-text: no state-conveying icons or borders that need the 3:1
|
|
1601
|
+
* non-text contrast threshold.
|
|
1602
|
+
* **Screen reader announcement:**
|
|
1603
|
+
* - Rendered state: announces the slot text as inline content within its
|
|
1604
|
+
* surrounding context (no role means AT reads it as plain text). The
|
|
1605
|
+
* `variant` value is not announced — that is intentional; the label text
|
|
1606
|
+
* is the source of truth for meaning.
|
|
1607
|
+
* - State change: N/A — no dynamic state owned by this component.
|
|
1608
|
+
*/
|
|
1609
|
+
interface DsBadge {
|
|
1610
|
+
/**
|
|
1611
|
+
* @default "neutral"
|
|
1612
|
+
*/
|
|
1613
|
+
"variant"?: | "neutral"
|
|
1614
|
+
| "ui-component"
|
|
1615
|
+
| "recipe"
|
|
1616
|
+
| "agent"
|
|
1617
|
+
| "config";
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* ## Accessibility
|
|
1621
|
+
* **Category:** interactive-display
|
|
1622
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
1623
|
+
* (satisfied natively by the underlying `<button>` element). When `href` is
|
|
1624
|
+
* set, the component renders `<a href>` instead — a link, not a button — and
|
|
1625
|
+
* that surface follows native `<a>` semantics rather than the Button pattern.
|
|
1626
|
+
* **Keyboard interactions:**
|
|
1627
|
+
* - Tab: moves focus to the internal `<button>` or `<a>` (shadow DOM does not
|
|
1628
|
+
* interrupt tab order).
|
|
1629
|
+
* - Enter: activates (fires click on `<button>`, navigates on `<a>`).
|
|
1630
|
+
* - Space: activates the `<button>` variant only (native behavior); on the
|
|
1631
|
+
* `<a>` variant Space scrolls the page as usual.
|
|
1632
|
+
* - Escape: N/A — no dismissible state owned by this component.
|
|
1633
|
+
* - Arrow keys: N/A.
|
|
1634
|
+
* - Printable characters: N/A.
|
|
1635
|
+
* **ARIA:**
|
|
1636
|
+
* - Roles used: none explicit — the native `<button>` / `<a href>` supplies
|
|
1637
|
+
* the implicit `button` / `link` role.
|
|
1638
|
+
* - State attributes: `disabled` (native attribute on `<button>`, not
|
|
1639
|
+
* `aria-disabled`). The `<a>` variant does not accept `disabled` — a
|
|
1640
|
+
* disabled navigation is a spec smell (see spec §"Button vs Link"); if a
|
|
1641
|
+
* link needs a disabled state, model it as a `<button>` action instead.
|
|
1642
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers MUST place
|
|
1643
|
+
* readable text (or a labeled icon) inside the tag. An empty slot leaves
|
|
1644
|
+
* the button/link without an accessible name and fails
|
|
1645
|
+
* `button-name` / `link-name` axe rules. Icon-only usage is out of contract
|
|
1646
|
+
* for `ds-button`; use a future `ds-icon-button` with a required
|
|
1647
|
+
* `aria-label` prop when that lands.
|
|
1648
|
+
* **Focus:**
|
|
1649
|
+
* - On mount: nothing auto-focuses.
|
|
1650
|
+
* - On activate: focus stays on the element (native click behavior; the
|
|
1651
|
+
* component does not move focus on its own).
|
|
1652
|
+
* - On close/deactivate: N/A.
|
|
1653
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
1654
|
+
* `outline-offset`, applied via `:focus-visible` — replaces the browser
|
|
1655
|
+
* default so both `<button>` and `<a>` variants use the same DS ring
|
|
1656
|
+
* (spec universal rule 3, WCAG 1.4.11 non-text contrast ≥ 3:1).
|
|
1657
|
+
* **Motion:**
|
|
1658
|
+
* - Animations: 150ms `background-color` ease-in-out transition on
|
|
1659
|
+
* `primary`/`secondary` variants (hover).
|
|
1660
|
+
* - Reduced-motion behavior: N/A — 150ms is below the spec's 200ms threshold
|
|
1661
|
+
* for `prefers-reduced-motion` handling, and the transition animates color
|
|
1662
|
+
* only (no motion of position, scale, or opacity).
|
|
1663
|
+
* **Contrast:**
|
|
1664
|
+
* - Text: `primary` and `secondary` variants pair `--ds-color-{primary,secondary}`
|
|
1665
|
+
* with `--ds-color-text-on-{primary,secondary}` — the token pairs are
|
|
1666
|
+
* authored to meet WCAG 2.1 AA (≥ 4.5:1). The `ghost` variant inherits
|
|
1667
|
+
* `color: inherit` and pins `opacity: 0.85`, so its effective contrast
|
|
1668
|
+
* depends on the consumer's surface — verify per site.
|
|
1669
|
+
* - Non-text (border on `ghost`): 1px `currentColor`; contrast tracks the
|
|
1670
|
+
* text color 1:1, subject to the same host-page caveat.
|
|
1671
|
+
* **Screen reader announcement:**
|
|
1672
|
+
* - Rendered state: announces the slot text followed by "button" (or "link"
|
|
1673
|
+
* for the `href` variant), plus "dimmed" / "unavailable" when `disabled`
|
|
1674
|
+
* is set (browser/AT dependent wording).
|
|
1675
|
+
* - State change: N/A — this component does not own toggle state. A pressed
|
|
1676
|
+
* toggle needs `aria-pressed`, which is out of scope here; model that as
|
|
1677
|
+
* a distinct component or extend the API deliberately per the spec.
|
|
1678
|
+
*/
|
|
1679
|
+
interface DsButton {
|
|
1680
|
+
/**
|
|
1681
|
+
* @default false
|
|
1682
|
+
*/
|
|
1683
|
+
"disabled"?: boolean;
|
|
1684
|
+
"href"?: string;
|
|
1685
|
+
/**
|
|
1686
|
+
* @default "md"
|
|
1687
|
+
*/
|
|
1688
|
+
"size"?: "md" | "lg";
|
|
1689
|
+
/**
|
|
1690
|
+
* @default "button"
|
|
1691
|
+
*/
|
|
1692
|
+
"type"?: "button" | "submit" | "reset";
|
|
1693
|
+
/**
|
|
1694
|
+
* @default "primary"
|
|
1695
|
+
*/
|
|
1696
|
+
"variant"?: "primary" | "secondary" | "ghost";
|
|
1697
|
+
}
|
|
1698
|
+
/**
|
|
1699
|
+
* ## Accessibility
|
|
1700
|
+
* **Category:** static-display
|
|
1701
|
+
* **APG pattern:** N/A — a container surface with no interactive
|
|
1702
|
+
* behavior of its own. If a consumer nests interactive elements
|
|
1703
|
+
* inside (button, link, form field), those elements satisfy their
|
|
1704
|
+
* own a11y bucket independently.
|
|
1705
|
+
* **Keyboard interactions:**
|
|
1706
|
+
* - Tab / Shift+Tab: N/A — the card itself is not focusable. Any
|
|
1707
|
+
* interactive descendant carries its own tab semantics.
|
|
1708
|
+
* - Enter / Space / Arrow / Escape / Printable: N/A.
|
|
1709
|
+
* **ARIA:**
|
|
1710
|
+
* - Roles used: none. `<ds-card>` is a purely visual grouping and
|
|
1711
|
+
* deliberately does not carry `role="region"` — that would demand
|
|
1712
|
+
* a labelledby target, which would only be correct in a subset of
|
|
1713
|
+
* uses. Consumers that need a labeled landmark should render a
|
|
1714
|
+
* `<section aria-labelledby>` around the card.
|
|
1715
|
+
* - State attributes: none.
|
|
1716
|
+
* - Labeling strategy: none applied by the component; consumer's
|
|
1717
|
+
* heading structure inside the slot supplies the accessible name
|
|
1718
|
+
* when needed.
|
|
1719
|
+
* **Focus:**
|
|
1720
|
+
* - On mount: nothing auto-focuses.
|
|
1721
|
+
* - Focus indicator: N/A — the card itself is not focusable.
|
|
1722
|
+
* **Motion:**
|
|
1723
|
+
* - Animations: none.
|
|
1724
|
+
* - Reduced-motion behavior: N/A.
|
|
1725
|
+
* **Contrast:**
|
|
1726
|
+
* - Text: inherits `color: inherit` / `currentColor`; the card does
|
|
1727
|
+
* not impose a text color, so contrast is the consumer theme's
|
|
1728
|
+
* responsibility.
|
|
1729
|
+
* - Non-text (border, subtle background tint): both derived from
|
|
1730
|
+
* `color-mix(in oklch, currentColor N%, transparent)`, matching
|
|
1731
|
+
* the pattern used by `ds-input` and `ds-select`. WCAG 1.4.11
|
|
1732
|
+
* (3:1 non-text contrast) is met in themes where the surface has
|
|
1733
|
+
* enough delta against the page background; the component does
|
|
1734
|
+
* not override the consumer's palette to force it.
|
|
1735
|
+
* **Screen reader announcement:**
|
|
1736
|
+
* - Rendered state: no announcement of the card itself. AT
|
|
1737
|
+
* navigates to the slotted content directly, using whatever
|
|
1738
|
+
* heading and landmark structure the consumer places inside.
|
|
1739
|
+
* - State change: N/A.
|
|
1740
|
+
*/
|
|
1741
|
+
interface DsCard {
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* ## Accessibility
|
|
1745
|
+
* **Category:** form-field
|
|
1746
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
1747
|
+
* `<input type="checkbox">`; the browser already implements the
|
|
1748
|
+
* checkbox role and its state semantics. Reference:
|
|
1749
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
|
1750
|
+
* Note: `ds-checkbox` intentionally does NOT reach for the WAI-ARIA
|
|
1751
|
+
* APG "tri-state checkbox" pattern (indeterminate + on + off) as a
|
|
1752
|
+
* scratch-built ARIA widget. The native input already exposes the
|
|
1753
|
+
* `indeterminate` DOM property and browsers report it to AT
|
|
1754
|
+
* correctly; the `indeterminate` prop here forwards to that DOM
|
|
1755
|
+
* property so the native mechanism carries the semantics.
|
|
1756
|
+
* **Keyboard interactions:**
|
|
1757
|
+
* - Tab: moves focus to the native `<input type="checkbox">`.
|
|
1758
|
+
* - Space: toggles the checked state (native behavior).
|
|
1759
|
+
* - Enter: no native effect on a checkbox (unlike button). Form
|
|
1760
|
+
* submission triggered by Enter comes from the enclosing form's
|
|
1761
|
+
* default submit button, not from the checkbox itself.
|
|
1762
|
+
* **ARIA:**
|
|
1763
|
+
* - Roles used: none explicit — the native checkbox supplies the
|
|
1764
|
+
* implicit `checkbox` role.
|
|
1765
|
+
* - State attributes: `checked` (DOM property, kept in sync via
|
|
1766
|
+
* componentDidRender because Stencil does not JSX-forward the
|
|
1767
|
+
* attribute cleanly for controlled use), `disabled` (native
|
|
1768
|
+
* attribute, not `aria-disabled`), `aria-invalid` (from the
|
|
1769
|
+
* `invalid` prop), `aria-required` (from the `required` prop),
|
|
1770
|
+
* `aria-describedby` (from the `describedBy` prop).
|
|
1771
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
1772
|
+
* the internal checkbox. This is the **canonical, recommended**
|
|
1773
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
1774
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
1775
|
+
* same secondary mode documented on `ds-input`.
|
|
1776
|
+
* **Focus:**
|
|
1777
|
+
* - On mount: nothing auto-focuses.
|
|
1778
|
+
* - On toggle: focus remains on the native checkbox.
|
|
1779
|
+
* - Focus indicator: browser default `:focus-visible` ring, kept on
|
|
1780
|
+
* purpose until the DS ships a themed focus utility using
|
|
1781
|
+
* `--ds-color-focus`. Not overridden by `ds-checkbox.css`.
|
|
1782
|
+
* **Motion:**
|
|
1783
|
+
* - Animations: none.
|
|
1784
|
+
* - Reduced-motion behavior: N/A.
|
|
1785
|
+
* **Contrast:**
|
|
1786
|
+
* - Text: N/A — the checkbox itself renders no text (labels are
|
|
1787
|
+
* supplied by the consumer via `label` prop or wrapping <label>).
|
|
1788
|
+
* - Non-text (the box and its check glyph): browser default rendering
|
|
1789
|
+
* is kept on purpose (no `appearance: none` override); this
|
|
1790
|
+
* preserves the OS/browser accessibility rendering that AT and
|
|
1791
|
+
* forced-color modes rely on. A themed override is a future
|
|
1792
|
+
* decision once the DS commits to a checkbox visual language that
|
|
1793
|
+
* still passes 3:1 non-text contrast in every theme.
|
|
1794
|
+
* **Screen reader announcement:**
|
|
1795
|
+
* - Rendered state: announces the `label` prop text as the
|
|
1796
|
+
* accessible name, followed by "checkbox", followed by "checked"
|
|
1797
|
+
* / "not checked" / "mixed" per the current DOM state.
|
|
1798
|
+
* - State change: on toggle, AT announces the new state (native
|
|
1799
|
+
* behavior); no `aria-live` added.
|
|
1800
|
+
* **Consumer contract:**
|
|
1801
|
+
* - `checked` is the source of truth for controlled use. Reading
|
|
1802
|
+
* `event.detail` from `dsChange` gives the new state; the internal
|
|
1803
|
+
* input is kept in sync via a Watch + a re-sync in componentDidRender.
|
|
1804
|
+
* - `indeterminate` is a visual/AT state, not a value — it is NOT
|
|
1805
|
+
* submitted with a form and clears on the next user toggle.
|
|
1806
|
+
*/
|
|
1807
|
+
interface DsCheckbox {
|
|
1808
|
+
/**
|
|
1809
|
+
* @default false
|
|
1810
|
+
*/
|
|
1811
|
+
"checked"?: boolean;
|
|
1812
|
+
"describedBy"?: string;
|
|
1813
|
+
/**
|
|
1814
|
+
* @default false
|
|
1815
|
+
*/
|
|
1816
|
+
"disabled"?: boolean;
|
|
1817
|
+
/**
|
|
1818
|
+
* @default false
|
|
1819
|
+
*/
|
|
1820
|
+
"indeterminate"?: boolean;
|
|
1821
|
+
/**
|
|
1822
|
+
* @default false
|
|
1823
|
+
*/
|
|
1824
|
+
"invalid"?: boolean;
|
|
1825
|
+
"label"?: string;
|
|
1826
|
+
"name"?: string;
|
|
1827
|
+
"onDsChange"?: (event: DsCheckboxCustomEvent<boolean>) => void;
|
|
1828
|
+
/**
|
|
1829
|
+
* @default false
|
|
1830
|
+
*/
|
|
1831
|
+
"required"?: boolean;
|
|
1832
|
+
"value"?: string;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* ## Accessibility
|
|
1836
|
+
* **Category:** interactive-display
|
|
1837
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
1838
|
+
* (for the internal copy control; the code readout itself is static text).
|
|
1839
|
+
* **Keyboard interactions:**
|
|
1840
|
+
* - Tab: moves focus into the internal copy `<button>`; the code readout
|
|
1841
|
+
* (`<pre><code>`) is not focusable itself, but its container is
|
|
1842
|
+
* horizontally scrollable and remains reachable via the browser's caret
|
|
1843
|
+
* browsing / find-in-page path.
|
|
1844
|
+
* - Enter: activates the copy button (writes `code` to the clipboard).
|
|
1845
|
+
* - Space: activates the copy button (native `<button>` behavior).
|
|
1846
|
+
* - Escape: N/A — the "Copied" state auto-clears after 1.4s; no dismissible
|
|
1847
|
+
* modal state.
|
|
1848
|
+
* - Arrow keys: N/A.
|
|
1849
|
+
* - Printable characters: N/A.
|
|
1850
|
+
* **ARIA:**
|
|
1851
|
+
* - Roles used: none explicit — the native `<button>` supplies the implicit
|
|
1852
|
+
* `button` role; `<pre>` and `<code>` supply their semantic roles.
|
|
1853
|
+
* - State attributes: none. The `copied` flag lives only in the button's
|
|
1854
|
+
* accessible name (see below); it is intentionally not surfaced via
|
|
1855
|
+
* `aria-pressed` because it is not a toggle — it is a transient
|
|
1856
|
+
* post-action confirmation.
|
|
1857
|
+
* - Labeling strategy: `aria-label` on the copy button — "Copy code" in the
|
|
1858
|
+
* resting state, "Copied" while the confirmation is visible. This is
|
|
1859
|
+
* authoritative because the button carries both an icon and the words
|
|
1860
|
+
* "Copy" / "Copied", and mixed icon+text needs an explicit accessible
|
|
1861
|
+
* name to avoid AT reading the SVG as a separate node.
|
|
1862
|
+
* **Focus:**
|
|
1863
|
+
* - On mount: nothing auto-focuses.
|
|
1864
|
+
* - On activate: focus stays on the copy button (clipboard write is
|
|
1865
|
+
* synchronous from the user's perspective; no focus is moved).
|
|
1866
|
+
* - On close/deactivate: N/A — no popup or overlay to close.
|
|
1867
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
1868
|
+
* `outline-offset`, applied to `.ds-code-block__copy:focus-visible`. The
|
|
1869
|
+
* readout itself is not focusable and has no focus indicator by design.
|
|
1870
|
+
* **Motion:**
|
|
1871
|
+
* - Animations: 120ms `color` / `border-color` ease transition on the copy
|
|
1872
|
+
* button (hover/focus).
|
|
1873
|
+
* - Reduced-motion behavior: N/A — 120ms is below the spec's 200ms
|
|
1874
|
+
* threshold, and the transition animates color only.
|
|
1875
|
+
* **Contrast:**
|
|
1876
|
+
* - Text (code readout): inherits `color: currentColor`; contrast depends
|
|
1877
|
+
* on the host page's text color vs. the block's tinted background
|
|
1878
|
+
* (`color-mix(currentColor 4%, transparent)`). Verify per site.
|
|
1879
|
+
* - Text (copy button): `color-mix(in oklch, currentColor 70%, transparent)`
|
|
1880
|
+
* at rest, `currentColor` on hover. The 70%-mixed resting color can drop
|
|
1881
|
+
* below 4.5:1 on low-contrast surfaces — audit per usage site, and lean
|
|
1882
|
+
* on the hover/focus states (both restore full `currentColor`) as the
|
|
1883
|
+
* accessible fallback.
|
|
1884
|
+
* - Non-text (button border, language tag border-bottom): both use
|
|
1885
|
+
* `color-mix(currentColor <15|12>%, transparent)` — decorative separators,
|
|
1886
|
+
* they do not convey state, so the 3:1 non-text contrast rule does not
|
|
1887
|
+
* apply. Focus and hover borders shift up to 30% for clearer edges.
|
|
1888
|
+
* **Screen reader announcement:**
|
|
1889
|
+
* - Rendered state: announces "Copy code, button" (the `aria-label`), then
|
|
1890
|
+
* the code content when the user navigates into the `<pre><code>` block
|
|
1891
|
+
* (usually announced as a code block on VoiceOver).
|
|
1892
|
+
* - State change: after clicking Copy, the button's `aria-label` flips to
|
|
1893
|
+
* "Copied"; AT will announce this on the next focus or re-read of the
|
|
1894
|
+
* button, but NOT proactively — there is no `aria-live` region. This is
|
|
1895
|
+
* deliberate per spec §"What we explicitly do NOT do" (avoid live-region
|
|
1896
|
+
* overuse), and the trade-off is that a screen-reader-only user does not
|
|
1897
|
+
* get an unsolicited confirmation. If a future review decides that
|
|
1898
|
+
* confirmation must be audible, add a visually-hidden
|
|
1899
|
+
* `aria-live="polite"` element whose text toggles on `copied`.
|
|
1900
|
+
*/
|
|
1901
|
+
interface DsCodeBlock {
|
|
1902
|
+
"code": string;
|
|
1903
|
+
/**
|
|
1904
|
+
* @default "bash"
|
|
1905
|
+
*/
|
|
1906
|
+
"language"?: string;
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1909
|
+
* ## Accessibility
|
|
1910
|
+
* **Category:** state-machine
|
|
1911
|
+
* **APG pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
|
|
1912
|
+
* This component wraps the **native `<dialog>` element** rather than
|
|
1913
|
+
* scratch-building the APG pattern on top of a `<div role="dialog">`.
|
|
1914
|
+
* `<dialog>` opened via `showModal()` gives us — from the browser,
|
|
1915
|
+
* verified by the platform, not by us:
|
|
1916
|
+
* - Focus trap: Tab / Shift+Tab cycle only through focusable
|
|
1917
|
+
* descendants (including slotted light-DOM descendants; the flattened
|
|
1918
|
+
* accessibility tree is what the trap operates on).
|
|
1919
|
+
* - Escape: closes the dialog and returns focus to the previously
|
|
1920
|
+
* focused element (native, not implemented here).
|
|
1921
|
+
* - Inert background: everything outside the dialog is inert while it
|
|
1922
|
+
* is open — no mouse, no keyboard, not part of the a11y tree.
|
|
1923
|
+
* - Return focus: on close, focus returns to whatever element had it
|
|
1924
|
+
* before `showModal()` — native, not implemented here.
|
|
1925
|
+
* - Initial focus: browser auto-focuses the first `autofocus` element
|
|
1926
|
+
* inside, or the first focusable if none — native.
|
|
1927
|
+
* - `role="dialog"` and `aria-modal="true"` semantics: implicit on
|
|
1928
|
+
* `<dialog>`, no ARIA needed from us.
|
|
1929
|
+
* This is the deliberate answer to the a11y spec §D warning about
|
|
1930
|
+
* scratch-building Tier 2 components: the native element removes the
|
|
1931
|
+
* "compiles, axe passes, screen-reader user gets trapped" failure
|
|
1932
|
+
* mode by delegating the state machine to the platform.
|
|
1933
|
+
* **Keyboard interactions (all handled by the native element unless
|
|
1934
|
+
* noted otherwise):**
|
|
1935
|
+
* - Tab: cycles focus within the dialog's focusable descendants
|
|
1936
|
+
* (native focus trap).
|
|
1937
|
+
* - Shift+Tab: reverse cycle (native).
|
|
1938
|
+
* - Escape: closes the dialog and fires the `close` event (native).
|
|
1939
|
+
* Consumers who need to prevent dismissal on Escape (destructive
|
|
1940
|
+
* flows: e.g. an unsaved-changes guard) must call
|
|
1941
|
+
* `event.preventDefault()` inside their own `keydown` listener on
|
|
1942
|
+
* the dialog before it reaches the browser — that is an escape
|
|
1943
|
+
* hatch, not a default we impose.
|
|
1944
|
+
* - Enter / Space: no special dialog behavior; forwarded to
|
|
1945
|
+
* whatever descendant has focus (a button will activate).
|
|
1946
|
+
* - Home / End / Arrow keys: no special dialog behavior; forwarded
|
|
1947
|
+
* to descendants.
|
|
1948
|
+
* - Printable characters: forwarded to descendants (e.g. typing
|
|
1949
|
+
* inside a slotted `<ds-input>`).
|
|
1950
|
+
* **ARIA attributes and value triggers:**
|
|
1951
|
+
* - Implicit `role="dialog"` on the native `<dialog>`.
|
|
1952
|
+
* - Implicit `aria-modal="true"` when opened via `showModal()`
|
|
1953
|
+
* (which is what this component always uses; `show()` — the
|
|
1954
|
+
* non-modal form — is intentionally not exposed in v1).
|
|
1955
|
+
* - `aria-label` set from the `label` prop onto the `<dialog>`.
|
|
1956
|
+
* Consumers who prefer an in-content title can pass a
|
|
1957
|
+
* `labelledBy` prop instead, whose value is the id of a heading
|
|
1958
|
+
* element they render inside the slot — `aria-labelledby`
|
|
1959
|
+
* resolves that id through the flattened tree.
|
|
1960
|
+
* - `aria-describedby` from the `describedBy` prop, resolving a
|
|
1961
|
+
* light-DOM id of a descriptive block (e.g. the dialog body copy
|
|
1962
|
+
* when the label is short).
|
|
1963
|
+
* **Focus lifecycle:**
|
|
1964
|
+
* - Opening: `showModal()` moves focus to the first `autofocus`
|
|
1965
|
+
* element inside, or the first focusable if none. Consumers who
|
|
1966
|
+
* want a specific initial focus target should mark that element
|
|
1967
|
+
* with `autofocus`.
|
|
1968
|
+
* - While open: focus is trapped within the flattened tree of the
|
|
1969
|
+
* `<dialog>`; Tab / Shift+Tab cycle through slotted descendants.
|
|
1970
|
+
* Focus cannot escape into the inert background.
|
|
1971
|
+
* - Closing: `close()` (either from Escape, from a form-submit with
|
|
1972
|
+
* `method="dialog"`, from a light-dismiss backdrop click handler
|
|
1973
|
+
* the consumer wires up, or from `open=false`) returns focus to
|
|
1974
|
+
* the element that had it immediately before `showModal()` was
|
|
1975
|
+
* called. The `dsClose` event fires after focus has been restored.
|
|
1976
|
+
* **ARIA labelling contract (choose one):**
|
|
1977
|
+
* - **`label`** (string) — sets `aria-label` on the dialog. Simplest
|
|
1978
|
+
* for short titles.
|
|
1979
|
+
* - **`labelledBy`** (id) — sets `aria-labelledby` to reference a
|
|
1980
|
+
* heading rendered inside the slot. Preferred when the dialog has
|
|
1981
|
+
* a visible title so the accessible name matches the visible name.
|
|
1982
|
+
* If both are omitted, axe reports `dialog-name` — the dialog has no
|
|
1983
|
+
* accessible name. The a11y test for this component asserts that at
|
|
1984
|
+
* least one of the two is provided.
|
|
1985
|
+
* **Motion:**
|
|
1986
|
+
* - Animations: none applied by the component. Consumers who want an
|
|
1987
|
+
* entrance transition can style `::backdrop` and `dialog[open]`
|
|
1988
|
+
* with their own animation, gated on `@media
|
|
1989
|
+
* (prefers-reduced-motion: no-preference)`.
|
|
1990
|
+
* - Reduced-motion behavior: N/A — no animation to collapse. If a
|
|
1991
|
+
* consumer adds one, they own the reduced-motion fallback.
|
|
1992
|
+
* **Contrast:**
|
|
1993
|
+
* - Text: inherits `color: inherit`; consumer theme owns contrast.
|
|
1994
|
+
* - Non-text: subtle bordered surface via
|
|
1995
|
+
* `color-mix(in oklch, currentColor N%, transparent)` matching
|
|
1996
|
+
* `ds-card`. Backdrop is a semi-transparent black by default.
|
|
1997
|
+
* **Screen reader announcement:**
|
|
1998
|
+
* - Rendered state (closed): dialog is not in the a11y tree.
|
|
1999
|
+
* - Opened: AT announces "dialog" plus the accessible name from
|
|
2000
|
+
* `label` / `labelledBy`, then reads the content following focus
|
|
2001
|
+
* (or the descriptive block referenced by `describedBy` first,
|
|
2002
|
+
* depending on AT).
|
|
2003
|
+
* - State change: on close, AT returns to the element that regained
|
|
2004
|
+
* focus (the trigger); no `aria-live` announcement is added.
|
|
2005
|
+
* **Consumer contract:**
|
|
2006
|
+
* - Set `open` to true to show; set to false to close. The component
|
|
2007
|
+
* syncs the DOM state via `showModal()` / `close()` in a Watch.
|
|
2008
|
+
* - Listen to `dsClose` to know when the dialog was dismissed (by
|
|
2009
|
+
* Escape, by `open=false`, or by a form submit with
|
|
2010
|
+
* `method="dialog"`). Listen to `dsOpen` for the mirror event.
|
|
2011
|
+
* - The default backdrop is opaque and NOT click-to-dismiss. Consumers
|
|
2012
|
+
* who want light-dismiss can add a click listener on the dialog
|
|
2013
|
+
* that closes when the click target is the dialog itself (not a
|
|
2014
|
+
* descendant): `if (e.target === dialog) dialog.close()`.
|
|
2015
|
+
* - Put focus targets (form fields, primary action button) inside
|
|
2016
|
+
* the slot. Use `autofocus` on the element that should receive
|
|
2017
|
+
* focus first.
|
|
2018
|
+
* **Pending manual verification (see commit message TODO):**
|
|
2019
|
+
* - VoiceOver on macOS: confirm the dialog announces its accessible
|
|
2020
|
+
* name + "dialog" on open, focus lands on the correct element,
|
|
2021
|
+
* Escape closes and focus returns to the trigger.
|
|
2022
|
+
* - Chromium high-contrast / forced-colors: confirm the surface
|
|
2023
|
+
* still reads as a bordered container with the native ::backdrop
|
|
2024
|
+
* dim.
|
|
2025
|
+
*/
|
|
2026
|
+
interface DsDialog {
|
|
2027
|
+
"describedBy"?: string;
|
|
2028
|
+
"label"?: string;
|
|
2029
|
+
"labelledBy"?: string;
|
|
2030
|
+
"onDsClose"?: (event: DsDialogCustomEvent<void>) => void;
|
|
2031
|
+
"onDsOpen"?: (event: DsDialogCustomEvent<void>) => void;
|
|
2032
|
+
/**
|
|
2033
|
+
* @default false
|
|
2034
|
+
*/
|
|
2035
|
+
"open"?: boolean;
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* ## Accessibility
|
|
2039
|
+
* **Category:** form-field
|
|
2040
|
+
* **APG pattern:** N/A — native element. This wraps a native `<input>`;
|
|
2041
|
+
* there is no WAI-ARIA APG pattern for a plain text input because the
|
|
2042
|
+
* browser already implements it natively. Reference:
|
|
2043
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
|
|
2044
|
+
* **Keyboard interactions:**
|
|
2045
|
+
* - Tab: moves focus to the native `<input>` (shadow DOM does not
|
|
2046
|
+
* interrupt tab order).
|
|
2047
|
+
* - Printable characters: insert at the caret position (native).
|
|
2048
|
+
* - Backspace / Delete: native editing.
|
|
2049
|
+
* - Arrow keys: move the caret within the value (native); for
|
|
2050
|
+
* `type="number"` Up/Down step the numeric value (native).
|
|
2051
|
+
* - Enter: on `type="search"` may submit the enclosing form (native);
|
|
2052
|
+
* no custom handling.
|
|
2053
|
+
* - Escape: no native meaning for a text input.
|
|
2054
|
+
* **ARIA:**
|
|
2055
|
+
* - Roles used: none explicit — the native `<input>` supplies its
|
|
2056
|
+
* implicit role (`textbox`, `searchbox`, etc. depending on `type`).
|
|
2057
|
+
* - State attributes: `disabled` (native attribute, not `aria-disabled`),
|
|
2058
|
+
* `aria-invalid` (from the `invalid` prop), `aria-required` (from the
|
|
2059
|
+
* `required` prop), `aria-describedby` (from the `describedBy` prop,
|
|
2060
|
+
* expected to reference a light-DOM node id — typically a hint or
|
|
2061
|
+
* error message rendered by the consumer alongside the field).
|
|
2062
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto the
|
|
2063
|
+
* internal `<input>`. This is the **canonical, recommended** mode —
|
|
2064
|
+
* explicit and independent of DOM structure. Consumers MUST pass
|
|
2065
|
+
* `label` (or otherwise give the field an accessible name) — an
|
|
2066
|
+
* `<input>` with no accessible name is a WCAG failure (axe rule
|
|
2067
|
+
* `label`). Wrapping the host in a light-DOM `<label>` (e.g.
|
|
2068
|
+
* `<label>Email <ds-input></ds-input></label>`) also produces an
|
|
2069
|
+
* accessible name in Chromium via flattened-tree resolution — the
|
|
2070
|
+
* same secondary mode documented on `ds-select`, verified in this
|
|
2071
|
+
* component's e2e tests. Setting `aria-label` directly on the
|
|
2072
|
+
* `<ds-input>` host does NOT work: shadow-DOM attribute forwarding
|
|
2073
|
+
* would be required — use the `label` prop instead.
|
|
2074
|
+
* **Focus:**
|
|
2075
|
+
* - On mount: nothing auto-focuses.
|
|
2076
|
+
* - On value change: focus remains on the native `<input>`.
|
|
2077
|
+
* - Focus indicator: the browser default `:focus-visible` ring is kept
|
|
2078
|
+
* on purpose — a themed focus ring using `--ds-color-focus` is a
|
|
2079
|
+
* future decision once the DS ships a shared focus utility. Not
|
|
2080
|
+
* overridden by `ds-input.css`.
|
|
2081
|
+
* **Motion:**
|
|
2082
|
+
* - Animations: none.
|
|
2083
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
2084
|
+
* **Contrast:**
|
|
2085
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends on
|
|
2086
|
+
* the consumer's theme, not verified in isolation here.
|
|
2087
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
2088
|
+
* matching `ds-select` — inherits whatever contrast the host page
|
|
2089
|
+
* sets. WCAG 1.4.11 (3:1 non-text) is the consumer's responsibility
|
|
2090
|
+
* in themes that override the surface.
|
|
2091
|
+
* **Screen reader announcement:**
|
|
2092
|
+
* - Rendered state: announces the `label` prop text as the accessible
|
|
2093
|
+
* name, followed by the browser/AT's role announcement for the
|
|
2094
|
+
* input type ("edit text", "search text", "password", "phone
|
|
2095
|
+
* number", "email"…). When `required=true` the announcement
|
|
2096
|
+
* includes "required"; when `invalid=true` it includes "invalid".
|
|
2097
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
2098
|
+
* character (native input behavior); no `aria-live` added, per the
|
|
2099
|
+
* spec's guidance against live-region overuse.
|
|
2100
|
+
* **Consumer contract:**
|
|
2101
|
+
* - `placeholder` is a hint, not a label. It disappears on focus in
|
|
2102
|
+
* most browsers and is not reliably announced by every AT — always
|
|
2103
|
+
* pair with `label`.
|
|
2104
|
+
* - For `type="password"` in a real form, pair with a light-DOM
|
|
2105
|
+
* `<input type="hidden">` or a form-associated ancestor that gives
|
|
2106
|
+
* the browser's password manager an `autocomplete` hint. This
|
|
2107
|
+
* component intentionally does not expose `autocomplete` in v1;
|
|
2108
|
+
* consumers that need it can render a native `<input>` for that
|
|
2109
|
+
* field until the prop is added.
|
|
2110
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
2111
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
2112
|
+
* status but not the reason.
|
|
2113
|
+
*/
|
|
2114
|
+
interface DsInput {
|
|
2115
|
+
"describedBy"?: string;
|
|
2116
|
+
/**
|
|
2117
|
+
* @default false
|
|
2118
|
+
*/
|
|
2119
|
+
"disabled"?: boolean;
|
|
2120
|
+
/**
|
|
2121
|
+
* @default false
|
|
2122
|
+
*/
|
|
2123
|
+
"invalid"?: boolean;
|
|
2124
|
+
"label"?: string;
|
|
2125
|
+
"name"?: string;
|
|
2126
|
+
"onDsInput"?: (event: DsInputCustomEvent<string>) => void;
|
|
2127
|
+
"placeholder"?: string;
|
|
2128
|
+
/**
|
|
2129
|
+
* @default false
|
|
2130
|
+
*/
|
|
2131
|
+
"required"?: boolean;
|
|
2132
|
+
/**
|
|
2133
|
+
* @default "md"
|
|
2134
|
+
*/
|
|
2135
|
+
"size"?: "md" | "lg";
|
|
2136
|
+
/**
|
|
2137
|
+
* @default "text"
|
|
2138
|
+
*/
|
|
2139
|
+
"type"?: | "text"
|
|
2140
|
+
| "email"
|
|
2141
|
+
| "password"
|
|
2142
|
+
| "search"
|
|
2143
|
+
| "tel"
|
|
2144
|
+
| "url"
|
|
2145
|
+
| "number";
|
|
2146
|
+
"value"?: string;
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* ## Accessibility
|
|
2150
|
+
* **Category:** static-display (indeterminate progress indicator)
|
|
2151
|
+
* **APG pattern:** N/A — the loader is a passive presence indicator, not a
|
|
2152
|
+
* `progressbar` (no measurable value). Uses `role="status"` per WAI-ARIA
|
|
2153
|
+
* live-region semantics for indeterminate loading states.
|
|
2154
|
+
* **Keyboard interactions:**
|
|
2155
|
+
* - Tab: N/A — not focusable.
|
|
2156
|
+
* - Enter: N/A.
|
|
2157
|
+
* - Escape: N/A.
|
|
2158
|
+
* - Arrow keys: N/A.
|
|
2159
|
+
* - Printable characters: N/A.
|
|
2160
|
+
* **ARIA:**
|
|
2161
|
+
* - Roles used: `role="status"` on the host — an implicit polite live
|
|
2162
|
+
* region ARIA lists as valid for "advisory information" including
|
|
2163
|
+
* in-progress operations. The inner `<svg>` is decorative and carries
|
|
2164
|
+
* `aria-hidden="true"` + `focusable="false"` so it never becomes a
|
|
2165
|
+
* parallel target for screen readers.
|
|
2166
|
+
* - State attributes: none. The loader has no state changes to announce
|
|
2167
|
+
* beyond its own presence; the polite live region on mount is the
|
|
2168
|
+
* announcement.
|
|
2169
|
+
* - Labeling strategy: `aria-label` from the `label` prop (default
|
|
2170
|
+
* "Loading"). Consumers pass a more specific string when the context
|
|
2171
|
+
* warrants ("Loading ceremonies", "Signing in", …).
|
|
2172
|
+
* **Focus:**
|
|
2173
|
+
* - On mount: nothing auto-focuses.
|
|
2174
|
+
* - On activate/open: N/A — not interactive.
|
|
2175
|
+
* - On close/deactivate: N/A.
|
|
2176
|
+
* - Focus indicator: N/A — the host is not focusable.
|
|
2177
|
+
* **Motion:**
|
|
2178
|
+
* - Animations: two mirrored halves of the logo path oscillate
|
|
2179
|
+
* symmetrically around the visual center, meeting at the ring midpoint
|
|
2180
|
+
* and separating again. Cycle 1.4s by default (`speed="normal"`);
|
|
2181
|
+
* `speed="slow"` = 2.2s, `speed="fast"` = 0.9s.
|
|
2182
|
+
* - Reduced-motion behavior: `@media (prefers-reduced-motion: reduce)`
|
|
2183
|
+
* collapses both halves to a static rendering with a slow opacity
|
|
2184
|
+
* pulse (2s ease-in-out). No rotation, no lateral movement.
|
|
2185
|
+
* **Contrast:**
|
|
2186
|
+
* - Text: N/A — the visible surface is the SVG stroke, not text.
|
|
2187
|
+
* - Non-text: stroke inherits `currentColor` from the host. Consumers are
|
|
2188
|
+
* responsible for confirming the stroke against its background meets
|
|
2189
|
+
* the ≥ 3:1 non-text contrast threshold (WCAG 1.4.11) where the loader
|
|
2190
|
+
* conveys state (e.g. distinguishing "still loading" from "idle").
|
|
2191
|
+
* **Screen reader announcement:**
|
|
2192
|
+
* - Rendered state: the polite live region announces the label
|
|
2193
|
+
* (e.g. "Loading") when the loader mounts.
|
|
2194
|
+
* - State change: N/A — the loader itself has no state that changes.
|
|
2195
|
+
* Consumers unmount the loader when the awaited work completes; that
|
|
2196
|
+
* removal is silent, which is the intended behavior (the arriving
|
|
2197
|
+
* content is the announcement).
|
|
2198
|
+
*/
|
|
2199
|
+
interface DsLoader {
|
|
2200
|
+
/**
|
|
2201
|
+
* @default "Loading"
|
|
2202
|
+
*/
|
|
2203
|
+
"label"?: string;
|
|
2204
|
+
/**
|
|
2205
|
+
* @default "md"
|
|
2206
|
+
*/
|
|
2207
|
+
"size"?: "sm" | "md" | "lg";
|
|
2208
|
+
/**
|
|
2209
|
+
* @default "normal"
|
|
2210
|
+
*/
|
|
2211
|
+
"speed"?: "slow" | "normal" | "fast";
|
|
2212
|
+
}
|
|
2213
|
+
interface DsSelect {
|
|
2214
|
+
/**
|
|
2215
|
+
* @default false
|
|
2216
|
+
*/
|
|
2217
|
+
"disabled"?: boolean;
|
|
2218
|
+
"label"?: string;
|
|
2219
|
+
"name"?: string;
|
|
2220
|
+
"onDsChange"?: (event: DsSelectCustomEvent<string>) => void;
|
|
2221
|
+
/**
|
|
2222
|
+
* Options rendered inside the internal shadow `<select>`. Accepts either: - a JSON string (SSR-friendly — server-rendered HTML can only set string attributes, and Stencil does not JSON-parse array attributes), e.g. `options='[{"value":"en","label":"EN"}]'` - an array property set from client-side JS, e.g. `el.options = [{ value: 'en', label: 'EN' }]` Malformed JSON fails safe to an empty list rather than throwing, so the component still renders a visible (if empty) select.
|
|
2223
|
+
* @default "[]"
|
|
2224
|
+
*/
|
|
2225
|
+
"options"?: string | ReadonlyArray<DsSelectOption>;
|
|
2226
|
+
/**
|
|
2227
|
+
* @default "md"
|
|
2228
|
+
*/
|
|
2229
|
+
"size"?: "md" | "lg";
|
|
2230
|
+
"value"?: string;
|
|
2231
|
+
}
|
|
2232
|
+
interface DsTabs {
|
|
2233
|
+
"active"?: string;
|
|
2234
|
+
/**
|
|
2235
|
+
* @default "[]"
|
|
2236
|
+
*/
|
|
2237
|
+
"items"?: string | ReadonlyArray<DsTabItem>;
|
|
2238
|
+
"label"?: string;
|
|
2239
|
+
"onDsChange"?: (event: DsTabsCustomEvent<string>) => void;
|
|
2240
|
+
/**
|
|
2241
|
+
* @default "horizontal"
|
|
2242
|
+
*/
|
|
2243
|
+
"orientation"?: "horizontal" | "vertical";
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* ## Accessibility
|
|
2247
|
+
* **Category:** form-field
|
|
2248
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
2249
|
+
* `<textarea>`; there is no WAI-ARIA APG pattern for a plain
|
|
2250
|
+
* multi-line text field because the browser already implements it
|
|
2251
|
+
* natively. Reference:
|
|
2252
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
|
|
2253
|
+
* **Keyboard interactions:**
|
|
2254
|
+
* - Tab: moves focus to the native `<textarea>`; Tab inside the
|
|
2255
|
+
* textarea inserts a tab character only when the browser is
|
|
2256
|
+
* configured to do so (default behavior: Tab moves focus out).
|
|
2257
|
+
* - Printable characters: insert at the caret position (native).
|
|
2258
|
+
* - Enter: inserts a newline within the value (native), does not
|
|
2259
|
+
* submit the enclosing form (unlike a single-line input).
|
|
2260
|
+
* - Backspace / Delete: native editing.
|
|
2261
|
+
* - Arrow keys: move the caret within the value (native).
|
|
2262
|
+
* - Escape: no native meaning for a text area.
|
|
2263
|
+
* **ARIA:**
|
|
2264
|
+
* - Roles used: none explicit — the native `<textarea>` supplies
|
|
2265
|
+
* the implicit `textbox` role with multiline behavior.
|
|
2266
|
+
* - State attributes: `disabled` (native attribute, not
|
|
2267
|
+
* `aria-disabled`), `aria-invalid` (from the `invalid` prop),
|
|
2268
|
+
* `aria-required` (from the `required` prop), `aria-describedby`
|
|
2269
|
+
* (from the `describedBy` prop, expected to reference a light-DOM
|
|
2270
|
+
* node id — typically a hint or error message rendered by the
|
|
2271
|
+
* consumer alongside the field).
|
|
2272
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
2273
|
+
* the internal `<textarea>`. This is the **canonical, recommended**
|
|
2274
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
2275
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
2276
|
+
* same secondary mode documented on `ds-input` and `ds-select`,
|
|
2277
|
+
* verified in this component's e2e tests. Setting `aria-label`
|
|
2278
|
+
* directly on the `<ds-textarea>` host does NOT work: shadow-DOM
|
|
2279
|
+
* attribute forwarding would be required — use the `label` prop.
|
|
2280
|
+
* **Focus:**
|
|
2281
|
+
* - On mount: nothing auto-focuses.
|
|
2282
|
+
* - On value change: focus remains on the native `<textarea>`.
|
|
2283
|
+
* - Focus indicator: the browser default `:focus-visible` ring is
|
|
2284
|
+
* kept on purpose — a themed focus ring using `--ds-color-focus`
|
|
2285
|
+
* is a future decision once the DS ships a shared focus utility.
|
|
2286
|
+
* **Motion:**
|
|
2287
|
+
* - Animations: none.
|
|
2288
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
2289
|
+
* **Contrast:**
|
|
2290
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends
|
|
2291
|
+
* on the consumer's theme, not verified in isolation.
|
|
2292
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
2293
|
+
* matching `ds-input`.
|
|
2294
|
+
* **Screen reader announcement:**
|
|
2295
|
+
* - Rendered state: announces the `label` prop text as the
|
|
2296
|
+
* accessible name, followed by "edit text, multiline" (browser/AT
|
|
2297
|
+
* wording varies). When `required=true` the announcement includes
|
|
2298
|
+
* "required"; when `invalid=true` it includes "invalid".
|
|
2299
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
2300
|
+
* character (native behavior); no `aria-live` added.
|
|
2301
|
+
* **Consumer contract:**
|
|
2302
|
+
* - `placeholder` is a hint, not a label — always pair with `label`.
|
|
2303
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
2304
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
2305
|
+
* status but not the reason.
|
|
2306
|
+
* - `rows` sets the initial visible line count. Vertical resize is
|
|
2307
|
+
* left to the native default (`resize: vertical`) so users can
|
|
2308
|
+
* grow the field for long entries; the consumer's CSS can pin it
|
|
2309
|
+
* with `resize: none` when a fixed height matters.
|
|
2310
|
+
*/
|
|
2311
|
+
interface DsTextarea {
|
|
2312
|
+
"describedBy"?: string;
|
|
2313
|
+
/**
|
|
2314
|
+
* @default false
|
|
2315
|
+
*/
|
|
2316
|
+
"disabled"?: boolean;
|
|
2317
|
+
/**
|
|
2318
|
+
* @default false
|
|
2319
|
+
*/
|
|
2320
|
+
"invalid"?: boolean;
|
|
2321
|
+
"label"?: string;
|
|
2322
|
+
"name"?: string;
|
|
2323
|
+
"onDsInput"?: (event: DsTextareaCustomEvent<string>) => void;
|
|
2324
|
+
"placeholder"?: string;
|
|
2325
|
+
/**
|
|
2326
|
+
* @default false
|
|
2327
|
+
*/
|
|
2328
|
+
"required"?: boolean;
|
|
2329
|
+
/**
|
|
2330
|
+
* @default 3
|
|
2331
|
+
*/
|
|
2332
|
+
"rows"?: number;
|
|
2333
|
+
/**
|
|
2334
|
+
* @default "md"
|
|
2335
|
+
*/
|
|
2336
|
+
"size"?: "md" | "lg";
|
|
2337
|
+
"value"?: string;
|
|
2338
|
+
}
|
|
2339
|
+
interface DsTooltip {
|
|
2340
|
+
/**
|
|
2341
|
+
* @default 100
|
|
2342
|
+
*/
|
|
2343
|
+
"closeDelay"?: number;
|
|
2344
|
+
"label": string;
|
|
2345
|
+
/**
|
|
2346
|
+
* @default 250
|
|
2347
|
+
*/
|
|
2348
|
+
"openDelay"?: number;
|
|
2349
|
+
/**
|
|
2350
|
+
* @default "bottom"
|
|
2351
|
+
*/
|
|
2352
|
+
"placement"?: "top" | "bottom";
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
interface DsBadgeAttributes {
|
|
2356
|
+
"variant": | "neutral"
|
|
2357
|
+
| "ui-component"
|
|
2358
|
+
| "recipe"
|
|
2359
|
+
| "agent"
|
|
2360
|
+
| "config";
|
|
2361
|
+
}
|
|
2362
|
+
interface DsButtonAttributes {
|
|
2363
|
+
"variant": "primary" | "secondary" | "ghost";
|
|
2364
|
+
"size": "md" | "lg";
|
|
2365
|
+
"disabled": boolean;
|
|
2366
|
+
"type": "button" | "submit" | "reset";
|
|
2367
|
+
"href": string;
|
|
2368
|
+
}
|
|
2369
|
+
interface DsCheckboxAttributes {
|
|
2370
|
+
"checked": boolean;
|
|
2371
|
+
"indeterminate": boolean;
|
|
2372
|
+
"name": string;
|
|
2373
|
+
"value": string;
|
|
2374
|
+
"disabled": boolean;
|
|
2375
|
+
"required": boolean;
|
|
2376
|
+
"invalid": boolean;
|
|
2377
|
+
"describedBy": string;
|
|
2378
|
+
"label": string;
|
|
2379
|
+
}
|
|
2380
|
+
interface DsCodeBlockAttributes {
|
|
2381
|
+
"code": string;
|
|
2382
|
+
"language": string;
|
|
2383
|
+
}
|
|
2384
|
+
interface DsDialogAttributes {
|
|
2385
|
+
"open": boolean;
|
|
2386
|
+
"label": string;
|
|
2387
|
+
"labelledBy": string;
|
|
2388
|
+
"describedBy": string;
|
|
2389
|
+
}
|
|
2390
|
+
interface DsInputAttributes {
|
|
2391
|
+
"value": string;
|
|
2392
|
+
"name": string;
|
|
2393
|
+
"type": | "text"
|
|
2394
|
+
| "email"
|
|
2395
|
+
| "password"
|
|
2396
|
+
| "search"
|
|
2397
|
+
| "tel"
|
|
2398
|
+
| "url"
|
|
2399
|
+
| "number";
|
|
2400
|
+
"placeholder": string;
|
|
2401
|
+
"disabled": boolean;
|
|
2402
|
+
"required": boolean;
|
|
2403
|
+
"invalid": boolean;
|
|
2404
|
+
"describedBy": string;
|
|
2405
|
+
"label": string;
|
|
2406
|
+
"size": "md" | "lg";
|
|
2407
|
+
}
|
|
2408
|
+
interface DsLoaderAttributes {
|
|
2409
|
+
"size": "sm" | "md" | "lg";
|
|
2410
|
+
"label": string;
|
|
2411
|
+
"speed": "slow" | "normal" | "fast";
|
|
2412
|
+
}
|
|
2413
|
+
interface DsSelectAttributes {
|
|
2414
|
+
"value": string;
|
|
2415
|
+
"name": string;
|
|
2416
|
+
"disabled": boolean;
|
|
2417
|
+
"size": "md" | "lg";
|
|
2418
|
+
"label": string;
|
|
2419
|
+
"options": string | ReadonlyArray<DsSelectOption>;
|
|
2420
|
+
}
|
|
2421
|
+
interface DsTabsAttributes {
|
|
2422
|
+
"items": string | ReadonlyArray<DsTabItem>;
|
|
2423
|
+
"active": string;
|
|
2424
|
+
"orientation": "horizontal" | "vertical";
|
|
2425
|
+
"label": string;
|
|
2426
|
+
}
|
|
2427
|
+
interface DsTextareaAttributes {
|
|
2428
|
+
"value": string;
|
|
2429
|
+
"name": string;
|
|
2430
|
+
"placeholder": string;
|
|
2431
|
+
"disabled": boolean;
|
|
2432
|
+
"required": boolean;
|
|
2433
|
+
"invalid": boolean;
|
|
2434
|
+
"describedBy": string;
|
|
2435
|
+
"label": string;
|
|
2436
|
+
"rows": number;
|
|
2437
|
+
"size": "md" | "lg";
|
|
2438
|
+
}
|
|
2439
|
+
interface DsTooltipAttributes {
|
|
2440
|
+
"label": string;
|
|
2441
|
+
"openDelay": number;
|
|
2442
|
+
"closeDelay": number;
|
|
2443
|
+
"placement": "top" | "bottom";
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
interface IntrinsicElements {
|
|
2447
|
+
"ds-badge": Omit<DsBadge, keyof DsBadgeAttributes> & { [K in keyof DsBadge & keyof DsBadgeAttributes]?: DsBadge[K] } & { [K in keyof DsBadge & keyof DsBadgeAttributes as `attr:${K}`]?: DsBadgeAttributes[K] } & { [K in keyof DsBadge & keyof DsBadgeAttributes as `prop:${K}`]?: DsBadge[K] };
|
|
2448
|
+
"ds-button": Omit<DsButton, keyof DsButtonAttributes> & { [K in keyof DsButton & keyof DsButtonAttributes]?: DsButton[K] } & { [K in keyof DsButton & keyof DsButtonAttributes as `attr:${K}`]?: DsButtonAttributes[K] } & { [K in keyof DsButton & keyof DsButtonAttributes as `prop:${K}`]?: DsButton[K] };
|
|
2449
|
+
"ds-card": DsCard;
|
|
2450
|
+
"ds-checkbox": Omit<DsCheckbox, keyof DsCheckboxAttributes> & { [K in keyof DsCheckbox & keyof DsCheckboxAttributes]?: DsCheckbox[K] } & { [K in keyof DsCheckbox & keyof DsCheckboxAttributes as `attr:${K}`]?: DsCheckboxAttributes[K] } & { [K in keyof DsCheckbox & keyof DsCheckboxAttributes as `prop:${K}`]?: DsCheckbox[K] };
|
|
2451
|
+
"ds-code-block": Omit<DsCodeBlock, keyof DsCodeBlockAttributes> & { [K in keyof DsCodeBlock & keyof DsCodeBlockAttributes]?: DsCodeBlock[K] } & { [K in keyof DsCodeBlock & keyof DsCodeBlockAttributes as `attr:${K}`]?: DsCodeBlockAttributes[K] } & { [K in keyof DsCodeBlock & keyof DsCodeBlockAttributes as `prop:${K}`]?: DsCodeBlock[K] } & OneOf<"code", DsCodeBlock["code"], DsCodeBlockAttributes["code"]>;
|
|
2452
|
+
"ds-dialog": Omit<DsDialog, keyof DsDialogAttributes> & { [K in keyof DsDialog & keyof DsDialogAttributes]?: DsDialog[K] } & { [K in keyof DsDialog & keyof DsDialogAttributes as `attr:${K}`]?: DsDialogAttributes[K] } & { [K in keyof DsDialog & keyof DsDialogAttributes as `prop:${K}`]?: DsDialog[K] };
|
|
2453
|
+
"ds-input": Omit<DsInput, keyof DsInputAttributes> & { [K in keyof DsInput & keyof DsInputAttributes]?: DsInput[K] } & { [K in keyof DsInput & keyof DsInputAttributes as `attr:${K}`]?: DsInputAttributes[K] } & { [K in keyof DsInput & keyof DsInputAttributes as `prop:${K}`]?: DsInput[K] };
|
|
2454
|
+
"ds-loader": Omit<DsLoader, keyof DsLoaderAttributes> & { [K in keyof DsLoader & keyof DsLoaderAttributes]?: DsLoader[K] } & { [K in keyof DsLoader & keyof DsLoaderAttributes as `attr:${K}`]?: DsLoaderAttributes[K] } & { [K in keyof DsLoader & keyof DsLoaderAttributes as `prop:${K}`]?: DsLoader[K] };
|
|
2455
|
+
"ds-select": Omit<DsSelect, keyof DsSelectAttributes> & { [K in keyof DsSelect & keyof DsSelectAttributes]?: DsSelect[K] } & { [K in keyof DsSelect & keyof DsSelectAttributes as `attr:${K}`]?: DsSelectAttributes[K] } & { [K in keyof DsSelect & keyof DsSelectAttributes as `prop:${K}`]?: DsSelect[K] };
|
|
2456
|
+
"ds-tabs": Omit<DsTabs, keyof DsTabsAttributes> & { [K in keyof DsTabs & keyof DsTabsAttributes]?: DsTabs[K] } & { [K in keyof DsTabs & keyof DsTabsAttributes as `attr:${K}`]?: DsTabsAttributes[K] } & { [K in keyof DsTabs & keyof DsTabsAttributes as `prop:${K}`]?: DsTabs[K] };
|
|
2457
|
+
"ds-textarea": Omit<DsTextarea, keyof DsTextareaAttributes> & { [K in keyof DsTextarea & keyof DsTextareaAttributes]?: DsTextarea[K] } & { [K in keyof DsTextarea & keyof DsTextareaAttributes as `attr:${K}`]?: DsTextareaAttributes[K] } & { [K in keyof DsTextarea & keyof DsTextareaAttributes as `prop:${K}`]?: DsTextarea[K] };
|
|
2458
|
+
"ds-tooltip": Omit<DsTooltip, keyof DsTooltipAttributes> & { [K in keyof DsTooltip & keyof DsTooltipAttributes]?: DsTooltip[K] } & { [K in keyof DsTooltip & keyof DsTooltipAttributes as `attr:${K}`]?: DsTooltipAttributes[K] } & { [K in keyof DsTooltip & keyof DsTooltipAttributes as `prop:${K}`]?: DsTooltip[K] } & OneOf<"label", DsTooltip["label"], DsTooltipAttributes["label"]>;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
export { LocalJSX as JSX };
|
|
2462
|
+
declare module "@stencil/core" {
|
|
2463
|
+
export namespace JSX {
|
|
2464
|
+
interface IntrinsicElements {
|
|
2465
|
+
/**
|
|
2466
|
+
* ## Accessibility
|
|
2467
|
+
* **Category:** static-display
|
|
2468
|
+
* **APG pattern:** N/A — non-interactive text label.
|
|
2469
|
+
* **Keyboard interactions:**
|
|
2470
|
+
* - Tab: N/A — the badge is not focusable and does not appear in tab order.
|
|
2471
|
+
* - Enter: N/A.
|
|
2472
|
+
* - Escape: N/A.
|
|
2473
|
+
* - Arrow keys: N/A.
|
|
2474
|
+
* - Printable characters: N/A.
|
|
2475
|
+
* **ARIA:**
|
|
2476
|
+
* - Roles used: none — renders a plain `<span>` around a `<slot />`. The
|
|
2477
|
+
* `variant` prop is a purely visual axis and MUST NOT be the sole carrier
|
|
2478
|
+
* of information (color/style alone fails WCAG 1.4.1 Use of Color); the
|
|
2479
|
+
* textual content inside the slot must convey the same meaning.
|
|
2480
|
+
* - State attributes: none.
|
|
2481
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers place the
|
|
2482
|
+
* readable label directly inside the tag.
|
|
2483
|
+
* **Focus:**
|
|
2484
|
+
* - On mount: nothing auto-focuses.
|
|
2485
|
+
* - On activate/open: N/A — non-interactive.
|
|
2486
|
+
* - On close/deactivate: N/A.
|
|
2487
|
+
* - Focus indicator: N/A — the host is not focusable. If a consumer wraps
|
|
2488
|
+
* `ds-badge` in an interactive element (e.g. `<button>`, `<a>`), that
|
|
2489
|
+
* wrapper owns the focus indicator via the DS `--ds-color-focus` token
|
|
2490
|
+
* and its own `:focus-visible` rule.
|
|
2491
|
+
* **Motion:**
|
|
2492
|
+
* - Animations: none.
|
|
2493
|
+
* - Reduced-motion behavior: N/A.
|
|
2494
|
+
* **Contrast:**
|
|
2495
|
+
* - Text: inherits `color: inherit` / `currentColor` from the host page and
|
|
2496
|
+
* the variant-specific style; not verified in isolation here. Consumers
|
|
2497
|
+
* are responsible for confirming ≥ 4.5:1 (WCAG 1.4.3) against the surface
|
|
2498
|
+
* the badge is placed on.
|
|
2499
|
+
* - Non-text: no state-conveying icons or borders that need the 3:1
|
|
2500
|
+
* non-text contrast threshold.
|
|
2501
|
+
* **Screen reader announcement:**
|
|
2502
|
+
* - Rendered state: announces the slot text as inline content within its
|
|
2503
|
+
* surrounding context (no role means AT reads it as plain text). The
|
|
2504
|
+
* `variant` value is not announced — that is intentional; the label text
|
|
2505
|
+
* is the source of truth for meaning.
|
|
2506
|
+
* - State change: N/A — no dynamic state owned by this component.
|
|
2507
|
+
*/
|
|
2508
|
+
"ds-badge": LocalJSX.IntrinsicElements["ds-badge"] & JSXBase.HTMLAttributes<HTMLDsBadgeElement>;
|
|
2509
|
+
/**
|
|
2510
|
+
* ## Accessibility
|
|
2511
|
+
* **Category:** interactive-display
|
|
2512
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
2513
|
+
* (satisfied natively by the underlying `<button>` element). When `href` is
|
|
2514
|
+
* set, the component renders `<a href>` instead — a link, not a button — and
|
|
2515
|
+
* that surface follows native `<a>` semantics rather than the Button pattern.
|
|
2516
|
+
* **Keyboard interactions:**
|
|
2517
|
+
* - Tab: moves focus to the internal `<button>` or `<a>` (shadow DOM does not
|
|
2518
|
+
* interrupt tab order).
|
|
2519
|
+
* - Enter: activates (fires click on `<button>`, navigates on `<a>`).
|
|
2520
|
+
* - Space: activates the `<button>` variant only (native behavior); on the
|
|
2521
|
+
* `<a>` variant Space scrolls the page as usual.
|
|
2522
|
+
* - Escape: N/A — no dismissible state owned by this component.
|
|
2523
|
+
* - Arrow keys: N/A.
|
|
2524
|
+
* - Printable characters: N/A.
|
|
2525
|
+
* **ARIA:**
|
|
2526
|
+
* - Roles used: none explicit — the native `<button>` / `<a href>` supplies
|
|
2527
|
+
* the implicit `button` / `link` role.
|
|
2528
|
+
* - State attributes: `disabled` (native attribute on `<button>`, not
|
|
2529
|
+
* `aria-disabled`). The `<a>` variant does not accept `disabled` — a
|
|
2530
|
+
* disabled navigation is a spec smell (see spec §"Button vs Link"); if a
|
|
2531
|
+
* link needs a disabled state, model it as a `<button>` action instead.
|
|
2532
|
+
* - Labeling strategy: visible text via `<slot />`. Consumers MUST place
|
|
2533
|
+
* readable text (or a labeled icon) inside the tag. An empty slot leaves
|
|
2534
|
+
* the button/link without an accessible name and fails
|
|
2535
|
+
* `button-name` / `link-name` axe rules. Icon-only usage is out of contract
|
|
2536
|
+
* for `ds-button`; use a future `ds-icon-button` with a required
|
|
2537
|
+
* `aria-label` prop when that lands.
|
|
2538
|
+
* **Focus:**
|
|
2539
|
+
* - On mount: nothing auto-focuses.
|
|
2540
|
+
* - On activate: focus stays on the element (native click behavior; the
|
|
2541
|
+
* component does not move focus on its own).
|
|
2542
|
+
* - On close/deactivate: N/A.
|
|
2543
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
2544
|
+
* `outline-offset`, applied via `:focus-visible` — replaces the browser
|
|
2545
|
+
* default so both `<button>` and `<a>` variants use the same DS ring
|
|
2546
|
+
* (spec universal rule 3, WCAG 1.4.11 non-text contrast ≥ 3:1).
|
|
2547
|
+
* **Motion:**
|
|
2548
|
+
* - Animations: 150ms `background-color` ease-in-out transition on
|
|
2549
|
+
* `primary`/`secondary` variants (hover).
|
|
2550
|
+
* - Reduced-motion behavior: N/A — 150ms is below the spec's 200ms threshold
|
|
2551
|
+
* for `prefers-reduced-motion` handling, and the transition animates color
|
|
2552
|
+
* only (no motion of position, scale, or opacity).
|
|
2553
|
+
* **Contrast:**
|
|
2554
|
+
* - Text: `primary` and `secondary` variants pair `--ds-color-{primary,secondary}`
|
|
2555
|
+
* with `--ds-color-text-on-{primary,secondary}` — the token pairs are
|
|
2556
|
+
* authored to meet WCAG 2.1 AA (≥ 4.5:1). The `ghost` variant inherits
|
|
2557
|
+
* `color: inherit` and pins `opacity: 0.85`, so its effective contrast
|
|
2558
|
+
* depends on the consumer's surface — verify per site.
|
|
2559
|
+
* - Non-text (border on `ghost`): 1px `currentColor`; contrast tracks the
|
|
2560
|
+
* text color 1:1, subject to the same host-page caveat.
|
|
2561
|
+
* **Screen reader announcement:**
|
|
2562
|
+
* - Rendered state: announces the slot text followed by "button" (or "link"
|
|
2563
|
+
* for the `href` variant), plus "dimmed" / "unavailable" when `disabled`
|
|
2564
|
+
* is set (browser/AT dependent wording).
|
|
2565
|
+
* - State change: N/A — this component does not own toggle state. A pressed
|
|
2566
|
+
* toggle needs `aria-pressed`, which is out of scope here; model that as
|
|
2567
|
+
* a distinct component or extend the API deliberately per the spec.
|
|
2568
|
+
*/
|
|
2569
|
+
"ds-button": LocalJSX.IntrinsicElements["ds-button"] & JSXBase.HTMLAttributes<HTMLDsButtonElement>;
|
|
2570
|
+
/**
|
|
2571
|
+
* ## Accessibility
|
|
2572
|
+
* **Category:** static-display
|
|
2573
|
+
* **APG pattern:** N/A — a container surface with no interactive
|
|
2574
|
+
* behavior of its own. If a consumer nests interactive elements
|
|
2575
|
+
* inside (button, link, form field), those elements satisfy their
|
|
2576
|
+
* own a11y bucket independently.
|
|
2577
|
+
* **Keyboard interactions:**
|
|
2578
|
+
* - Tab / Shift+Tab: N/A — the card itself is not focusable. Any
|
|
2579
|
+
* interactive descendant carries its own tab semantics.
|
|
2580
|
+
* - Enter / Space / Arrow / Escape / Printable: N/A.
|
|
2581
|
+
* **ARIA:**
|
|
2582
|
+
* - Roles used: none. `<ds-card>` is a purely visual grouping and
|
|
2583
|
+
* deliberately does not carry `role="region"` — that would demand
|
|
2584
|
+
* a labelledby target, which would only be correct in a subset of
|
|
2585
|
+
* uses. Consumers that need a labeled landmark should render a
|
|
2586
|
+
* `<section aria-labelledby>` around the card.
|
|
2587
|
+
* - State attributes: none.
|
|
2588
|
+
* - Labeling strategy: none applied by the component; consumer's
|
|
2589
|
+
* heading structure inside the slot supplies the accessible name
|
|
2590
|
+
* when needed.
|
|
2591
|
+
* **Focus:**
|
|
2592
|
+
* - On mount: nothing auto-focuses.
|
|
2593
|
+
* - Focus indicator: N/A — the card itself is not focusable.
|
|
2594
|
+
* **Motion:**
|
|
2595
|
+
* - Animations: none.
|
|
2596
|
+
* - Reduced-motion behavior: N/A.
|
|
2597
|
+
* **Contrast:**
|
|
2598
|
+
* - Text: inherits `color: inherit` / `currentColor`; the card does
|
|
2599
|
+
* not impose a text color, so contrast is the consumer theme's
|
|
2600
|
+
* responsibility.
|
|
2601
|
+
* - Non-text (border, subtle background tint): both derived from
|
|
2602
|
+
* `color-mix(in oklch, currentColor N%, transparent)`, matching
|
|
2603
|
+
* the pattern used by `ds-input` and `ds-select`. WCAG 1.4.11
|
|
2604
|
+
* (3:1 non-text contrast) is met in themes where the surface has
|
|
2605
|
+
* enough delta against the page background; the component does
|
|
2606
|
+
* not override the consumer's palette to force it.
|
|
2607
|
+
* **Screen reader announcement:**
|
|
2608
|
+
* - Rendered state: no announcement of the card itself. AT
|
|
2609
|
+
* navigates to the slotted content directly, using whatever
|
|
2610
|
+
* heading and landmark structure the consumer places inside.
|
|
2611
|
+
* - State change: N/A.
|
|
2612
|
+
*/
|
|
2613
|
+
"ds-card": LocalJSX.IntrinsicElements["ds-card"] & JSXBase.HTMLAttributes<HTMLDsCardElement>;
|
|
2614
|
+
/**
|
|
2615
|
+
* ## Accessibility
|
|
2616
|
+
* **Category:** form-field
|
|
2617
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
2618
|
+
* `<input type="checkbox">`; the browser already implements the
|
|
2619
|
+
* checkbox role and its state semantics. Reference:
|
|
2620
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
|
2621
|
+
* Note: `ds-checkbox` intentionally does NOT reach for the WAI-ARIA
|
|
2622
|
+
* APG "tri-state checkbox" pattern (indeterminate + on + off) as a
|
|
2623
|
+
* scratch-built ARIA widget. The native input already exposes the
|
|
2624
|
+
* `indeterminate` DOM property and browsers report it to AT
|
|
2625
|
+
* correctly; the `indeterminate` prop here forwards to that DOM
|
|
2626
|
+
* property so the native mechanism carries the semantics.
|
|
2627
|
+
* **Keyboard interactions:**
|
|
2628
|
+
* - Tab: moves focus to the native `<input type="checkbox">`.
|
|
2629
|
+
* - Space: toggles the checked state (native behavior).
|
|
2630
|
+
* - Enter: no native effect on a checkbox (unlike button). Form
|
|
2631
|
+
* submission triggered by Enter comes from the enclosing form's
|
|
2632
|
+
* default submit button, not from the checkbox itself.
|
|
2633
|
+
* **ARIA:**
|
|
2634
|
+
* - Roles used: none explicit — the native checkbox supplies the
|
|
2635
|
+
* implicit `checkbox` role.
|
|
2636
|
+
* - State attributes: `checked` (DOM property, kept in sync via
|
|
2637
|
+
* componentDidRender because Stencil does not JSX-forward the
|
|
2638
|
+
* attribute cleanly for controlled use), `disabled` (native
|
|
2639
|
+
* attribute, not `aria-disabled`), `aria-invalid` (from the
|
|
2640
|
+
* `invalid` prop), `aria-required` (from the `required` prop),
|
|
2641
|
+
* `aria-describedby` (from the `describedBy` prop).
|
|
2642
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
2643
|
+
* the internal checkbox. This is the **canonical, recommended**
|
|
2644
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
2645
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
2646
|
+
* same secondary mode documented on `ds-input`.
|
|
2647
|
+
* **Focus:**
|
|
2648
|
+
* - On mount: nothing auto-focuses.
|
|
2649
|
+
* - On toggle: focus remains on the native checkbox.
|
|
2650
|
+
* - Focus indicator: browser default `:focus-visible` ring, kept on
|
|
2651
|
+
* purpose until the DS ships a themed focus utility using
|
|
2652
|
+
* `--ds-color-focus`. Not overridden by `ds-checkbox.css`.
|
|
2653
|
+
* **Motion:**
|
|
2654
|
+
* - Animations: none.
|
|
2655
|
+
* - Reduced-motion behavior: N/A.
|
|
2656
|
+
* **Contrast:**
|
|
2657
|
+
* - Text: N/A — the checkbox itself renders no text (labels are
|
|
2658
|
+
* supplied by the consumer via `label` prop or wrapping <label>).
|
|
2659
|
+
* - Non-text (the box and its check glyph): browser default rendering
|
|
2660
|
+
* is kept on purpose (no `appearance: none` override); this
|
|
2661
|
+
* preserves the OS/browser accessibility rendering that AT and
|
|
2662
|
+
* forced-color modes rely on. A themed override is a future
|
|
2663
|
+
* decision once the DS commits to a checkbox visual language that
|
|
2664
|
+
* still passes 3:1 non-text contrast in every theme.
|
|
2665
|
+
* **Screen reader announcement:**
|
|
2666
|
+
* - Rendered state: announces the `label` prop text as the
|
|
2667
|
+
* accessible name, followed by "checkbox", followed by "checked"
|
|
2668
|
+
* / "not checked" / "mixed" per the current DOM state.
|
|
2669
|
+
* - State change: on toggle, AT announces the new state (native
|
|
2670
|
+
* behavior); no `aria-live` added.
|
|
2671
|
+
* **Consumer contract:**
|
|
2672
|
+
* - `checked` is the source of truth for controlled use. Reading
|
|
2673
|
+
* `event.detail` from `dsChange` gives the new state; the internal
|
|
2674
|
+
* input is kept in sync via a Watch + a re-sync in componentDidRender.
|
|
2675
|
+
* - `indeterminate` is a visual/AT state, not a value — it is NOT
|
|
2676
|
+
* submitted with a form and clears on the next user toggle.
|
|
2677
|
+
*/
|
|
2678
|
+
"ds-checkbox": LocalJSX.IntrinsicElements["ds-checkbox"] & JSXBase.HTMLAttributes<HTMLDsCheckboxElement>;
|
|
2679
|
+
/**
|
|
2680
|
+
* ## Accessibility
|
|
2681
|
+
* **Category:** interactive-display
|
|
2682
|
+
* **APG pattern:** Button — https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
2683
|
+
* (for the internal copy control; the code readout itself is static text).
|
|
2684
|
+
* **Keyboard interactions:**
|
|
2685
|
+
* - Tab: moves focus into the internal copy `<button>`; the code readout
|
|
2686
|
+
* (`<pre><code>`) is not focusable itself, but its container is
|
|
2687
|
+
* horizontally scrollable and remains reachable via the browser's caret
|
|
2688
|
+
* browsing / find-in-page path.
|
|
2689
|
+
* - Enter: activates the copy button (writes `code` to the clipboard).
|
|
2690
|
+
* - Space: activates the copy button (native `<button>` behavior).
|
|
2691
|
+
* - Escape: N/A — the "Copied" state auto-clears after 1.4s; no dismissible
|
|
2692
|
+
* modal state.
|
|
2693
|
+
* - Arrow keys: N/A.
|
|
2694
|
+
* - Printable characters: N/A.
|
|
2695
|
+
* **ARIA:**
|
|
2696
|
+
* - Roles used: none explicit — the native `<button>` supplies the implicit
|
|
2697
|
+
* `button` role; `<pre>` and `<code>` supply their semantic roles.
|
|
2698
|
+
* - State attributes: none. The `copied` flag lives only in the button's
|
|
2699
|
+
* accessible name (see below); it is intentionally not surfaced via
|
|
2700
|
+
* `aria-pressed` because it is not a toggle — it is a transient
|
|
2701
|
+
* post-action confirmation.
|
|
2702
|
+
* - Labeling strategy: `aria-label` on the copy button — "Copy code" in the
|
|
2703
|
+
* resting state, "Copied" while the confirmation is visible. This is
|
|
2704
|
+
* authoritative because the button carries both an icon and the words
|
|
2705
|
+
* "Copy" / "Copied", and mixed icon+text needs an explicit accessible
|
|
2706
|
+
* name to avoid AT reading the SVG as a separate node.
|
|
2707
|
+
* **Focus:**
|
|
2708
|
+
* - On mount: nothing auto-focuses.
|
|
2709
|
+
* - On activate: focus stays on the copy button (clipboard write is
|
|
2710
|
+
* synchronous from the user's perspective; no focus is moved).
|
|
2711
|
+
* - On close/deactivate: N/A — no popup or overlay to close.
|
|
2712
|
+
* - Focus indicator: 2px `outline` in `var(--ds-color-focus)` with 2px
|
|
2713
|
+
* `outline-offset`, applied to `.ds-code-block__copy:focus-visible`. The
|
|
2714
|
+
* readout itself is not focusable and has no focus indicator by design.
|
|
2715
|
+
* **Motion:**
|
|
2716
|
+
* - Animations: 120ms `color` / `border-color` ease transition on the copy
|
|
2717
|
+
* button (hover/focus).
|
|
2718
|
+
* - Reduced-motion behavior: N/A — 120ms is below the spec's 200ms
|
|
2719
|
+
* threshold, and the transition animates color only.
|
|
2720
|
+
* **Contrast:**
|
|
2721
|
+
* - Text (code readout): inherits `color: currentColor`; contrast depends
|
|
2722
|
+
* on the host page's text color vs. the block's tinted background
|
|
2723
|
+
* (`color-mix(currentColor 4%, transparent)`). Verify per site.
|
|
2724
|
+
* - Text (copy button): `color-mix(in oklch, currentColor 70%, transparent)`
|
|
2725
|
+
* at rest, `currentColor` on hover. The 70%-mixed resting color can drop
|
|
2726
|
+
* below 4.5:1 on low-contrast surfaces — audit per usage site, and lean
|
|
2727
|
+
* on the hover/focus states (both restore full `currentColor`) as the
|
|
2728
|
+
* accessible fallback.
|
|
2729
|
+
* - Non-text (button border, language tag border-bottom): both use
|
|
2730
|
+
* `color-mix(currentColor <15|12>%, transparent)` — decorative separators,
|
|
2731
|
+
* they do not convey state, so the 3:1 non-text contrast rule does not
|
|
2732
|
+
* apply. Focus and hover borders shift up to 30% for clearer edges.
|
|
2733
|
+
* **Screen reader announcement:**
|
|
2734
|
+
* - Rendered state: announces "Copy code, button" (the `aria-label`), then
|
|
2735
|
+
* the code content when the user navigates into the `<pre><code>` block
|
|
2736
|
+
* (usually announced as a code block on VoiceOver).
|
|
2737
|
+
* - State change: after clicking Copy, the button's `aria-label` flips to
|
|
2738
|
+
* "Copied"; AT will announce this on the next focus or re-read of the
|
|
2739
|
+
* button, but NOT proactively — there is no `aria-live` region. This is
|
|
2740
|
+
* deliberate per spec §"What we explicitly do NOT do" (avoid live-region
|
|
2741
|
+
* overuse), and the trade-off is that a screen-reader-only user does not
|
|
2742
|
+
* get an unsolicited confirmation. If a future review decides that
|
|
2743
|
+
* confirmation must be audible, add a visually-hidden
|
|
2744
|
+
* `aria-live="polite"` element whose text toggles on `copied`.
|
|
2745
|
+
*/
|
|
2746
|
+
"ds-code-block": LocalJSX.IntrinsicElements["ds-code-block"] & JSXBase.HTMLAttributes<HTMLDsCodeBlockElement>;
|
|
2747
|
+
/**
|
|
2748
|
+
* ## Accessibility
|
|
2749
|
+
* **Category:** state-machine
|
|
2750
|
+
* **APG pattern:** https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
|
|
2751
|
+
* This component wraps the **native `<dialog>` element** rather than
|
|
2752
|
+
* scratch-building the APG pattern on top of a `<div role="dialog">`.
|
|
2753
|
+
* `<dialog>` opened via `showModal()` gives us — from the browser,
|
|
2754
|
+
* verified by the platform, not by us:
|
|
2755
|
+
* - Focus trap: Tab / Shift+Tab cycle only through focusable
|
|
2756
|
+
* descendants (including slotted light-DOM descendants; the flattened
|
|
2757
|
+
* accessibility tree is what the trap operates on).
|
|
2758
|
+
* - Escape: closes the dialog and returns focus to the previously
|
|
2759
|
+
* focused element (native, not implemented here).
|
|
2760
|
+
* - Inert background: everything outside the dialog is inert while it
|
|
2761
|
+
* is open — no mouse, no keyboard, not part of the a11y tree.
|
|
2762
|
+
* - Return focus: on close, focus returns to whatever element had it
|
|
2763
|
+
* before `showModal()` — native, not implemented here.
|
|
2764
|
+
* - Initial focus: browser auto-focuses the first `autofocus` element
|
|
2765
|
+
* inside, or the first focusable if none — native.
|
|
2766
|
+
* - `role="dialog"` and `aria-modal="true"` semantics: implicit on
|
|
2767
|
+
* `<dialog>`, no ARIA needed from us.
|
|
2768
|
+
* This is the deliberate answer to the a11y spec §D warning about
|
|
2769
|
+
* scratch-building Tier 2 components: the native element removes the
|
|
2770
|
+
* "compiles, axe passes, screen-reader user gets trapped" failure
|
|
2771
|
+
* mode by delegating the state machine to the platform.
|
|
2772
|
+
* **Keyboard interactions (all handled by the native element unless
|
|
2773
|
+
* noted otherwise):**
|
|
2774
|
+
* - Tab: cycles focus within the dialog's focusable descendants
|
|
2775
|
+
* (native focus trap).
|
|
2776
|
+
* - Shift+Tab: reverse cycle (native).
|
|
2777
|
+
* - Escape: closes the dialog and fires the `close` event (native).
|
|
2778
|
+
* Consumers who need to prevent dismissal on Escape (destructive
|
|
2779
|
+
* flows: e.g. an unsaved-changes guard) must call
|
|
2780
|
+
* `event.preventDefault()` inside their own `keydown` listener on
|
|
2781
|
+
* the dialog before it reaches the browser — that is an escape
|
|
2782
|
+
* hatch, not a default we impose.
|
|
2783
|
+
* - Enter / Space: no special dialog behavior; forwarded to
|
|
2784
|
+
* whatever descendant has focus (a button will activate).
|
|
2785
|
+
* - Home / End / Arrow keys: no special dialog behavior; forwarded
|
|
2786
|
+
* to descendants.
|
|
2787
|
+
* - Printable characters: forwarded to descendants (e.g. typing
|
|
2788
|
+
* inside a slotted `<ds-input>`).
|
|
2789
|
+
* **ARIA attributes and value triggers:**
|
|
2790
|
+
* - Implicit `role="dialog"` on the native `<dialog>`.
|
|
2791
|
+
* - Implicit `aria-modal="true"` when opened via `showModal()`
|
|
2792
|
+
* (which is what this component always uses; `show()` — the
|
|
2793
|
+
* non-modal form — is intentionally not exposed in v1).
|
|
2794
|
+
* - `aria-label` set from the `label` prop onto the `<dialog>`.
|
|
2795
|
+
* Consumers who prefer an in-content title can pass a
|
|
2796
|
+
* `labelledBy` prop instead, whose value is the id of a heading
|
|
2797
|
+
* element they render inside the slot — `aria-labelledby`
|
|
2798
|
+
* resolves that id through the flattened tree.
|
|
2799
|
+
* - `aria-describedby` from the `describedBy` prop, resolving a
|
|
2800
|
+
* light-DOM id of a descriptive block (e.g. the dialog body copy
|
|
2801
|
+
* when the label is short).
|
|
2802
|
+
* **Focus lifecycle:**
|
|
2803
|
+
* - Opening: `showModal()` moves focus to the first `autofocus`
|
|
2804
|
+
* element inside, or the first focusable if none. Consumers who
|
|
2805
|
+
* want a specific initial focus target should mark that element
|
|
2806
|
+
* with `autofocus`.
|
|
2807
|
+
* - While open: focus is trapped within the flattened tree of the
|
|
2808
|
+
* `<dialog>`; Tab / Shift+Tab cycle through slotted descendants.
|
|
2809
|
+
* Focus cannot escape into the inert background.
|
|
2810
|
+
* - Closing: `close()` (either from Escape, from a form-submit with
|
|
2811
|
+
* `method="dialog"`, from a light-dismiss backdrop click handler
|
|
2812
|
+
* the consumer wires up, or from `open=false`) returns focus to
|
|
2813
|
+
* the element that had it immediately before `showModal()` was
|
|
2814
|
+
* called. The `dsClose` event fires after focus has been restored.
|
|
2815
|
+
* **ARIA labelling contract (choose one):**
|
|
2816
|
+
* - **`label`** (string) — sets `aria-label` on the dialog. Simplest
|
|
2817
|
+
* for short titles.
|
|
2818
|
+
* - **`labelledBy`** (id) — sets `aria-labelledby` to reference a
|
|
2819
|
+
* heading rendered inside the slot. Preferred when the dialog has
|
|
2820
|
+
* a visible title so the accessible name matches the visible name.
|
|
2821
|
+
* If both are omitted, axe reports `dialog-name` — the dialog has no
|
|
2822
|
+
* accessible name. The a11y test for this component asserts that at
|
|
2823
|
+
* least one of the two is provided.
|
|
2824
|
+
* **Motion:**
|
|
2825
|
+
* - Animations: none applied by the component. Consumers who want an
|
|
2826
|
+
* entrance transition can style `::backdrop` and `dialog[open]`
|
|
2827
|
+
* with their own animation, gated on `@media
|
|
2828
|
+
* (prefers-reduced-motion: no-preference)`.
|
|
2829
|
+
* - Reduced-motion behavior: N/A — no animation to collapse. If a
|
|
2830
|
+
* consumer adds one, they own the reduced-motion fallback.
|
|
2831
|
+
* **Contrast:**
|
|
2832
|
+
* - Text: inherits `color: inherit`; consumer theme owns contrast.
|
|
2833
|
+
* - Non-text: subtle bordered surface via
|
|
2834
|
+
* `color-mix(in oklch, currentColor N%, transparent)` matching
|
|
2835
|
+
* `ds-card`. Backdrop is a semi-transparent black by default.
|
|
2836
|
+
* **Screen reader announcement:**
|
|
2837
|
+
* - Rendered state (closed): dialog is not in the a11y tree.
|
|
2838
|
+
* - Opened: AT announces "dialog" plus the accessible name from
|
|
2839
|
+
* `label` / `labelledBy`, then reads the content following focus
|
|
2840
|
+
* (or the descriptive block referenced by `describedBy` first,
|
|
2841
|
+
* depending on AT).
|
|
2842
|
+
* - State change: on close, AT returns to the element that regained
|
|
2843
|
+
* focus (the trigger); no `aria-live` announcement is added.
|
|
2844
|
+
* **Consumer contract:**
|
|
2845
|
+
* - Set `open` to true to show; set to false to close. The component
|
|
2846
|
+
* syncs the DOM state via `showModal()` / `close()` in a Watch.
|
|
2847
|
+
* - Listen to `dsClose` to know when the dialog was dismissed (by
|
|
2848
|
+
* Escape, by `open=false`, or by a form submit with
|
|
2849
|
+
* `method="dialog"`). Listen to `dsOpen` for the mirror event.
|
|
2850
|
+
* - The default backdrop is opaque and NOT click-to-dismiss. Consumers
|
|
2851
|
+
* who want light-dismiss can add a click listener on the dialog
|
|
2852
|
+
* that closes when the click target is the dialog itself (not a
|
|
2853
|
+
* descendant): `if (e.target === dialog) dialog.close()`.
|
|
2854
|
+
* - Put focus targets (form fields, primary action button) inside
|
|
2855
|
+
* the slot. Use `autofocus` on the element that should receive
|
|
2856
|
+
* focus first.
|
|
2857
|
+
* **Pending manual verification (see commit message TODO):**
|
|
2858
|
+
* - VoiceOver on macOS: confirm the dialog announces its accessible
|
|
2859
|
+
* name + "dialog" on open, focus lands on the correct element,
|
|
2860
|
+
* Escape closes and focus returns to the trigger.
|
|
2861
|
+
* - Chromium high-contrast / forced-colors: confirm the surface
|
|
2862
|
+
* still reads as a bordered container with the native ::backdrop
|
|
2863
|
+
* dim.
|
|
2864
|
+
*/
|
|
2865
|
+
"ds-dialog": LocalJSX.IntrinsicElements["ds-dialog"] & JSXBase.HTMLAttributes<HTMLDsDialogElement>;
|
|
2866
|
+
/**
|
|
2867
|
+
* ## Accessibility
|
|
2868
|
+
* **Category:** form-field
|
|
2869
|
+
* **APG pattern:** N/A — native element. This wraps a native `<input>`;
|
|
2870
|
+
* there is no WAI-ARIA APG pattern for a plain text input because the
|
|
2871
|
+
* browser already implements it natively. Reference:
|
|
2872
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
|
|
2873
|
+
* **Keyboard interactions:**
|
|
2874
|
+
* - Tab: moves focus to the native `<input>` (shadow DOM does not
|
|
2875
|
+
* interrupt tab order).
|
|
2876
|
+
* - Printable characters: insert at the caret position (native).
|
|
2877
|
+
* - Backspace / Delete: native editing.
|
|
2878
|
+
* - Arrow keys: move the caret within the value (native); for
|
|
2879
|
+
* `type="number"` Up/Down step the numeric value (native).
|
|
2880
|
+
* - Enter: on `type="search"` may submit the enclosing form (native);
|
|
2881
|
+
* no custom handling.
|
|
2882
|
+
* - Escape: no native meaning for a text input.
|
|
2883
|
+
* **ARIA:**
|
|
2884
|
+
* - Roles used: none explicit — the native `<input>` supplies its
|
|
2885
|
+
* implicit role (`textbox`, `searchbox`, etc. depending on `type`).
|
|
2886
|
+
* - State attributes: `disabled` (native attribute, not `aria-disabled`),
|
|
2887
|
+
* `aria-invalid` (from the `invalid` prop), `aria-required` (from the
|
|
2888
|
+
* `required` prop), `aria-describedby` (from the `describedBy` prop,
|
|
2889
|
+
* expected to reference a light-DOM node id — typically a hint or
|
|
2890
|
+
* error message rendered by the consumer alongside the field).
|
|
2891
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto the
|
|
2892
|
+
* internal `<input>`. This is the **canonical, recommended** mode —
|
|
2893
|
+
* explicit and independent of DOM structure. Consumers MUST pass
|
|
2894
|
+
* `label` (or otherwise give the field an accessible name) — an
|
|
2895
|
+
* `<input>` with no accessible name is a WCAG failure (axe rule
|
|
2896
|
+
* `label`). Wrapping the host in a light-DOM `<label>` (e.g.
|
|
2897
|
+
* `<label>Email <ds-input></ds-input></label>`) also produces an
|
|
2898
|
+
* accessible name in Chromium via flattened-tree resolution — the
|
|
2899
|
+
* same secondary mode documented on `ds-select`, verified in this
|
|
2900
|
+
* component's e2e tests. Setting `aria-label` directly on the
|
|
2901
|
+
* `<ds-input>` host does NOT work: shadow-DOM attribute forwarding
|
|
2902
|
+
* would be required — use the `label` prop instead.
|
|
2903
|
+
* **Focus:**
|
|
2904
|
+
* - On mount: nothing auto-focuses.
|
|
2905
|
+
* - On value change: focus remains on the native `<input>`.
|
|
2906
|
+
* - Focus indicator: the browser default `:focus-visible` ring is kept
|
|
2907
|
+
* on purpose — a themed focus ring using `--ds-color-focus` is a
|
|
2908
|
+
* future decision once the DS ships a shared focus utility. Not
|
|
2909
|
+
* overridden by `ds-input.css`.
|
|
2910
|
+
* **Motion:**
|
|
2911
|
+
* - Animations: none.
|
|
2912
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
2913
|
+
* **Contrast:**
|
|
2914
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends on
|
|
2915
|
+
* the consumer's theme, not verified in isolation here.
|
|
2916
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
2917
|
+
* matching `ds-select` — inherits whatever contrast the host page
|
|
2918
|
+
* sets. WCAG 1.4.11 (3:1 non-text) is the consumer's responsibility
|
|
2919
|
+
* in themes that override the surface.
|
|
2920
|
+
* **Screen reader announcement:**
|
|
2921
|
+
* - Rendered state: announces the `label` prop text as the accessible
|
|
2922
|
+
* name, followed by the browser/AT's role announcement for the
|
|
2923
|
+
* input type ("edit text", "search text", "password", "phone
|
|
2924
|
+
* number", "email"…). When `required=true` the announcement
|
|
2925
|
+
* includes "required"; when `invalid=true` it includes "invalid".
|
|
2926
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
2927
|
+
* character (native input behavior); no `aria-live` added, per the
|
|
2928
|
+
* spec's guidance against live-region overuse.
|
|
2929
|
+
* **Consumer contract:**
|
|
2930
|
+
* - `placeholder` is a hint, not a label. It disappears on focus in
|
|
2931
|
+
* most browsers and is not reliably announced by every AT — always
|
|
2932
|
+
* pair with `label`.
|
|
2933
|
+
* - For `type="password"` in a real form, pair with a light-DOM
|
|
2934
|
+
* `<input type="hidden">` or a form-associated ancestor that gives
|
|
2935
|
+
* the browser's password manager an `autocomplete` hint. This
|
|
2936
|
+
* component intentionally does not expose `autocomplete` in v1;
|
|
2937
|
+
* consumers that need it can render a native `<input>` for that
|
|
2938
|
+
* field until the prop is added.
|
|
2939
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
2940
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
2941
|
+
* status but not the reason.
|
|
2942
|
+
*/
|
|
2943
|
+
"ds-input": LocalJSX.IntrinsicElements["ds-input"] & JSXBase.HTMLAttributes<HTMLDsInputElement>;
|
|
2944
|
+
/**
|
|
2945
|
+
* ## Accessibility
|
|
2946
|
+
* **Category:** static-display (indeterminate progress indicator)
|
|
2947
|
+
* **APG pattern:** N/A — the loader is a passive presence indicator, not a
|
|
2948
|
+
* `progressbar` (no measurable value). Uses `role="status"` per WAI-ARIA
|
|
2949
|
+
* live-region semantics for indeterminate loading states.
|
|
2950
|
+
* **Keyboard interactions:**
|
|
2951
|
+
* - Tab: N/A — not focusable.
|
|
2952
|
+
* - Enter: N/A.
|
|
2953
|
+
* - Escape: N/A.
|
|
2954
|
+
* - Arrow keys: N/A.
|
|
2955
|
+
* - Printable characters: N/A.
|
|
2956
|
+
* **ARIA:**
|
|
2957
|
+
* - Roles used: `role="status"` on the host — an implicit polite live
|
|
2958
|
+
* region ARIA lists as valid for "advisory information" including
|
|
2959
|
+
* in-progress operations. The inner `<svg>` is decorative and carries
|
|
2960
|
+
* `aria-hidden="true"` + `focusable="false"` so it never becomes a
|
|
2961
|
+
* parallel target for screen readers.
|
|
2962
|
+
* - State attributes: none. The loader has no state changes to announce
|
|
2963
|
+
* beyond its own presence; the polite live region on mount is the
|
|
2964
|
+
* announcement.
|
|
2965
|
+
* - Labeling strategy: `aria-label` from the `label` prop (default
|
|
2966
|
+
* "Loading"). Consumers pass a more specific string when the context
|
|
2967
|
+
* warrants ("Loading ceremonies", "Signing in", …).
|
|
2968
|
+
* **Focus:**
|
|
2969
|
+
* - On mount: nothing auto-focuses.
|
|
2970
|
+
* - On activate/open: N/A — not interactive.
|
|
2971
|
+
* - On close/deactivate: N/A.
|
|
2972
|
+
* - Focus indicator: N/A — the host is not focusable.
|
|
2973
|
+
* **Motion:**
|
|
2974
|
+
* - Animations: two mirrored halves of the logo path oscillate
|
|
2975
|
+
* symmetrically around the visual center, meeting at the ring midpoint
|
|
2976
|
+
* and separating again. Cycle 1.4s by default (`speed="normal"`);
|
|
2977
|
+
* `speed="slow"` = 2.2s, `speed="fast"` = 0.9s.
|
|
2978
|
+
* - Reduced-motion behavior: `@media (prefers-reduced-motion: reduce)`
|
|
2979
|
+
* collapses both halves to a static rendering with a slow opacity
|
|
2980
|
+
* pulse (2s ease-in-out). No rotation, no lateral movement.
|
|
2981
|
+
* **Contrast:**
|
|
2982
|
+
* - Text: N/A — the visible surface is the SVG stroke, not text.
|
|
2983
|
+
* - Non-text: stroke inherits `currentColor` from the host. Consumers are
|
|
2984
|
+
* responsible for confirming the stroke against its background meets
|
|
2985
|
+
* the ≥ 3:1 non-text contrast threshold (WCAG 1.4.11) where the loader
|
|
2986
|
+
* conveys state (e.g. distinguishing "still loading" from "idle").
|
|
2987
|
+
* **Screen reader announcement:**
|
|
2988
|
+
* - Rendered state: the polite live region announces the label
|
|
2989
|
+
* (e.g. "Loading") when the loader mounts.
|
|
2990
|
+
* - State change: N/A — the loader itself has no state that changes.
|
|
2991
|
+
* Consumers unmount the loader when the awaited work completes; that
|
|
2992
|
+
* removal is silent, which is the intended behavior (the arriving
|
|
2993
|
+
* content is the announcement).
|
|
2994
|
+
*/
|
|
2995
|
+
"ds-loader": LocalJSX.IntrinsicElements["ds-loader"] & JSXBase.HTMLAttributes<HTMLDsLoaderElement>;
|
|
2996
|
+
"ds-select": LocalJSX.IntrinsicElements["ds-select"] & JSXBase.HTMLAttributes<HTMLDsSelectElement>;
|
|
2997
|
+
"ds-tabs": LocalJSX.IntrinsicElements["ds-tabs"] & JSXBase.HTMLAttributes<HTMLDsTabsElement>;
|
|
2998
|
+
/**
|
|
2999
|
+
* ## Accessibility
|
|
3000
|
+
* **Category:** form-field
|
|
3001
|
+
* **APG pattern:** N/A — native element. This wraps a native
|
|
3002
|
+
* `<textarea>`; there is no WAI-ARIA APG pattern for a plain
|
|
3003
|
+
* multi-line text field because the browser already implements it
|
|
3004
|
+
* natively. Reference:
|
|
3005
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
|
|
3006
|
+
* **Keyboard interactions:**
|
|
3007
|
+
* - Tab: moves focus to the native `<textarea>`; Tab inside the
|
|
3008
|
+
* textarea inserts a tab character only when the browser is
|
|
3009
|
+
* configured to do so (default behavior: Tab moves focus out).
|
|
3010
|
+
* - Printable characters: insert at the caret position (native).
|
|
3011
|
+
* - Enter: inserts a newline within the value (native), does not
|
|
3012
|
+
* submit the enclosing form (unlike a single-line input).
|
|
3013
|
+
* - Backspace / Delete: native editing.
|
|
3014
|
+
* - Arrow keys: move the caret within the value (native).
|
|
3015
|
+
* - Escape: no native meaning for a text area.
|
|
3016
|
+
* **ARIA:**
|
|
3017
|
+
* - Roles used: none explicit — the native `<textarea>` supplies
|
|
3018
|
+
* the implicit `textbox` role with multiline behavior.
|
|
3019
|
+
* - State attributes: `disabled` (native attribute, not
|
|
3020
|
+
* `aria-disabled`), `aria-invalid` (from the `invalid` prop),
|
|
3021
|
+
* `aria-required` (from the `required` prop), `aria-describedby`
|
|
3022
|
+
* (from the `describedBy` prop, expected to reference a light-DOM
|
|
3023
|
+
* node id — typically a hint or error message rendered by the
|
|
3024
|
+
* consumer alongside the field).
|
|
3025
|
+
* - Labeling strategy: `aria-label` set from the `label` prop onto
|
|
3026
|
+
* the internal `<textarea>`. This is the **canonical, recommended**
|
|
3027
|
+
* mode. Wrapping the host in a light-DOM `<label>` also produces
|
|
3028
|
+
* an accessible name in Chromium via flattened-tree resolution —
|
|
3029
|
+
* same secondary mode documented on `ds-input` and `ds-select`,
|
|
3030
|
+
* verified in this component's e2e tests. Setting `aria-label`
|
|
3031
|
+
* directly on the `<ds-textarea>` host does NOT work: shadow-DOM
|
|
3032
|
+
* attribute forwarding would be required — use the `label` prop.
|
|
3033
|
+
* **Focus:**
|
|
3034
|
+
* - On mount: nothing auto-focuses.
|
|
3035
|
+
* - On value change: focus remains on the native `<textarea>`.
|
|
3036
|
+
* - Focus indicator: the browser default `:focus-visible` ring is
|
|
3037
|
+
* kept on purpose — a themed focus ring using `--ds-color-focus`
|
|
3038
|
+
* is a future decision once the DS ships a shared focus utility.
|
|
3039
|
+
* **Motion:**
|
|
3040
|
+
* - Animations: none.
|
|
3041
|
+
* - Reduced-motion behavior: N/A — no animation to collapse.
|
|
3042
|
+
* **Contrast:**
|
|
3043
|
+
* - Text: inherits `color: inherit` / `currentColor`; ratio depends
|
|
3044
|
+
* on the consumer's theme, not verified in isolation.
|
|
3045
|
+
* - Border: `color-mix(in oklch, currentColor 22%, transparent)`,
|
|
3046
|
+
* matching `ds-input`.
|
|
3047
|
+
* **Screen reader announcement:**
|
|
3048
|
+
* - Rendered state: announces the `label` prop text as the
|
|
3049
|
+
* accessible name, followed by "edit text, multiline" (browser/AT
|
|
3050
|
+
* wording varies). When `required=true` the announcement includes
|
|
3051
|
+
* "required"; when `invalid=true` it includes "invalid".
|
|
3052
|
+
* - State change: on value edit, AT typically echoes the typed
|
|
3053
|
+
* character (native behavior); no `aria-live` added.
|
|
3054
|
+
* **Consumer contract:**
|
|
3055
|
+
* - `placeholder` is a hint, not a label — always pair with `label`.
|
|
3056
|
+
* - When `invalid=true`, pair with a `describedBy` id that points at
|
|
3057
|
+
* a visible error message; `aria-invalid` alone gives the AT the
|
|
3058
|
+
* status but not the reason.
|
|
3059
|
+
* - `rows` sets the initial visible line count. Vertical resize is
|
|
3060
|
+
* left to the native default (`resize: vertical`) so users can
|
|
3061
|
+
* grow the field for long entries; the consumer's CSS can pin it
|
|
3062
|
+
* with `resize: none` when a fixed height matters.
|
|
3063
|
+
*/
|
|
3064
|
+
"ds-textarea": LocalJSX.IntrinsicElements["ds-textarea"] & JSXBase.HTMLAttributes<HTMLDsTextareaElement>;
|
|
3065
|
+
"ds-tooltip": LocalJSX.IntrinsicElements["ds-tooltip"] & JSXBase.HTMLAttributes<HTMLDsTooltipElement>;
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
}
|