@compa11y/web 0.1.3 → 0.1.6
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/README.md +17 -831
- package/dist/compa11y.iife.js +207 -88
- package/dist/compa11y.js +1253 -993
- package/dist/components/listbox.d.ts +3 -3
- package/dist/components/menu.d.ts +3 -0
- package/dist/components/tabs.d.ts +5 -0
- package/dist/components/toast.d.ts +48 -0
- package/dist/index.d.ts +2 -1
- package/dist/utils/styles.d.ts +5 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,856 +1,42 @@
|
|
|
1
1
|
# @compa11y/web
|
|
2
2
|
|
|
3
|
-
Accessible Web Components for any HTML page. No framework required.
|
|
3
|
+
Accessible Web Components for any HTML page. No framework required. Every component handles ARIA attributes, keyboard navigation, focus management, and screen reader announcements under the hood.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
### CDN (Recommended for quick start)
|
|
8
|
-
|
|
9
|
-
```html
|
|
10
|
-
<script src="https://unpkg.com/@compa11y/web"></script>
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### npm
|
|
14
|
-
|
|
15
7
|
```bash
|
|
16
8
|
npm install @compa11y/web
|
|
17
9
|
```
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
Or via CDN:
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<script src="https://unpkg.com/@compa11y/web"></script>
|
|
21
15
|
```
|
|
22
16
|
|
|
23
17
|
## Components
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```html
|
|
28
|
-
<button id="open-dialog">Open Dialog</button>
|
|
19
|
+
`<a11y-dialog>`, `<a11y-menu>`, `<a11y-tabs>`, `<a11y-toast>`, `<a11y-combobox>`, `<a11y-select>`, `<a11y-listbox>`, `<a11y-checkbox>`, `<a11y-radio-group>`, `<a11y-switch>`, `<a11y-input>`, `<a11y-textarea>`, `<a11y-button>`
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
<h2 slot="title">Confirm Action</h2>
|
|
32
|
-
<p slot="description">Are you sure you want to proceed?</p>
|
|
21
|
+
## Quick start
|
|
33
22
|
|
|
34
|
-
|
|
23
|
+
```html
|
|
24
|
+
<!-- Fully accessible dialog — focus trap, Escape to close, screen reader announcements -->
|
|
25
|
+
<button id="open-btn">Open Dialog</button>
|
|
35
26
|
|
|
27
|
+
<a11y-dialog trigger="#open-btn">
|
|
28
|
+
<h2 slot="title">Confirm</h2>
|
|
29
|
+
<p>Are you sure?</p>
|
|
36
30
|
<div slot="actions">
|
|
37
|
-
<button
|
|
38
|
-
<button
|
|
31
|
+
<button>Cancel</button>
|
|
32
|
+
<button>Confirm</button>
|
|
39
33
|
</div>
|
|
40
34
|
</a11y-dialog>
|
|
41
35
|
```
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
| Attribute | Description | Default |
|
|
46
|
-
| ------------------------ | ------------------------------- | ------- |
|
|
47
|
-
| `trigger` | CSS selector for trigger button | — |
|
|
48
|
-
| `open` | Whether dialog is open | `false` |
|
|
49
|
-
| `close-on-outside-click` | Close when clicking overlay | `true` |
|
|
50
|
-
| `close-on-escape` | Close when pressing Escape | `true` |
|
|
51
|
-
|
|
52
|
-
#### Slots
|
|
53
|
-
|
|
54
|
-
| Slot | Description |
|
|
55
|
-
| ------------- | ----------------------------------------- |
|
|
56
|
-
| `title` | Dialog title (required for accessibility) |
|
|
57
|
-
| `description` | Optional description |
|
|
58
|
-
| (default) | Dialog content |
|
|
59
|
-
| `actions` | Footer buttons |
|
|
60
|
-
|
|
61
|
-
#### Methods
|
|
62
|
-
|
|
63
|
-
```js
|
|
64
|
-
const dialog = document.querySelector('a11y-dialog');
|
|
65
|
-
dialog.show(); // Open
|
|
66
|
-
dialog.close(); // Close
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
#### Events
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
dialog.addEventListener('a11y-dialog-open', () => {});
|
|
73
|
-
dialog.addEventListener('a11y-dialog-close', () => {});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Menu
|
|
77
|
-
|
|
78
|
-
```html
|
|
79
|
-
<a11y-menu>
|
|
80
|
-
<button slot="trigger">Actions ▾</button>
|
|
81
|
-
|
|
82
|
-
<button role="menuitem">Edit</button>
|
|
83
|
-
<button role="menuitem">Duplicate</button>
|
|
84
|
-
<div role="separator"></div>
|
|
85
|
-
<button role="menuitem">Delete</button>
|
|
86
|
-
</a11y-menu>
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
#### Attributes
|
|
90
|
-
|
|
91
|
-
| Attribute | Description | Default |
|
|
92
|
-
| --------- | -------------------- | ------- |
|
|
93
|
-
| `open` | Whether menu is open | `false` |
|
|
94
|
-
|
|
95
|
-
#### Methods
|
|
96
|
-
|
|
97
|
-
```js
|
|
98
|
-
const menu = document.querySelector('a11y-menu');
|
|
99
|
-
menu.show(); // Open
|
|
100
|
-
menu.close(); // Close
|
|
101
|
-
menu.toggle(); // Toggle
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
#### Events
|
|
105
|
-
|
|
106
|
-
```js
|
|
107
|
-
menu.addEventListener('a11y-menu-open', () => {});
|
|
108
|
-
menu.addEventListener('a11y-menu-close', () => {});
|
|
109
|
-
menu.addEventListener('a11y-menu-select', (e) => {
|
|
110
|
-
console.log(e.detail.item);
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Tabs
|
|
115
|
-
|
|
116
|
-
```html
|
|
117
|
-
<a11y-tabs>
|
|
118
|
-
<button role="tab" aria-controls="panel-1">Tab 1</button>
|
|
119
|
-
<button role="tab" aria-controls="panel-2">Tab 2</button>
|
|
120
|
-
<button role="tab" aria-controls="panel-3">Tab 3</button>
|
|
121
|
-
|
|
122
|
-
<div role="tabpanel" id="panel-1">Content 1</div>
|
|
123
|
-
<div role="tabpanel" id="panel-2">Content 2</div>
|
|
124
|
-
<div role="tabpanel" id="panel-3">Content 3</div>
|
|
125
|
-
</a11y-tabs>
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
#### Attributes
|
|
129
|
-
|
|
130
|
-
| Attribute | Description | Default |
|
|
131
|
-
| ----------------- | ---------------------------- | ------------ |
|
|
132
|
-
| `orientation` | `horizontal` or `vertical` | `horizontal` |
|
|
133
|
-
| `activation-mode` | `automatic` or `manual` | `automatic` |
|
|
134
|
-
| `selected-index` | Currently selected tab index | `0` |
|
|
135
|
-
|
|
136
|
-
#### Methods
|
|
137
|
-
|
|
138
|
-
```js
|
|
139
|
-
const tabs = document.querySelector('a11y-tabs');
|
|
140
|
-
tabs.select(2); // Select by index
|
|
141
|
-
tabs.next(); // Select next tab
|
|
142
|
-
tabs.previous(); // Select previous tab
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
#### Events
|
|
146
|
-
|
|
147
|
-
```js
|
|
148
|
-
tabs.addEventListener('a11y-tabs-change', (e) => {
|
|
149
|
-
console.log(e.detail.index, e.detail.tab, e.detail.panel);
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Combobox
|
|
154
|
-
|
|
155
|
-
```html
|
|
156
|
-
<a11y-combobox
|
|
157
|
-
label="Choose a country"
|
|
158
|
-
placeholder="Search countries..."
|
|
159
|
-
></a11y-combobox>
|
|
160
|
-
|
|
161
|
-
<script>
|
|
162
|
-
const combobox = document.querySelector('a11y-combobox');
|
|
163
|
-
combobox.options = [
|
|
164
|
-
{ value: 'us', label: 'United States' },
|
|
165
|
-
{ value: 'uk', label: 'United Kingdom' },
|
|
166
|
-
{ value: 'ca', label: 'Canada' },
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
combobox.addEventListener('change', (e) => {
|
|
170
|
-
console.log('Selected:', e.detail.value);
|
|
171
|
-
});
|
|
172
|
-
</script>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
#### Attributes
|
|
176
|
-
|
|
177
|
-
| Attribute | Description | Default |
|
|
178
|
-
| --------------- | ------------------------------------- | -------------------- |
|
|
179
|
-
| `label` | Label text (required for a11y) | — |
|
|
180
|
-
| `placeholder` | Input placeholder | — |
|
|
181
|
-
| `value` | Currently selected value | — |
|
|
182
|
-
| `disabled` | Disable the combobox | `false` |
|
|
183
|
-
| `clearable` | Show clear button when value selected | `false` |
|
|
184
|
-
| `empty-message` | Message when no options match | `'No results found'` |
|
|
185
|
-
|
|
186
|
-
#### Properties
|
|
187
|
-
|
|
188
|
-
```js
|
|
189
|
-
const combobox = document.querySelector('a11y-combobox');
|
|
190
|
-
combobox.options = [...]; // Set options
|
|
191
|
-
combobox.value = 'us'; // Set value
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
#### Events
|
|
195
|
-
|
|
196
|
-
```js
|
|
197
|
-
combobox.addEventListener('change', (e) => {
|
|
198
|
-
console.log(e.detail.value, e.detail.option);
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Select
|
|
203
|
-
|
|
204
|
-
```html
|
|
205
|
-
<a11y-select aria-label="Choose a fruit" placeholder="Pick a fruit...">
|
|
206
|
-
<option value="apple">Apple</option>
|
|
207
|
-
<option value="banana">Banana</option>
|
|
208
|
-
<option value="cherry">Cherry</option>
|
|
209
|
-
<option value="dragonfruit" disabled>Dragon Fruit (unavailable)</option>
|
|
210
|
-
<option value="elderberry">Elderberry</option>
|
|
211
|
-
</a11y-select>
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
#### Attributes
|
|
215
|
-
|
|
216
|
-
| Attribute | Description | Default |
|
|
217
|
-
| ----------------- | ------------------------ | ----------------------- |
|
|
218
|
-
| `placeholder` | Trigger placeholder text | `'Select an option...'` |
|
|
219
|
-
| `value` | Currently selected value | — |
|
|
220
|
-
| `disabled` | Disable the select | `false` |
|
|
221
|
-
| `aria-label` | Accessible label | — |
|
|
222
|
-
| `aria-labelledby` | ID of labelling element | — |
|
|
223
|
-
|
|
224
|
-
#### Properties
|
|
225
|
-
|
|
226
|
-
```js
|
|
227
|
-
const select = document.querySelector('a11y-select');
|
|
228
|
-
select.value = 'apple'; // Set value programmatically
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
#### Methods
|
|
232
|
-
|
|
233
|
-
```js
|
|
234
|
-
const select = document.querySelector('a11y-select');
|
|
235
|
-
select.show(); // Open
|
|
236
|
-
select.close(); // Close
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
#### Events
|
|
240
|
-
|
|
241
|
-
```js
|
|
242
|
-
select.addEventListener('change', (e) => {
|
|
243
|
-
console.log('Value:', e.detail.value);
|
|
244
|
-
console.log('Label:', e.detail.label);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
select.addEventListener('a11y-select-change', (e) => {
|
|
248
|
-
console.log('Selected:', e.detail);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
select.addEventListener('a11y-select-open', () => {});
|
|
252
|
-
select.addEventListener('a11y-select-close', () => {});
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
#### Keyboard Navigation
|
|
256
|
-
|
|
257
|
-
| Key | Action |
|
|
258
|
-
| ----------------- | ----------------------------------------- |
|
|
259
|
-
| `Enter` / `Space` | Open listbox or select highlighted option |
|
|
260
|
-
| `ArrowDown` | Open listbox / move highlight down |
|
|
261
|
-
| `ArrowUp` | Open listbox / move highlight up |
|
|
262
|
-
| `Home` / `End` | Jump to first / last option |
|
|
263
|
-
| `Escape` | Close listbox |
|
|
264
|
-
| `Tab` | Close listbox and move focus |
|
|
265
|
-
| Type characters | Jump to matching option (type-ahead) |
|
|
266
|
-
|
|
267
|
-
### Switch
|
|
268
|
-
|
|
269
|
-
```html
|
|
270
|
-
<a11y-switch label="Enable notifications"></a11y-switch>
|
|
271
|
-
|
|
272
|
-
<!-- Checked by default -->
|
|
273
|
-
<a11y-switch checked label="Dark mode"></a11y-switch>
|
|
274
|
-
|
|
275
|
-
<!-- Disabled -->
|
|
276
|
-
<a11y-switch disabled label="Premium feature"></a11y-switch>
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
#### Attributes
|
|
280
|
-
|
|
281
|
-
| Attribute | Description | Default |
|
|
282
|
-
| ---------- | ------------------------- | ------- |
|
|
283
|
-
| `label` | Label text | — |
|
|
284
|
-
| `checked` | Whether switch is on | `false` |
|
|
285
|
-
| `disabled` | Disable the switch | `false` |
|
|
286
|
-
| `value` | Value for form submission | — |
|
|
287
|
-
| `name` | Name for form submission | — |
|
|
288
|
-
|
|
289
|
-
#### Properties
|
|
290
|
-
|
|
291
|
-
```js
|
|
292
|
-
const switchEl = document.querySelector('a11y-switch');
|
|
293
|
-
switchEl.checked = true; // Set checked state
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
#### Events
|
|
297
|
-
|
|
298
|
-
```js
|
|
299
|
-
switchEl.addEventListener('change', (e) => {
|
|
300
|
-
console.log('Checked:', e.detail.checked);
|
|
301
|
-
console.log('Value:', e.detail.value);
|
|
302
|
-
console.log('Name:', e.detail.name);
|
|
303
|
-
});
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Listbox
|
|
307
|
-
|
|
308
|
-
```html
|
|
309
|
-
<!-- Single select -->
|
|
310
|
-
<a11y-listbox aria-label="Favorite fruit" value="apple">
|
|
311
|
-
<a11y-optgroup label="Citrus">
|
|
312
|
-
<a11y-option value="orange">Orange</a11y-option>
|
|
313
|
-
<a11y-option value="lemon">Lemon</a11y-option>
|
|
314
|
-
<a11y-option value="grapefruit">Grapefruit</a11y-option>
|
|
315
|
-
</a11y-optgroup>
|
|
316
|
-
<a11y-optgroup label="Berries">
|
|
317
|
-
<a11y-option value="strawberry">Strawberry</a11y-option>
|
|
318
|
-
<a11y-option value="blueberry">Blueberry</a11y-option>
|
|
319
|
-
<a11y-option value="raspberry" disabled>Raspberry (sold out)</a11y-option>
|
|
320
|
-
</a11y-optgroup>
|
|
321
|
-
<a11y-option value="apple">Apple</a11y-option>
|
|
322
|
-
<a11y-option value="banana">Banana</a11y-option>
|
|
323
|
-
</a11y-listbox>
|
|
324
|
-
|
|
325
|
-
<!-- Multi select -->
|
|
326
|
-
<a11y-listbox multiple aria-label="Pizza toppings" value="cheese,mushrooms">
|
|
327
|
-
<a11y-option value="cheese">Cheese</a11y-option>
|
|
328
|
-
<a11y-option value="pepperoni">Pepperoni</a11y-option>
|
|
329
|
-
<a11y-option value="mushrooms">Mushrooms</a11y-option>
|
|
330
|
-
<a11y-option value="onions">Onions</a11y-option>
|
|
331
|
-
<a11y-option value="pineapple" disabled>Pineapple (unavailable)</a11y-option>
|
|
332
|
-
</a11y-listbox>
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
#### Listbox Attributes
|
|
336
|
-
|
|
337
|
-
| Attribute | Description | Default |
|
|
338
|
-
| ----------------- | --------------------------------------------- | ---------- |
|
|
339
|
-
| `value` | Selected value(s) (comma-separated for multi) | — |
|
|
340
|
-
| `multiple` | Enable multi-select mode | `false` |
|
|
341
|
-
| `disabled` | Disable all options | `false` |
|
|
342
|
-
| `discoverable` | Keep disabled listbox in tab order | `true` |
|
|
343
|
-
| `orientation` | `horizontal` or `vertical` | `vertical` |
|
|
344
|
-
| `aria-label` | Accessible label | — |
|
|
345
|
-
| `aria-labelledby` | ID of labelling element | — |
|
|
346
|
-
|
|
347
|
-
#### Option Attributes
|
|
348
|
-
|
|
349
|
-
| Attribute | Description | Default |
|
|
350
|
-
| -------------- | --------------------------------- | ------- |
|
|
351
|
-
| `value` | Value for this option | — |
|
|
352
|
-
| `disabled` | Disable this option | `false` |
|
|
353
|
-
| `discoverable` | Keep disabled option in tab order | `true` |
|
|
354
|
-
|
|
355
|
-
#### Optgroup Attributes
|
|
356
|
-
|
|
357
|
-
| Attribute | Description | Default |
|
|
358
|
-
| ---------- | ------------------------------- | ------- |
|
|
359
|
-
| `label` | Group label (visible, required) | — |
|
|
360
|
-
| `disabled` | Disable all options in group | `false` |
|
|
361
|
-
|
|
362
|
-
#### Properties
|
|
363
|
-
|
|
364
|
-
```js
|
|
365
|
-
const listbox = document.querySelector('a11y-listbox');
|
|
366
|
-
listbox.value = 'apple'; // Set selected value (single)
|
|
367
|
-
listbox.value = 'cheese,onions'; // Set selected values (multi, comma-separated)
|
|
368
|
-
listbox.multiple = true; // Toggle multi-select mode
|
|
369
|
-
listbox.disabled = true; // Disable all options
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
#### Events
|
|
373
|
-
|
|
374
|
-
```js
|
|
375
|
-
listbox.addEventListener('change', (e) => {
|
|
376
|
-
console.log('Value:', e.detail.value);
|
|
377
|
-
console.log('Label:', e.detail.label);
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
listbox.addEventListener('a11y-listbox-change', (e) => {
|
|
381
|
-
console.log('Selected:', e.detail);
|
|
382
|
-
});
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
#### Keyboard Navigation
|
|
386
|
-
|
|
387
|
-
| Key | Single Select | Multi Select |
|
|
388
|
-
| ----------------------- | ---------------------------------- | ------------------------------- |
|
|
389
|
-
| `ArrowDown` / `ArrowUp` | Move focus and select | Move focus only |
|
|
390
|
-
| `Home` / `End` | First/last option and select | Move focus only |
|
|
391
|
-
| `Space` | — | Toggle focused option |
|
|
392
|
-
| `Shift+ArrowDown/Up` | — | Move focus and toggle selection |
|
|
393
|
-
| `Ctrl+Shift+Home/End` | — | Select range to first/last |
|
|
394
|
-
| `Ctrl+A` / `Cmd+A` | — | Toggle select all |
|
|
395
|
-
| Type characters | Jump to matching option and select | Jump to matching option |
|
|
396
|
-
|
|
397
|
-
#### CSS Custom Properties
|
|
398
|
-
|
|
399
|
-
```css
|
|
400
|
-
a11y-listbox {
|
|
401
|
-
--compa11y-listbox-bg: white;
|
|
402
|
-
--compa11y-listbox-border: 1px solid #ccc;
|
|
403
|
-
--compa11y-listbox-radius: 6px;
|
|
404
|
-
--compa11y-listbox-max-height: 300px;
|
|
405
|
-
--compa11y-listbox-padding: 4px;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
a11y-option {
|
|
409
|
-
--compa11y-option-padding: 0.5rem 0.75rem;
|
|
410
|
-
--compa11y-option-radius: 4px;
|
|
411
|
-
--compa11y-option-hover-bg: #f5f5f5;
|
|
412
|
-
--compa11y-option-focused-bg: #e6f0ff;
|
|
413
|
-
--compa11y-option-focused-border: #0066cc;
|
|
414
|
-
--compa11y-option-selected-bg: #e6f0ff;
|
|
415
|
-
--compa11y-option-selected-color: #0066cc;
|
|
416
|
-
--compa11y-option-check-color: #0066cc;
|
|
417
|
-
--compa11y-option-disabled-color: #999;
|
|
418
|
-
--compa11y-option-disabled-bg: transparent;
|
|
419
|
-
--compa11y-focus-color: #0066cc;
|
|
420
|
-
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### Input
|
|
424
|
-
|
|
425
|
-
```html
|
|
426
|
-
<a11y-input
|
|
427
|
-
label="Full Name"
|
|
428
|
-
hint="Enter your first and last name"
|
|
429
|
-
required
|
|
430
|
-
placeholder="John Doe"
|
|
431
|
-
type="text"
|
|
432
|
-
></a11y-input>
|
|
433
|
-
|
|
434
|
-
<!-- With error -->
|
|
435
|
-
<a11y-input
|
|
436
|
-
label="Email"
|
|
437
|
-
error="Please enter a valid email"
|
|
438
|
-
type="email"
|
|
439
|
-
></a11y-input>
|
|
440
|
-
|
|
441
|
-
<!-- Read-only -->
|
|
442
|
-
<a11y-input label="User ID" value="USR-12345" readonly></a11y-input>
|
|
443
|
-
|
|
444
|
-
<!-- Disabled -->
|
|
445
|
-
<a11y-input label="Organization" value="Compa11y Inc." disabled></a11y-input>
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
#### Attributes
|
|
449
|
-
|
|
450
|
-
| Attribute | Description | Default |
|
|
451
|
-
| ----------------- | ------------------------ | -------- |
|
|
452
|
-
| `label` | Visible label text | — |
|
|
453
|
-
| `hint` | Hint/description text | — |
|
|
454
|
-
| `error` | Error message text | — |
|
|
455
|
-
| `type` | Input type | `'text'` |
|
|
456
|
-
| `placeholder` | Placeholder text | — |
|
|
457
|
-
| `value` | Current value | — |
|
|
458
|
-
| `disabled` | Disable the input | `false` |
|
|
459
|
-
| `readonly` | Read-only input | `false` |
|
|
460
|
-
| `required` | Required field | `false` |
|
|
461
|
-
| `name` | Name for form submission | — |
|
|
462
|
-
| `aria-label` | Accessible label | — |
|
|
463
|
-
| `aria-labelledby` | ID of labelling element | — |
|
|
464
|
-
|
|
465
|
-
#### Properties
|
|
466
|
-
|
|
467
|
-
```js
|
|
468
|
-
const input = document.querySelector('a11y-input');
|
|
469
|
-
input.value = 'Hello'; // Set value
|
|
470
|
-
input.error = 'Required'; // Set error (shows role="alert")
|
|
471
|
-
input.error = ''; // Clear error
|
|
472
|
-
input.disabled = true; // Disable
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
#### Methods
|
|
476
|
-
|
|
477
|
-
```js
|
|
478
|
-
const input = document.querySelector('a11y-input');
|
|
479
|
-
input.focus(); // Focus the input
|
|
480
|
-
input.blur(); // Blur the input
|
|
481
|
-
input.select(); // Select all text
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
#### Events
|
|
485
|
-
|
|
486
|
-
```js
|
|
487
|
-
input.addEventListener('input', (e) => {
|
|
488
|
-
console.log('Value:', e.detail.value);
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
input.addEventListener('change', (e) => {
|
|
492
|
-
console.log('Final value:', e.detail.value);
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
input.addEventListener('a11y-input-focus', () => {});
|
|
496
|
-
input.addEventListener('a11y-input-blur', () => {});
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Textarea
|
|
500
|
-
|
|
501
|
-
```html
|
|
502
|
-
<a11y-textarea
|
|
503
|
-
label="Description"
|
|
504
|
-
hint="Provide at least 10 characters"
|
|
505
|
-
required
|
|
506
|
-
rows="4"
|
|
507
|
-
placeholder="Enter a description..."
|
|
508
|
-
></a11y-textarea>
|
|
509
|
-
|
|
510
|
-
<!-- With error -->
|
|
511
|
-
<a11y-textarea label="Bio" error="Bio is required" rows="5"></a11y-textarea>
|
|
512
|
-
|
|
513
|
-
<!-- Read-only -->
|
|
514
|
-
<a11y-textarea
|
|
515
|
-
label="Terms"
|
|
516
|
-
value="Read only content..."
|
|
517
|
-
readonly
|
|
518
|
-
rows="3"
|
|
519
|
-
></a11y-textarea>
|
|
520
|
-
|
|
521
|
-
<!-- Disabled -->
|
|
522
|
-
<a11y-textarea
|
|
523
|
-
label="Notes"
|
|
524
|
-
value="Disabled content"
|
|
525
|
-
disabled
|
|
526
|
-
rows="2"
|
|
527
|
-
></a11y-textarea>
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
#### Attributes
|
|
531
|
-
|
|
532
|
-
| Attribute | Description | Default |
|
|
533
|
-
| ----------------- | ------------------------ | ------------ |
|
|
534
|
-
| `label` | Visible label text | — |
|
|
535
|
-
| `hint` | Hint/description text | — |
|
|
536
|
-
| `error` | Error message text | — |
|
|
537
|
-
| `rows` | Number of visible rows | `3` |
|
|
538
|
-
| `resize` | Resize behavior | `'vertical'` |
|
|
539
|
-
| `placeholder` | Placeholder text | — |
|
|
540
|
-
| `value` | Current value | — |
|
|
541
|
-
| `disabled` | Disable the textarea | `false` |
|
|
542
|
-
| `readonly` | Read-only textarea | `false` |
|
|
543
|
-
| `required` | Required field | `false` |
|
|
544
|
-
| `name` | Name for form submission | — |
|
|
545
|
-
| `aria-label` | Accessible label | — |
|
|
546
|
-
| `aria-labelledby` | ID of labelling element | — |
|
|
547
|
-
|
|
548
|
-
#### Properties
|
|
549
|
-
|
|
550
|
-
```js
|
|
551
|
-
const textarea = document.querySelector('a11y-textarea');
|
|
552
|
-
textarea.value = 'Hello'; // Set value
|
|
553
|
-
textarea.error = 'Required'; // Set error (shows role="alert")
|
|
554
|
-
textarea.error = ''; // Clear error
|
|
555
|
-
textarea.disabled = true; // Disable
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
#### Methods
|
|
559
|
-
|
|
560
|
-
```js
|
|
561
|
-
const textarea = document.querySelector('a11y-textarea');
|
|
562
|
-
textarea.focus(); // Focus the textarea
|
|
563
|
-
textarea.blur(); // Blur the textarea
|
|
564
|
-
textarea.select(); // Select all text
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
#### Events
|
|
568
|
-
|
|
569
|
-
```js
|
|
570
|
-
textarea.addEventListener('input', (e) => {
|
|
571
|
-
console.log('Value:', e.detail.value);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
textarea.addEventListener('change', (e) => {
|
|
575
|
-
console.log('Final value:', e.detail.value);
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
textarea.addEventListener('a11y-textarea-focus', () => {});
|
|
579
|
-
textarea.addEventListener('a11y-textarea-blur', () => {});
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
### Button
|
|
583
|
-
|
|
584
|
-
```html
|
|
585
|
-
<a11y-button variant="primary">Save</a11y-button>
|
|
586
|
-
<a11y-button variant="danger">Delete</a11y-button>
|
|
587
|
-
<a11y-button variant="outline">Cancel</a11y-button>
|
|
588
|
-
|
|
589
|
-
<!-- Loading state -->
|
|
590
|
-
<a11y-button variant="primary" loading>Saving...</a11y-button>
|
|
591
|
-
|
|
592
|
-
<!-- Disabled but discoverable (stays in tab order) -->
|
|
593
|
-
<a11y-button variant="primary" disabled discoverable>Unavailable</a11y-button>
|
|
594
|
-
|
|
595
|
-
<!-- Sizes -->
|
|
596
|
-
<a11y-button variant="primary" size="sm">Small</a11y-button>
|
|
597
|
-
<a11y-button variant="primary" size="md">Medium</a11y-button>
|
|
598
|
-
<a11y-button variant="primary" size="lg">Large</a11y-button>
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
#### Attributes
|
|
602
|
-
|
|
603
|
-
| Attribute | Description | Default |
|
|
604
|
-
| -------------- | ----------------------------------------------------------- | ------------- |
|
|
605
|
-
| `variant` | Visual variant (primary, secondary, danger, outline, ghost) | `'secondary'` |
|
|
606
|
-
| `size` | Size (sm, md, lg) | `'md'` |
|
|
607
|
-
| `type` | Button type (button, submit, reset) | `'button'` |
|
|
608
|
-
| `disabled` | Disable the button | `false` |
|
|
609
|
-
| `discoverable` | Keep disabled button in tab order | `false` |
|
|
610
|
-
| `loading` | Loading state (shows spinner, aria-busy) | `false` |
|
|
611
|
-
| `aria-label` | Accessible label | — |
|
|
612
|
-
|
|
613
|
-
#### Properties
|
|
614
|
-
|
|
615
|
-
```js
|
|
616
|
-
const btn = document.querySelector('a11y-button');
|
|
617
|
-
btn.disabled = true; // Disable
|
|
618
|
-
btn.loading = true; // Set loading
|
|
619
|
-
btn.variant = 'danger'; // Change variant
|
|
620
|
-
btn.size = 'lg'; // Change size
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
#### Methods
|
|
624
|
-
|
|
625
|
-
```js
|
|
626
|
-
const btn = document.querySelector('a11y-button');
|
|
627
|
-
btn.focus(); // Focus the button
|
|
628
|
-
btn.blur(); // Blur the button
|
|
629
|
-
btn.click(); // Programmatic click
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
#### Events
|
|
633
|
-
|
|
634
|
-
```js
|
|
635
|
-
btn.addEventListener('a11y-button-click', () => {
|
|
636
|
-
console.log('Button clicked');
|
|
637
|
-
});
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
#### CSS Custom Properties
|
|
641
|
-
|
|
642
|
-
```css
|
|
643
|
-
a11y-button {
|
|
644
|
-
--compa11y-button-radius: 4px;
|
|
645
|
-
--compa11y-button-font-weight: 500;
|
|
646
|
-
--compa11y-button-disabled-opacity: 0.5;
|
|
647
|
-
--compa11y-button-primary-bg: #0066cc;
|
|
648
|
-
--compa11y-button-primary-color: white;
|
|
649
|
-
--compa11y-button-danger-bg: #ef4444;
|
|
650
|
-
--compa11y-button-danger-color: white;
|
|
651
|
-
--compa11y-focus-color: #0066cc;
|
|
652
|
-
}
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
## Styling
|
|
656
|
-
|
|
657
|
-
Use CSS custom properties for theming:
|
|
658
|
-
|
|
659
|
-
```css
|
|
660
|
-
/* Dialog */
|
|
661
|
-
a11y-dialog {
|
|
662
|
-
--compa11y-dialog-bg: white;
|
|
663
|
-
--compa11y-dialog-radius: 8px;
|
|
664
|
-
--compa11y-dialog-padding: 1.5rem;
|
|
665
|
-
--compa11y-dialog-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
|
|
666
|
-
--compa11y-dialog-overlay-bg: rgba(0, 0, 0, 0.5);
|
|
667
|
-
--compa11y-dialog-z-index: 9999;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/* Menu */
|
|
671
|
-
a11y-menu {
|
|
672
|
-
--compa11y-menu-bg: white;
|
|
673
|
-
--compa11y-menu-border: 1px solid #e0e0e0;
|
|
674
|
-
--compa11y-menu-radius: 4px;
|
|
675
|
-
--compa11y-menu-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
676
|
-
--compa11y-menu-item-hover-bg: #f5f5f5;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/* Tabs */
|
|
680
|
-
a11y-tabs {
|
|
681
|
-
--compa11y-tabs-border: 1px solid #e0e0e0;
|
|
682
|
-
--compa11y-tab-padding: 0.75rem 1rem;
|
|
683
|
-
--compa11y-tab-color: #666;
|
|
684
|
-
--compa11y-tab-active-color: #0066cc;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/* Combobox */
|
|
688
|
-
a11y-combobox {
|
|
689
|
-
--compa11y-combobox-width: 300px;
|
|
690
|
-
--compa11y-combobox-border: 1px solid #ccc;
|
|
691
|
-
--compa11y-combobox-radius: 4px;
|
|
692
|
-
--compa11y-combobox-option-hover-bg: #f5f5f5;
|
|
693
|
-
--compa11y-combobox-option-selected-bg: #e6f0ff;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/* Select */
|
|
697
|
-
a11y-select {
|
|
698
|
-
--compa11y-select-width: 300px;
|
|
699
|
-
--compa11y-select-border: 1px solid #ccc;
|
|
700
|
-
--compa11y-select-radius: 4px;
|
|
701
|
-
--compa11y-select-bg: white;
|
|
702
|
-
--compa11y-select-placeholder-color: #999;
|
|
703
|
-
--compa11y-select-chevron-color: #666;
|
|
704
|
-
--compa11y-select-option-hover-bg: #f5f5f5;
|
|
705
|
-
--compa11y-select-option-selected-bg: #e6f0ff;
|
|
706
|
-
--compa11y-select-check-color: #0066cc;
|
|
707
|
-
--compa11y-select-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/* Switch */
|
|
711
|
-
a11y-switch {
|
|
712
|
-
--compa11y-switch-bg: #d1d5db;
|
|
713
|
-
--compa11y-switch-checked-bg: #0066cc;
|
|
714
|
-
--compa11y-switch-thumb-bg: white;
|
|
715
|
-
--compa11y-switch-width: 2.75rem;
|
|
716
|
-
--compa11y-switch-height: 1.5rem;
|
|
717
|
-
--compa11y-switch-thumb-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
/* Input */
|
|
721
|
-
a11y-input {
|
|
722
|
-
--compa11y-input-border: 1px solid #ccc;
|
|
723
|
-
--compa11y-input-border-focus: #0066cc;
|
|
724
|
-
--compa11y-input-border-error: #ef4444;
|
|
725
|
-
--compa11y-input-bg: white;
|
|
726
|
-
--compa11y-input-radius: 4px;
|
|
727
|
-
--compa11y-input-padding: 0.5rem 0.75rem;
|
|
728
|
-
--compa11y-input-font-size: 0.875rem;
|
|
729
|
-
--compa11y-input-label-color: inherit;
|
|
730
|
-
--compa11y-input-label-size: 0.875rem;
|
|
731
|
-
--compa11y-input-label-weight: 500;
|
|
732
|
-
--compa11y-input-hint-color: #666;
|
|
733
|
-
--compa11y-input-hint-size: 0.8125rem;
|
|
734
|
-
--compa11y-input-error-color: #ef4444;
|
|
735
|
-
--compa11y-input-error-size: 0.8125rem;
|
|
736
|
-
--compa11y-input-required-color: #ef4444;
|
|
737
|
-
--compa11y-input-disabled-bg: #f5f5f5;
|
|
738
|
-
--compa11y-input-placeholder-color: #999;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
/* Textarea */
|
|
742
|
-
a11y-textarea {
|
|
743
|
-
--compa11y-textarea-border: 1px solid #ccc;
|
|
744
|
-
--compa11y-textarea-border-focus: #0066cc;
|
|
745
|
-
--compa11y-textarea-border-error: #ef4444;
|
|
746
|
-
--compa11y-textarea-bg: white;
|
|
747
|
-
--compa11y-textarea-radius: 4px;
|
|
748
|
-
--compa11y-textarea-padding: 0.5rem 0.75rem;
|
|
749
|
-
--compa11y-textarea-font-size: 0.875rem;
|
|
750
|
-
--compa11y-textarea-resize: vertical;
|
|
751
|
-
--compa11y-textarea-label-color: inherit;
|
|
752
|
-
--compa11y-textarea-label-size: 0.875rem;
|
|
753
|
-
--compa11y-textarea-label-weight: 500;
|
|
754
|
-
--compa11y-textarea-hint-color: #666;
|
|
755
|
-
--compa11y-textarea-hint-size: 0.8125rem;
|
|
756
|
-
--compa11y-textarea-error-color: #ef4444;
|
|
757
|
-
--compa11y-textarea-error-size: 0.8125rem;
|
|
758
|
-
--compa11y-textarea-required-color: #ef4444;
|
|
759
|
-
--compa11y-textarea-disabled-bg: #f5f5f5;
|
|
760
|
-
--compa11y-textarea-placeholder-color: #999;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
/* Button */
|
|
764
|
-
a11y-button {
|
|
765
|
-
--compa11y-button-radius: 4px;
|
|
766
|
-
--compa11y-button-font-weight: 500;
|
|
767
|
-
--compa11y-button-disabled-opacity: 0.5;
|
|
768
|
-
--compa11y-button-primary-bg: #0066cc;
|
|
769
|
-
--compa11y-button-primary-color: white;
|
|
770
|
-
--compa11y-button-danger-bg: #ef4444;
|
|
771
|
-
--compa11y-button-danger-color: white;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/* Listbox */
|
|
775
|
-
a11y-listbox {
|
|
776
|
-
--compa11y-listbox-bg: white;
|
|
777
|
-
--compa11y-listbox-border: 1px solid #ccc;
|
|
778
|
-
--compa11y-listbox-radius: 6px;
|
|
779
|
-
--compa11y-listbox-max-height: 300px;
|
|
780
|
-
--compa11y-listbox-padding: 4px;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
a11y-option {
|
|
784
|
-
--compa11y-option-padding: 0.5rem 0.75rem;
|
|
785
|
-
--compa11y-option-radius: 4px;
|
|
786
|
-
--compa11y-option-hover-bg: #f5f5f5;
|
|
787
|
-
--compa11y-option-focused-bg: #e6f0ff;
|
|
788
|
-
--compa11y-option-selected-bg: #e6f0ff;
|
|
789
|
-
--compa11y-option-check-color: #0066cc;
|
|
790
|
-
--compa11y-option-disabled-color: #999;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
/* Focus ring */
|
|
794
|
-
:root {
|
|
795
|
-
--compa11y-focus-color: #0066cc;
|
|
796
|
-
}
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
Use `::part()` for Shadow DOM styling:
|
|
800
|
-
|
|
801
|
-
```css
|
|
802
|
-
a11y-dialog::part(overlay) {
|
|
803
|
-
backdrop-filter: blur(4px);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
a11y-dialog::part(dialog) {
|
|
807
|
-
max-width: 600px;
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
a11y-dialog::part(close-button) {
|
|
811
|
-
color: #666;
|
|
812
|
-
}
|
|
813
|
-
```
|
|
814
|
-
|
|
815
|
-
## JavaScript API
|
|
816
|
-
|
|
817
|
-
```js
|
|
818
|
-
import {
|
|
819
|
-
// Announcer
|
|
820
|
-
announce,
|
|
821
|
-
announcePolite,
|
|
822
|
-
announceAssertive,
|
|
823
|
-
|
|
824
|
-
// Focus
|
|
825
|
-
createFocusTrap,
|
|
826
|
-
createFocusScope,
|
|
827
|
-
|
|
828
|
-
// Keyboard
|
|
829
|
-
createKeyboardManager,
|
|
830
|
-
KeyboardPatterns,
|
|
831
|
-
|
|
832
|
-
// ARIA
|
|
833
|
-
aria,
|
|
834
|
-
|
|
835
|
-
// Platform
|
|
836
|
-
prefersReducedMotion,
|
|
837
|
-
prefersHighContrast,
|
|
838
|
-
} from '@compa11y/web';
|
|
839
|
-
|
|
840
|
-
// Make announcements
|
|
841
|
-
announcePolite('Item added to cart');
|
|
842
|
-
|
|
843
|
-
// Check preferences
|
|
844
|
-
if (prefersReducedMotion()) {
|
|
845
|
-
// Disable animations
|
|
846
|
-
}
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
## Browser Support
|
|
37
|
+
## Documentation
|
|
850
38
|
|
|
851
|
-
|
|
852
|
-
- Firefox 78+
|
|
853
|
-
- Safari 14+
|
|
39
|
+
Full documentation, live examples, attributes/events reference, and accessibility details at **[compa11y.org](https://compa11y.org)**.
|
|
854
40
|
|
|
855
41
|
## License
|
|
856
42
|
|