@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 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
- ```js
20
- import '@compa11y/web';
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
- ### Dialog
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
- <a11y-dialog trigger="#open-dialog">
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
- <p>This action cannot be undone.</p>
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 onclick="this.closest('a11y-dialog').close()">Cancel</button>
38
- <button onclick="handleConfirm()">Confirm</button>
31
+ <button>Cancel</button>
32
+ <button>Confirm</button>
39
33
  </div>
40
34
  </a11y-dialog>
41
35
  ```
42
36
 
43
- #### Attributes
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
- - Chrome/Edge 88+
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