accessible-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1038 -0
- package/package.json +81 -0
- package/src/README.md +2240 -0
- package/src/css/a11y-accordion.core.css +94 -0
- package/src/css/a11y-accordion.theme.css +246 -0
- package/src/css/a11y-dropdown.core.css +169 -0
- package/src/css/a11y-dropdown.theme.css +175 -0
- package/src/css/a11y-modal.core.css +136 -0
- package/src/css/a11y-modal.theme.css +218 -0
- package/src/css/a11y-offcanvas.core.css +122 -0
- package/src/css/a11y-offcanvas.theme.css +170 -0
- package/src/css/a11y-tabs.core.css +120 -0
- package/src/css/a11y-tabs.theme.css +312 -0
- package/src/js/a11y-accordion.js +447 -0
- package/src/js/a11y-dropdown.js +353 -0
- package/src/js/a11y-modal.js +290 -0
- package/src/js/a11y-offcanvas.js +298 -0
- package/src/js/a11y-tabs.js +336 -0
- package/src/js/index.js +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
# Accessible Kit
|
|
2
|
+
|
|
3
|
+
<!-- Badges will be added when published -->
|
|
4
|
+
[](https://www.npmjs.com/package/accessible-kit)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://bundlephobia.com/package/accessible-kit)
|
|
7
|
+
|
|
8
|
+
> Lightweight, accessible UI component library with full ARIA support. Zero dependencies, vanilla JavaScript.
|
|
9
|
+
|
|
10
|
+
A collection of fully accessible UI components for modern web applications. Built with a focus on keyboard navigation, screen reader support, and following WAI-ARIA best practices.
|
|
11
|
+
|
|
12
|
+
## âĻ Features
|
|
13
|
+
|
|
14
|
+
- **ðŊ Fully Accessible** - Complete ARIA support and keyboard navigation
|
|
15
|
+
- **ðŠķ Lightweight** - Zero dependencies, vanilla JavaScript
|
|
16
|
+
- **ðĻ Customizable** - Separated core/theme CSS for easy theming
|
|
17
|
+
- **ðą Responsive** - Mobile-first design approach
|
|
18
|
+
- **ð Dark Mode** - Built-in dark mode support
|
|
19
|
+
- **âŋ Inclusive** - High contrast mode and reduced motion support
|
|
20
|
+
- **ð§ Framework Agnostic** - Works with any framework or vanilla JS
|
|
21
|
+
- **ðĶ Tree-shakeable** - Import only what you need
|
|
22
|
+
|
|
23
|
+
## ð Table of Contents
|
|
24
|
+
|
|
25
|
+
- [Demo](#-demo)
|
|
26
|
+
- [Installation](#-installation)
|
|
27
|
+
- [Quick Start](#-quick-start)
|
|
28
|
+
- [Components](#-components)
|
|
29
|
+
- [Dropdown](#dropdown)
|
|
30
|
+
- [Tabs](#tabs)
|
|
31
|
+
- [Accordion](#accordion)
|
|
32
|
+
- [Collapse](#collapse)
|
|
33
|
+
- [Modal](#modal)
|
|
34
|
+
- [Offcanvas](#offcanvas)
|
|
35
|
+
- [Browser Support](#-browser-support)
|
|
36
|
+
- [Contributing](#-contributing)
|
|
37
|
+
- [License](#-license)
|
|
38
|
+
|
|
39
|
+
## ð Demo
|
|
40
|
+
|
|
41
|
+
View live examples at: [https://5ulo.github.io/accessible-kit/](https://5ulo.github.io/accessible-kit/)
|
|
42
|
+
|
|
43
|
+
## ðĶ Installation
|
|
44
|
+
|
|
45
|
+
### NPM
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install accessible-kit
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Yarn
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
yarn add accessible-kit
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### CDN
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<!-- All components -->
|
|
61
|
+
<script src="https://unpkg.com/accessible-kit/src/js/index.js"></script>
|
|
62
|
+
|
|
63
|
+
<!-- Individual component -->
|
|
64
|
+
<script src="https://unpkg.com/accessible-kit/src/js/a11y-dropdown.js"></script>
|
|
65
|
+
<link rel="stylesheet" href="https://unpkg.com/accessible-kit/src/css/a11y-dropdown.core.css">
|
|
66
|
+
<link rel="stylesheet" href="https://unpkg.com/accessible-kit/src/css/a11y-dropdown.theme.css">
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## ð Quick Start
|
|
70
|
+
|
|
71
|
+
### Using CDN
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!DOCTYPE html>
|
|
75
|
+
<html lang="en">
|
|
76
|
+
<head>
|
|
77
|
+
<link rel="stylesheet" href="https://unpkg.com/accessible-kit/src/css/a11y-dropdown.core.css">
|
|
78
|
+
<link rel="stylesheet" href="https://unpkg.com/accessible-kit/src/css/a11y-dropdown.theme.css">
|
|
79
|
+
</head>
|
|
80
|
+
<body>
|
|
81
|
+
<div data-dropdown>
|
|
82
|
+
<button data-dropdown-button>
|
|
83
|
+
Options
|
|
84
|
+
<span data-dropdown-arrow></span>
|
|
85
|
+
</button>
|
|
86
|
+
<div data-dropdown-menu>
|
|
87
|
+
<div>
|
|
88
|
+
<div>
|
|
89
|
+
<button data-dropdown-item>Edit</button>
|
|
90
|
+
<button data-dropdown-item>Delete</button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<script src="https://unpkg.com/accessible-kit/src/js/a11y-dropdown.js"></script>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Using NPM/Modules
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// Import all components
|
|
105
|
+
import 'accessible-kit';
|
|
106
|
+
|
|
107
|
+
// Or import specific components
|
|
108
|
+
import 'accessible-kit/dropdown';
|
|
109
|
+
import 'accessible-kit/tabs';
|
|
110
|
+
|
|
111
|
+
// Import styles
|
|
112
|
+
import 'accessible-kit/styles/dropdown-core';
|
|
113
|
+
import 'accessible-kit/styles/dropdown-theme';
|
|
114
|
+
|
|
115
|
+
// Initialize
|
|
116
|
+
a11yKit.initAll();
|
|
117
|
+
// or
|
|
118
|
+
a11yKit.initDropdowns();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Module Imports (ES6+)
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
import { AccessibleDropdown } from 'accessible-kit/dropdown';
|
|
125
|
+
|
|
126
|
+
const dropdown = new AccessibleDropdown(element, {
|
|
127
|
+
closeOnSelect: true,
|
|
128
|
+
onOpen: () => console.log('Opened'),
|
|
129
|
+
onClose: () => console.log('Closed')
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## ð§Đ Components
|
|
134
|
+
|
|
135
|
+
### Available Components (v1.0)
|
|
136
|
+
|
|
137
|
+
- â
**Dropdown** - Accessible dropdown menus with keyboard navigation
|
|
138
|
+
- â
**Tabs** - Tab panels following WAI-ARIA Tabs Pattern
|
|
139
|
+
- â
**Accordion** - Expandable sections with keyboard support
|
|
140
|
+
- â
**Collapse** - Standalone collapsible panels
|
|
141
|
+
- â
**Modal** - Accessible dialogs with focus trap
|
|
142
|
+
- â
**Offcanvas** - Slide-out side panels with focus management
|
|
143
|
+
|
|
144
|
+
### Roadmap (v1.1+)
|
|
145
|
+
|
|
146
|
+
- ð **Tooltip** - Accessible tooltips with positioning
|
|
147
|
+
- ð **Toast/Notification** - Live region announcements
|
|
148
|
+
- ð **Toggle Switch** - Accessible switch component
|
|
149
|
+
- ð **Popover** - Interactive popovers
|
|
150
|
+
- ð **Alert/Banner** - Dismissible alert messages
|
|
151
|
+
- ð **Combobox/Autocomplete** - Searchable select component
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Dropdown
|
|
156
|
+
|
|
157
|
+
Fully accessible dropdown component with keyboard navigation and ARIA support.
|
|
158
|
+
|
|
159
|
+
### Features
|
|
160
|
+
|
|
161
|
+
- â
Full ARIA attributes support
|
|
162
|
+
- â
Keyboard navigation (arrows, Enter, Space, Esc, Home, End)
|
|
163
|
+
- â
Focus management
|
|
164
|
+
- â
Auto-close on outside click
|
|
165
|
+
- â
CSS Grid animations by default
|
|
166
|
+
- â
Multiple variants (nav, language switcher)
|
|
167
|
+
- â
Automatic `prefers-reduced-motion` support
|
|
168
|
+
|
|
169
|
+
### Installation
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<!-- CSS -->
|
|
173
|
+
<link rel="stylesheet" href="css/a11y-dropdown.core.css">
|
|
174
|
+
<link rel="stylesheet" href="css/a11y-dropdown.theme.css">
|
|
175
|
+
|
|
176
|
+
<!-- JavaScript -->
|
|
177
|
+
<script src="js/a11y-dropdown.js"></script>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Basic Usage
|
|
181
|
+
|
|
182
|
+
```html
|
|
183
|
+
<div data-dropdown>
|
|
184
|
+
<button data-dropdown-button>
|
|
185
|
+
Options
|
|
186
|
+
<span data-dropdown-arrow></span>
|
|
187
|
+
</button>
|
|
188
|
+
<div data-dropdown-menu>
|
|
189
|
+
<!-- Two wrapper divs required for animations -->
|
|
190
|
+
<div>
|
|
191
|
+
<div>
|
|
192
|
+
<button data-dropdown-item>Edit</button>
|
|
193
|
+
<button data-dropdown-item>Duplicate</button>
|
|
194
|
+
<hr data-dropdown-divider>
|
|
195
|
+
<button data-dropdown-item>Delete</button>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### JavaScript Initialization
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// Auto-initialize all dropdowns
|
|
206
|
+
initDropdowns();
|
|
207
|
+
|
|
208
|
+
// Or manual initialization with options
|
|
209
|
+
const dropdown = new AccessibleDropdown(element, {
|
|
210
|
+
closeOnSelect: true,
|
|
211
|
+
closeOnOutsideClick: true,
|
|
212
|
+
closeOnEscape: true,
|
|
213
|
+
hoverDelay: 0,
|
|
214
|
+
onOpen: (dropdown) => {},
|
|
215
|
+
onClose: (dropdown) => {},
|
|
216
|
+
onSelect: (item, index) => {}
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Animations
|
|
221
|
+
|
|
222
|
+
Dropdowns have **CSS Grid animations enabled by default**. The animation automatically respects `prefers-reduced-motion` user preferences.
|
|
223
|
+
|
|
224
|
+
**Customizing animation speed:**
|
|
225
|
+
|
|
226
|
+
```html
|
|
227
|
+
<!-- Inline via style attribute -->
|
|
228
|
+
<div data-dropdown style="--a11y-dropdown-duration: 0.5s;">
|
|
229
|
+
<!-- dropdown content -->
|
|
230
|
+
</div>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```css
|
|
234
|
+
/* Or globally in CSS */
|
|
235
|
+
:root {
|
|
236
|
+
--a11y-dropdown-duration: 0.3s;
|
|
237
|
+
--a11y-dropdown-easing: ease-in-out;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Disable animations:**
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<div data-dropdown-menu data-no-animation>
|
|
245
|
+
<!-- menu without animations -->
|
|
246
|
+
</div>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Configuration
|
|
250
|
+
|
|
251
|
+
**Data attributes:**
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<!-- Keep dropdown open after selection -->
|
|
255
|
+
<div data-dropdown data-close-on-select="false">
|
|
256
|
+
|
|
257
|
+
<!-- Disable close on outside click -->
|
|
258
|
+
<div data-dropdown data-close-on-outside-click="false">
|
|
259
|
+
|
|
260
|
+
<!-- Hover delay (ms) -->
|
|
261
|
+
<div data-dropdown data-hover-delay="200">
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**JavaScript options:**
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
{
|
|
268
|
+
closeOnSelect: true, // Close after selecting item
|
|
269
|
+
closeOnOutsideClick: true, // Close on outside click
|
|
270
|
+
closeOnEscape: true, // Close on Escape key
|
|
271
|
+
hoverDelay: 0, // Hover delay in ms
|
|
272
|
+
onOpen: (dropdown) => {}, // Callback on open
|
|
273
|
+
onClose: (dropdown) => {}, // Callback on close
|
|
274
|
+
onSelect: (item, index) => {} // Callback on item select
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Keyboard Navigation
|
|
279
|
+
|
|
280
|
+
- **Enter** / **Space** - Open/close menu or select item
|
|
281
|
+
- **â** / **â** - Navigate between items
|
|
282
|
+
- **Home** / **End** - Jump to first/last item
|
|
283
|
+
- **Esc** - Close menu and return focus to button
|
|
284
|
+
- **Tab** - Close menu and move focus
|
|
285
|
+
|
|
286
|
+
### Variants
|
|
287
|
+
|
|
288
|
+
**Navigation menu:**
|
|
289
|
+
|
|
290
|
+
```html
|
|
291
|
+
<div data-dropdown data-variant="nav">
|
|
292
|
+
<button data-dropdown-button>Products</button>
|
|
293
|
+
<div data-dropdown-menu>
|
|
294
|
+
<div>
|
|
295
|
+
<div>
|
|
296
|
+
<a href="#" data-dropdown-item>Product 1</a>
|
|
297
|
+
<a href="#" data-dropdown-item>Product 2</a>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Language switcher:**
|
|
305
|
+
|
|
306
|
+
```html
|
|
307
|
+
<div data-dropdown data-variant="language">
|
|
308
|
+
<button data-dropdown-button>
|
|
309
|
+
ðŽð§ EN
|
|
310
|
+
<span data-dropdown-arrow></span>
|
|
311
|
+
</button>
|
|
312
|
+
<div data-dropdown-menu>
|
|
313
|
+
<div>
|
|
314
|
+
<div>
|
|
315
|
+
<button data-dropdown-item>
|
|
316
|
+
<span data-dropdown-item-flag>ðŽð§</span>
|
|
317
|
+
English
|
|
318
|
+
</button>
|
|
319
|
+
<button data-dropdown-item>
|
|
320
|
+
<span data-dropdown-item-flag>ðļð°</span>
|
|
321
|
+
SlovenÄina
|
|
322
|
+
</button>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Right-aligned menu:**
|
|
330
|
+
|
|
331
|
+
```html
|
|
332
|
+
<div data-dropdown-menu="right">
|
|
333
|
+
<!-- items -->
|
|
334
|
+
</div>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Tabs
|
|
340
|
+
|
|
341
|
+
Fully accessible tabs component following WAI-ARIA Tabs Pattern.
|
|
342
|
+
|
|
343
|
+
### Features
|
|
344
|
+
|
|
345
|
+
- â
Full ARIA attributes (role, aria-selected, aria-controls)
|
|
346
|
+
- â
Keyboard navigation (arrows, Home, End)
|
|
347
|
+
- â
Automatic and manual activation modes
|
|
348
|
+
- â
Focus management
|
|
349
|
+
- â
Disabled tabs support
|
|
350
|
+
- â
Horizontal and vertical orientation
|
|
351
|
+
- â
Multiple visual variants (basic, boxed, pills)
|
|
352
|
+
- â
Fade animations by default
|
|
353
|
+
- â
Automatic `prefers-reduced-motion` support
|
|
354
|
+
|
|
355
|
+
### Installation
|
|
356
|
+
|
|
357
|
+
```html
|
|
358
|
+
<!-- CSS -->
|
|
359
|
+
<link rel="stylesheet" href="css/a11y-tabs.core.css">
|
|
360
|
+
<link rel="stylesheet" href="css/a11y-tabs.theme.css">
|
|
361
|
+
|
|
362
|
+
<!-- JavaScript -->
|
|
363
|
+
<script src="js/a11y-tabs.js"></script>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Basic Usage
|
|
367
|
+
|
|
368
|
+
```html
|
|
369
|
+
<div data-tabs>
|
|
370
|
+
<div role="tablist" data-tabs-list>
|
|
371
|
+
<button data-tabs-tab>Tab 1</button>
|
|
372
|
+
<button data-tabs-tab>Tab 2</button>
|
|
373
|
+
<button data-tabs-tab>Tab 3</button>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div data-tabs-panel>
|
|
377
|
+
Content for tab 1
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<div data-tabs-panel>
|
|
381
|
+
Content for tab 2
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div data-tabs-panel>
|
|
385
|
+
Content for tab 3
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### JavaScript Initialization
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
// Auto-initialize all tabs
|
|
394
|
+
initTabs();
|
|
395
|
+
|
|
396
|
+
// Manual initialization
|
|
397
|
+
const tabs = new AccessibleTabs(element, {
|
|
398
|
+
activeIndex: 0, // Initial active tab (0-based)
|
|
399
|
+
automatic: true, // Auto-activate on arrow keys
|
|
400
|
+
orientation: 'horizontal', // 'horizontal' or 'vertical'
|
|
401
|
+
onChange: (tab, panel, index) => {},
|
|
402
|
+
onTabClick: (tab, index) => {}
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Animations
|
|
407
|
+
|
|
408
|
+
Tabs have **fade animation enabled by default** when switching between panels.
|
|
409
|
+
|
|
410
|
+
**Customizing animation speed:**
|
|
411
|
+
|
|
412
|
+
```html
|
|
413
|
+
<div data-tabs style="--a11y-tabs-duration: 0.5s;">
|
|
414
|
+
<!-- tabs -->
|
|
415
|
+
</div>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
```css
|
|
419
|
+
:root {
|
|
420
|
+
--a11y-tabs-duration: 0.3s;
|
|
421
|
+
--a11y-tabs-easing: ease-in-out;
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Disable animations:**
|
|
426
|
+
|
|
427
|
+
```html
|
|
428
|
+
<div data-tabs data-no-animation>
|
|
429
|
+
<!-- tabs without fade animation -->
|
|
430
|
+
</div>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Configuration
|
|
434
|
+
|
|
435
|
+
**Data attributes:**
|
|
436
|
+
|
|
437
|
+
```html
|
|
438
|
+
<!-- Set initial active tab (0-based index) -->
|
|
439
|
+
<div data-tabs data-active-index="1">
|
|
440
|
+
|
|
441
|
+
<!-- Manual activation (requires Enter/Space) -->
|
|
442
|
+
<div data-tabs data-automatic="false">
|
|
443
|
+
|
|
444
|
+
<!-- Vertical orientation -->
|
|
445
|
+
<div data-tabs data-orientation="vertical">
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**JavaScript options:**
|
|
449
|
+
|
|
450
|
+
```javascript
|
|
451
|
+
{
|
|
452
|
+
activeIndex: 0, // Initial active tab
|
|
453
|
+
automatic: true, // Auto-activate on arrow keys
|
|
454
|
+
orientation: 'horizontal', // 'horizontal' or 'vertical'
|
|
455
|
+
onChange: (tab, panel, index) => {}, // Callback on tab change
|
|
456
|
+
onTabClick: (tab, index) => {} // Callback on tab click
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Keyboard Navigation
|
|
461
|
+
|
|
462
|
+
**Horizontal tabs:**
|
|
463
|
+
- **â** / **â** - Navigate between tabs
|
|
464
|
+
- **Home** - Jump to first tab
|
|
465
|
+
- **End** - Jump to last tab
|
|
466
|
+
- **Enter** / **Space** - Activate tab (manual mode)
|
|
467
|
+
- **Tab** - Move focus to panel content
|
|
468
|
+
|
|
469
|
+
**Vertical tabs:**
|
|
470
|
+
- **â** / **â** - Navigate between tabs
|
|
471
|
+
- **Home** / **End** - First/last tab
|
|
472
|
+
- **Enter** / **Space** - Activate tab
|
|
473
|
+
- **Tab** - Move to panel content
|
|
474
|
+
|
|
475
|
+
### Variants
|
|
476
|
+
|
|
477
|
+
**Boxed variant:**
|
|
478
|
+
|
|
479
|
+
```html
|
|
480
|
+
<div data-tabs data-variant="boxed">
|
|
481
|
+
<div role="tablist" data-tabs-list>
|
|
482
|
+
<button data-tabs-tab>Tab 1</button>
|
|
483
|
+
<button data-tabs-tab>Tab 2</button>
|
|
484
|
+
</div>
|
|
485
|
+
|
|
486
|
+
<div data-tabs-panels>
|
|
487
|
+
<div data-tabs-panel>Content 1</div>
|
|
488
|
+
<div data-tabs-panel>Content 2</div>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Pills variant:**
|
|
494
|
+
|
|
495
|
+
```html
|
|
496
|
+
<div data-tabs data-variant="pills">
|
|
497
|
+
<!-- tabs styled as pills/buttons -->
|
|
498
|
+
</div>
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Vertical tabs:**
|
|
502
|
+
|
|
503
|
+
```html
|
|
504
|
+
<div data-tabs data-orientation="vertical">
|
|
505
|
+
<div role="tablist" data-tabs-list>
|
|
506
|
+
<button data-tabs-tab>Tab 1</button>
|
|
507
|
+
<button data-tabs-tab>Tab 2</button>
|
|
508
|
+
</div>
|
|
509
|
+
|
|
510
|
+
<div data-tabs-panels>
|
|
511
|
+
<div data-tabs-panel>Content 1</div>
|
|
512
|
+
<div data-tabs-panel>Content 2</div>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Disabled tab:**
|
|
518
|
+
|
|
519
|
+
```html
|
|
520
|
+
<button data-tabs-tab disabled>Disabled Tab</button>
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Tabs with badges:**
|
|
524
|
+
|
|
525
|
+
```html
|
|
526
|
+
<button data-tabs-tab>
|
|
527
|
+
Active
|
|
528
|
+
<span class="tabs__tab-badge">12</span>
|
|
529
|
+
</button>
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Accordion
|
|
535
|
+
|
|
536
|
+
Fully accessible accordion component with keyboard navigation.
|
|
537
|
+
|
|
538
|
+
### Features
|
|
539
|
+
|
|
540
|
+
- â
Full ARIA attributes support
|
|
541
|
+
- â
Keyboard navigation (arrows, Home, End, Enter, Space)
|
|
542
|
+
- â
Single or multiple expand modes
|
|
543
|
+
- â
CSS Grid animations by default
|
|
544
|
+
- â
Optional "Toggle All" button
|
|
545
|
+
- â
Automatic `prefers-reduced-motion` support
|
|
546
|
+
|
|
547
|
+
### Installation
|
|
548
|
+
|
|
549
|
+
```html
|
|
550
|
+
<!-- CSS -->
|
|
551
|
+
<link rel="stylesheet" href="css/a11y-accordion.core.css">
|
|
552
|
+
<link rel="stylesheet" href="css/a11y-accordion.theme.css">
|
|
553
|
+
|
|
554
|
+
<!-- JavaScript -->
|
|
555
|
+
<script src="js/a11y-accordion.js"></script>
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### Basic Usage
|
|
559
|
+
|
|
560
|
+
```html
|
|
561
|
+
<div data-accordion>
|
|
562
|
+
<div data-accordion-item>
|
|
563
|
+
<button data-accordion-trigger>
|
|
564
|
+
Section 1
|
|
565
|
+
</button>
|
|
566
|
+
<div data-accordion-panel>
|
|
567
|
+
<!-- Two wrapper divs required for animations -->
|
|
568
|
+
<div>
|
|
569
|
+
<div>
|
|
570
|
+
<p>Content for section 1</p>
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
|
|
576
|
+
<div data-accordion-item>
|
|
577
|
+
<button data-accordion-trigger>
|
|
578
|
+
Section 2
|
|
579
|
+
</button>
|
|
580
|
+
<div data-accordion-panel>
|
|
581
|
+
<div>
|
|
582
|
+
<div>
|
|
583
|
+
<p>Content for section 2</p>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### JavaScript Initialization
|
|
592
|
+
|
|
593
|
+
```javascript
|
|
594
|
+
// Auto-initialize all accordions
|
|
595
|
+
initAccordions();
|
|
596
|
+
|
|
597
|
+
// Manual initialization
|
|
598
|
+
const accordion = new AccessibleAccordion(element, {
|
|
599
|
+
allowMultiple: false,
|
|
600
|
+
allowToggle: true,
|
|
601
|
+
defaultExpanded: [],
|
|
602
|
+
onToggle: (item, isExpanded) => {},
|
|
603
|
+
onChange: (expandedItems) => {}
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Animations
|
|
608
|
+
|
|
609
|
+
Accordions have **CSS Grid animations enabled by default**.
|
|
610
|
+
|
|
611
|
+
**Customizing animation:**
|
|
612
|
+
|
|
613
|
+
```html
|
|
614
|
+
<div data-accordion style="--a11y-accordion-duration: 0.5s;">
|
|
615
|
+
<!-- accordion items -->
|
|
616
|
+
</div>
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
```css
|
|
620
|
+
:root {
|
|
621
|
+
--a11y-accordion-duration: 0.3s;
|
|
622
|
+
--a11y-accordion-easing: ease;
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
**Disable animations:**
|
|
627
|
+
|
|
628
|
+
```html
|
|
629
|
+
<div data-accordion data-no-animation>
|
|
630
|
+
<!-- accordion without animations -->
|
|
631
|
+
</div>
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Configuration
|
|
635
|
+
|
|
636
|
+
**Allow multiple sections open:**
|
|
637
|
+
|
|
638
|
+
```html
|
|
639
|
+
<div data-accordion data-accordion-multiple>
|
|
640
|
+
<!-- multiple items can be open -->
|
|
641
|
+
</div>
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**Default expanded state:**
|
|
645
|
+
|
|
646
|
+
```html
|
|
647
|
+
<div data-accordion-item data-default-expanded>
|
|
648
|
+
<!-- this item is expanded by default -->
|
|
649
|
+
</div>
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Toggle All Button:**
|
|
653
|
+
|
|
654
|
+
```html
|
|
655
|
+
<div data-accordion data-accordion-multiple>
|
|
656
|
+
<!-- Auto-generated toggle button -->
|
|
657
|
+
<div data-accordion-controls="top"></div>
|
|
658
|
+
|
|
659
|
+
<div data-accordion-item>
|
|
660
|
+
<!-- items -->
|
|
661
|
+
</div>
|
|
662
|
+
|
|
663
|
+
<!-- Bottom toggle button -->
|
|
664
|
+
<div data-accordion-controls="bottom"></div>
|
|
665
|
+
</div>
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### Keyboard Navigation
|
|
669
|
+
|
|
670
|
+
- **â** - Move to next trigger
|
|
671
|
+
- **â** - Move to previous trigger
|
|
672
|
+
- **Home** - Move to first trigger
|
|
673
|
+
- **End** - Move to last trigger
|
|
674
|
+
- **Enter** / **Space** - Toggle accordion item
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## Collapse
|
|
679
|
+
|
|
680
|
+
Standalone collapsible panels - accordion functionality without the group structure.
|
|
681
|
+
|
|
682
|
+
### Features
|
|
683
|
+
|
|
684
|
+
- â
Standalone toggle buttons
|
|
685
|
+
- â
CSS Grid animations by default
|
|
686
|
+
- â
Control multiple panels with one button
|
|
687
|
+
- â
Dynamic button text based on state
|
|
688
|
+
- â
ARIA attributes for accessibility
|
|
689
|
+
|
|
690
|
+
### Basic Usage
|
|
691
|
+
|
|
692
|
+
```html
|
|
693
|
+
<!-- Simple toggle -->
|
|
694
|
+
<button data-collapse-toggle="#panel">
|
|
695
|
+
Toggle Panel
|
|
696
|
+
</button>
|
|
697
|
+
|
|
698
|
+
<div id="panel">
|
|
699
|
+
<!-- Two wrapper divs required for animations -->
|
|
700
|
+
<div>
|
|
701
|
+
<div>
|
|
702
|
+
<p>Panel content here</p>
|
|
703
|
+
</div>
|
|
704
|
+
</div>
|
|
705
|
+
</div>
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
### Control Multiple Panels
|
|
709
|
+
|
|
710
|
+
```html
|
|
711
|
+
<!-- Using class selector -->
|
|
712
|
+
<button data-collapse-toggle=".info-panel">
|
|
713
|
+
Toggle All Info Panels
|
|
714
|
+
</button>
|
|
715
|
+
|
|
716
|
+
<div class="info-panel">
|
|
717
|
+
<div><div>Panel 1</div></div>
|
|
718
|
+
</div>
|
|
719
|
+
<div class="info-panel">
|
|
720
|
+
<div><div>Panel 2</div></div>
|
|
721
|
+
</div>
|
|
722
|
+
<div class="info-panel">
|
|
723
|
+
<div><div>Panel 3</div></div>
|
|
724
|
+
</div>
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Dynamic Button Text
|
|
728
|
+
|
|
729
|
+
```html
|
|
730
|
+
<button
|
|
731
|
+
data-collapse-toggle="#details"
|
|
732
|
+
data-text-for-show="Show Details"
|
|
733
|
+
data-text-for-hide="Hide Details"
|
|
734
|
+
data-aria-label-for-show="Show additional details"
|
|
735
|
+
data-aria-label-for-hide="Hide additional details">
|
|
736
|
+
<span>Show Details</span>
|
|
737
|
+
</button>
|
|
738
|
+
|
|
739
|
+
<div id="details">
|
|
740
|
+
<div><div>Details content</div></div>
|
|
741
|
+
</div>
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
### Animations
|
|
745
|
+
|
|
746
|
+
**Disable animations for specific panel:**
|
|
747
|
+
|
|
748
|
+
```html
|
|
749
|
+
<div id="no-animation-panel" data-no-animation>
|
|
750
|
+
<div>
|
|
751
|
+
<div>Panel without animation</div>
|
|
752
|
+
</div>
|
|
753
|
+
</div>
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
---
|
|
757
|
+
|
|
758
|
+
## Modal
|
|
759
|
+
|
|
760
|
+
Accessible modal dialogs with focus trap following WAI-ARIA Dialog Pattern.
|
|
761
|
+
|
|
762
|
+
### Features
|
|
763
|
+
|
|
764
|
+
- â
Full ARIA attributes (role="dialog", aria-modal="true")
|
|
765
|
+
- â
Focus trap - Tab cycles only within modal
|
|
766
|
+
- â
Return focus - Returns focus to trigger after close
|
|
767
|
+
- â
Scroll lock - Locks body scroll when open
|
|
768
|
+
- â
Escape to close
|
|
769
|
+
- â
Backdrop click to close
|
|
770
|
+
- â
Configurable behavior
|
|
771
|
+
|
|
772
|
+
### Installation
|
|
773
|
+
|
|
774
|
+
```html
|
|
775
|
+
<!-- CSS -->
|
|
776
|
+
<link rel="stylesheet" href="css/a11y-modal.core.css">
|
|
777
|
+
<link rel="stylesheet" href="css/a11y-modal.theme.css">
|
|
778
|
+
|
|
779
|
+
<!-- JavaScript -->
|
|
780
|
+
<script src="js/a11y-modal.js"></script>
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
### Basic Usage
|
|
784
|
+
|
|
785
|
+
```html
|
|
786
|
+
<!-- Trigger button -->
|
|
787
|
+
<button data-modal-trigger="my-modal">Open Modal</button>
|
|
788
|
+
|
|
789
|
+
<!-- Modal -->
|
|
790
|
+
<div data-modal id="my-modal">
|
|
791
|
+
<div data-modal-backdrop></div>
|
|
792
|
+
<div data-modal-dialog>
|
|
793
|
+
<div data-modal-header>
|
|
794
|
+
<h2 data-modal-title>Modal Title</h2>
|
|
795
|
+
<button data-modal-close aria-label="Close modal">Ã</button>
|
|
796
|
+
</div>
|
|
797
|
+
<div data-modal-body>
|
|
798
|
+
<p>Modal content goes here.</p>
|
|
799
|
+
</div>
|
|
800
|
+
<div data-modal-footer>
|
|
801
|
+
<button data-modal-close>Cancel</button>
|
|
802
|
+
<button>Confirm</button>
|
|
803
|
+
</div>
|
|
804
|
+
</div>
|
|
805
|
+
</div>
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### JavaScript Initialization
|
|
809
|
+
|
|
810
|
+
```javascript
|
|
811
|
+
// Auto-initialize all modals
|
|
812
|
+
initModals();
|
|
813
|
+
|
|
814
|
+
// Manual initialization
|
|
815
|
+
const modal = new AccessibleModal(element, {
|
|
816
|
+
closeOnBackdropClick: true,
|
|
817
|
+
closeOnEscape: true,
|
|
818
|
+
lockScroll: true,
|
|
819
|
+
onOpen: (modal) => {},
|
|
820
|
+
onClose: (modal) => {}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
// Programmatic control
|
|
824
|
+
modal.open();
|
|
825
|
+
modal.close();
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### Configuration
|
|
829
|
+
|
|
830
|
+
```javascript
|
|
831
|
+
{
|
|
832
|
+
closeOnBackdropClick: true, // Close on backdrop click
|
|
833
|
+
closeOnEscape: true, // Close on Esc key
|
|
834
|
+
lockScroll: true, // Lock body scroll
|
|
835
|
+
onOpen: (modal) => {}, // Callback on open
|
|
836
|
+
onClose: (modal) => {} // Callback on close
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### Keyboard Navigation
|
|
841
|
+
|
|
842
|
+
- **Tab** - Cycle through focusable elements (trapped within modal)
|
|
843
|
+
- **Shift + Tab** - Reverse cycle
|
|
844
|
+
- **Esc** - Close modal
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
## Offcanvas
|
|
849
|
+
|
|
850
|
+
Slide-out side panels with focus trap and full accessibility support.
|
|
851
|
+
|
|
852
|
+
### Features
|
|
853
|
+
|
|
854
|
+
- â
Full ARIA attributes (role="dialog", aria-modal="true")
|
|
855
|
+
- â
Focus trap - Tab cycles only within offcanvas
|
|
856
|
+
- â
Return focus - Returns focus to trigger after close
|
|
857
|
+
- â
Scroll lock - Locks body scroll when open
|
|
858
|
+
- â
Escape to close
|
|
859
|
+
- â
Backdrop click to close
|
|
860
|
+
- â
4 positions (left, right, top, bottom)
|
|
861
|
+
|
|
862
|
+
### Installation
|
|
863
|
+
|
|
864
|
+
```html
|
|
865
|
+
<!-- CSS -->
|
|
866
|
+
<link rel="stylesheet" href="css/a11y-offcanvas.core.css">
|
|
867
|
+
<link rel="stylesheet" href="css/a11y-offcanvas.theme.css">
|
|
868
|
+
|
|
869
|
+
<!-- JavaScript -->
|
|
870
|
+
<script src="js/a11y-offcanvas.js"></script>
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Basic Usage
|
|
874
|
+
|
|
875
|
+
```html
|
|
876
|
+
<!-- Trigger button -->
|
|
877
|
+
<button data-offcanvas-trigger="menu">Open Menu</button>
|
|
878
|
+
|
|
879
|
+
<!-- Offcanvas -->
|
|
880
|
+
<div data-offcanvas id="menu">
|
|
881
|
+
<div data-position="left" data-offcanvas-panel>
|
|
882
|
+
<div data-offcanvas-header>
|
|
883
|
+
<h2 data-offcanvas-title>Menu</h2>
|
|
884
|
+
<button data-offcanvas-close aria-label="Close menu">Ã</button>
|
|
885
|
+
</div>
|
|
886
|
+
<div data-offcanvas-body>
|
|
887
|
+
<nav>
|
|
888
|
+
<a href="#">Home</a>
|
|
889
|
+
<a href="#">About</a>
|
|
890
|
+
<a href="#">Contact</a>
|
|
891
|
+
</nav>
|
|
892
|
+
</div>
|
|
893
|
+
</div>
|
|
894
|
+
</div>
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### Positions
|
|
898
|
+
|
|
899
|
+
```html
|
|
900
|
+
<!-- Left (default) -->
|
|
901
|
+
<div data-offcanvas-panel data-position="left">
|
|
902
|
+
|
|
903
|
+
<!-- Right -->
|
|
904
|
+
<div data-offcanvas-panel data-position="right">
|
|
905
|
+
|
|
906
|
+
<!-- Top -->
|
|
907
|
+
<div data-offcanvas-panel data-position="top">
|
|
908
|
+
|
|
909
|
+
<!-- Bottom -->
|
|
910
|
+
<div data-offcanvas-panel data-position="bottom">
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
### Animations
|
|
914
|
+
|
|
915
|
+
**Customizing animation:**
|
|
916
|
+
|
|
917
|
+
```html
|
|
918
|
+
<div data-offcanvas style="--a11y-offcanvas-duration: 0.5s;">
|
|
919
|
+
<!-- offcanvas content -->
|
|
920
|
+
</div>
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
```css
|
|
924
|
+
:root {
|
|
925
|
+
--a11y-offcanvas-duration: 0.3s;
|
|
926
|
+
--a11y-offcanvas-easing: ease-in-out;
|
|
927
|
+
}
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
**Disable animations:**
|
|
931
|
+
|
|
932
|
+
```html
|
|
933
|
+
<div data-offcanvas data-no-animation>
|
|
934
|
+
<!-- offcanvas without animations -->
|
|
935
|
+
</div>
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
---
|
|
939
|
+
|
|
940
|
+
## ð Browser Support
|
|
941
|
+
|
|
942
|
+
Tested and working in:
|
|
943
|
+
|
|
944
|
+
- **Chrome/Edge** 90+
|
|
945
|
+
- **Firefox** 88+
|
|
946
|
+
- **Safari** 14+
|
|
947
|
+
- **iOS Safari** 14+
|
|
948
|
+
- **Android Chrome** 90+
|
|
949
|
+
|
|
950
|
+
## ðïļ CSS Architecture
|
|
951
|
+
|
|
952
|
+
All components use a **separated core/theme architecture**:
|
|
953
|
+
|
|
954
|
+
### Core CSS (`*.core.css`)
|
|
955
|
+
|
|
956
|
+
Contains only logic, positioning, layout, and behavior:
|
|
957
|
+
- Positioning (relative, absolute, z-index)
|
|
958
|
+
- Layout structure (display, flex, grid)
|
|
959
|
+
- Visibility states
|
|
960
|
+
- Animations
|
|
961
|
+
- Responsive breakpoints
|
|
962
|
+
|
|
963
|
+
**Do not modify** unless changing component functionality.
|
|
964
|
+
|
|
965
|
+
### Theme CSS (`*.theme.css`)
|
|
966
|
+
|
|
967
|
+
Contains all visual styling:
|
|
968
|
+
- Colors (background, border, text)
|
|
969
|
+
- Spacing (padding, margin)
|
|
970
|
+
- Typography
|
|
971
|
+
- Borders and border-radius
|
|
972
|
+
- Shadows
|
|
973
|
+
- Dark mode
|
|
974
|
+
- High contrast mode
|
|
975
|
+
|
|
976
|
+
**Customize freely** to match your design system.
|
|
977
|
+
|
|
978
|
+
## ðĻ Creating Custom Themes
|
|
979
|
+
|
|
980
|
+
1. Copy the theme CSS file:
|
|
981
|
+
```bash
|
|
982
|
+
cp a11y-dropdown.theme.css my-custom-theme.css
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
2. Modify CSS variables and styles
|
|
986
|
+
|
|
987
|
+
3. Update your HTML:
|
|
988
|
+
```html
|
|
989
|
+
<link rel="stylesheet" href="css/a11y-dropdown.core.css">
|
|
990
|
+
<link rel="stylesheet" href="css/my-custom-theme.css">
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
## ðĪ Contributing
|
|
994
|
+
|
|
995
|
+
Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) before submitting PRs.
|
|
996
|
+
|
|
997
|
+
### Development Setup
|
|
998
|
+
|
|
999
|
+
```bash
|
|
1000
|
+
# Clone repository
|
|
1001
|
+
git clone https://github.com/5ulo/accessible-kit.git
|
|
1002
|
+
cd accessible-kit
|
|
1003
|
+
|
|
1004
|
+
# Install dependencies
|
|
1005
|
+
npm install
|
|
1006
|
+
|
|
1007
|
+
# Start development server
|
|
1008
|
+
npm run dev
|
|
1009
|
+
|
|
1010
|
+
# Build for production
|
|
1011
|
+
npm run build
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
### Running Tests
|
|
1015
|
+
|
|
1016
|
+
```bash
|
|
1017
|
+
npm test
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
## ð License
|
|
1021
|
+
|
|
1022
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
1023
|
+
|
|
1024
|
+
## ð Acknowledgments
|
|
1025
|
+
|
|
1026
|
+
Built with a focus on accessibility and usability, following:
|
|
1027
|
+
- [WAI-ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/)
|
|
1028
|
+
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
|
|
1029
|
+
- Modern web standards and best practices
|
|
1030
|
+
|
|
1031
|
+
## ðŽ Support
|
|
1032
|
+
|
|
1033
|
+
- ð [Documentation](https://5ulo.github.io/accessible-kit/)
|
|
1034
|
+
- ð [Issue Tracker](https://github.com/5ulo/accessible-kit/issues)
|
|
1035
|
+
|
|
1036
|
+
---
|
|
1037
|
+
|
|
1038
|
+
Made with âŋ and âĪïļ for a more accessible web.
|