@yuktishaalaa/yuktai 2.0.12 → 2.0.14
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 +204 -37
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -1,31 +1,70 @@
|
|
|
1
1
|
# @yuktishaalaa/yuktai
|
|
2
2
|
|
|
3
|
-
> Universal runtime accessibility plugin — automatically fixes WCAG violations on every page, for every user, with zero configuration.
|
|
3
|
+
> Universal runtime accessibility plugin — automatically fixes WCAG 2.2 violations on every page, for every user, with zero configuration. No JAWS needed.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@yuktishaalaa/yuktai)
|
|
6
|
-
[](https://www.npmjs.com/package/@yuktishaalaa/yuktai)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://nodejs.org)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What's new in v2.0.0
|
|
13
|
+
|
|
14
|
+
- ✅ No JAWS needed — works with NVDA, VoiceOver, TalkBack, Narrator
|
|
15
|
+
- ✅ Built-in SpeechSynthesis — browser speaks on focus, zero install
|
|
16
|
+
- ✅ Visual alerts — sliding banner for deaf users, no audio needed
|
|
17
|
+
- ✅ Keyboard navigation — arrow keys, Escape, focus trap, no AT needed
|
|
18
|
+
- ✅ Skip links — multi-target (main, nav, search), always visible on mobile
|
|
19
|
+
- ✅ Preference panel — blind / deaf / motor / colour blind presets
|
|
20
|
+
- ✅ Persistent preferences — saved to localStorage across pages
|
|
21
|
+
- ✅ WCAG 2.2 — focus appearance, target size 44×44px, timeout warning
|
|
22
|
+
- ✅ Audit score — 0–100 with issue breakdown (localhost dev only)
|
|
23
|
+
- ✅ Dyslexia font — Atkinson Hyperlegible (research-backed)
|
|
24
|
+
- ✅ React 17, 18, 19 supported
|
|
25
|
+
- ✅ TypeScript declarations — full `index.d.ts` generated
|
|
7
26
|
|
|
8
27
|
---
|
|
9
28
|
|
|
10
29
|
## Install
|
|
11
30
|
|
|
31
|
+
### React 17 or 18
|
|
12
32
|
```bash
|
|
13
33
|
npm i @yuktishaalaa/yuktai
|
|
14
34
|
```
|
|
15
35
|
|
|
36
|
+
### React 19 or MUI v5 conflict
|
|
37
|
+
```bash
|
|
38
|
+
npm i @yuktishaalaa/yuktai --legacy-peer-deps
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### MUI v5 + React 19 permanent fix
|
|
42
|
+
```bash
|
|
43
|
+
npm i @mui/material@6 @mui/icons-material@6 --legacy-peer-deps
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Requirements
|
|
47
|
+
- Node.js 18 or higher
|
|
48
|
+
- npm 8 or higher
|
|
49
|
+
|
|
16
50
|
---
|
|
17
51
|
|
|
18
52
|
## What it does
|
|
19
53
|
|
|
20
54
|
yuktai scans every HTML element on the page and automatically injects missing accessibility attributes — `aria-label`, `alt`, `role`, `tabindex`, `scope`, `autocomplete`, `aria-required`, `aria-expanded`, and more. It also:
|
|
21
55
|
|
|
22
|
-
- Injects
|
|
56
|
+
- Injects multi-target skip links — main content, navigation, search
|
|
23
57
|
- Ensures `<html lang>` and `<title>` are present
|
|
24
58
|
- Fixes `<meta viewport>` if it blocks user zoom
|
|
25
|
-
- Watches for new DOM elements via `MutationObserver`
|
|
26
|
-
- Applies user-controlled visual preferences
|
|
59
|
+
- Watches for new DOM elements via `MutationObserver`
|
|
60
|
+
- Applies user-controlled visual preferences — high contrast, dark mode, reduce motion, large targets, font scale, dyslexia font, colour blind simulation
|
|
61
|
+
- Speaks focused elements aloud via browser SpeechSynthesis — zero install
|
|
62
|
+
- Shows visual alert banners for deaf users — no audio dependency
|
|
63
|
+
- Enforces keyboard navigation — arrow keys, Escape, focus trap in modals
|
|
64
|
+
- Saves user preferences to localStorage across sessions
|
|
65
|
+
- Shows accessibility audit score 0–100 in development
|
|
27
66
|
|
|
28
|
-
The floating button appears bottom-
|
|
67
|
+
The floating ♿ button appears bottom-right. Click it, choose preferences, click **Apply settings**.
|
|
29
68
|
|
|
30
69
|
---
|
|
31
70
|
|
|
@@ -53,9 +92,9 @@ export default function App() {
|
|
|
53
92
|
|
|
54
93
|
---
|
|
55
94
|
|
|
56
|
-
## Next.js
|
|
95
|
+
## Next.js — App Router
|
|
57
96
|
|
|
58
|
-
**Step 1 — next.config.js**
|
|
97
|
+
**Step 1 — next.config.js**
|
|
59
98
|
|
|
60
99
|
```js
|
|
61
100
|
const nextConfig = {
|
|
@@ -64,25 +103,48 @@ const nextConfig = {
|
|
|
64
103
|
module.exports = nextConfig;
|
|
65
104
|
```
|
|
66
105
|
|
|
67
|
-
**Step 2 —
|
|
106
|
+
**Step 2 — Create a client wrapper**
|
|
68
107
|
|
|
69
108
|
```tsx
|
|
109
|
+
// components/YuktaiClient.tsx
|
|
110
|
+
"use client";
|
|
111
|
+
import { useState, useEffect, type ReactNode } from "react";
|
|
70
112
|
import { YuktAIWrapper } from "@yuktishaalaa/yuktai";
|
|
71
113
|
|
|
114
|
+
export default function YuktaiClient({ children }: { children: ReactNode }) {
|
|
115
|
+
const [mounted, setMounted] = useState(false);
|
|
116
|
+
useEffect(() => setMounted(true), []);
|
|
117
|
+
|
|
118
|
+
// Prevents React hydration mismatch error #418
|
|
119
|
+
if (!mounted) return <>{children}</>;
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<YuktAIWrapper position="left">
|
|
123
|
+
{children}
|
|
124
|
+
</YuktAIWrapper>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Step 3 — app/layout.tsx**
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import YuktaiClient from "@/components/YuktaiClient";
|
|
133
|
+
|
|
72
134
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
73
135
|
return (
|
|
74
136
|
<html lang="en">
|
|
75
137
|
<body>
|
|
76
|
-
<
|
|
138
|
+
<YuktaiClient>
|
|
77
139
|
<main>{children}</main>
|
|
78
|
-
</
|
|
140
|
+
</YuktaiClient>
|
|
79
141
|
</body>
|
|
80
142
|
</html>
|
|
81
143
|
);
|
|
82
144
|
}
|
|
83
145
|
```
|
|
84
146
|
|
|
85
|
-
**Pages Router
|
|
147
|
+
**Pages Router — pages/_app.tsx**
|
|
86
148
|
|
|
87
149
|
```tsx
|
|
88
150
|
import { YuktAIWrapper } from "@yuktishaalaa/yuktai";
|
|
@@ -210,16 +272,16 @@ import "@yuktishaalaa/yuktai/vanilla";
|
|
|
210
272
|
**Programmatic API**
|
|
211
273
|
|
|
212
274
|
```js
|
|
213
|
-
window.yuktai.apply();
|
|
214
|
-
window.yuktai.reset();
|
|
215
|
-
window.yuktai.wcagPlugin.scan();
|
|
275
|
+
window.yuktai.apply();
|
|
276
|
+
window.yuktai.reset();
|
|
277
|
+
window.yuktai.wcagPlugin.scan();
|
|
216
278
|
```
|
|
217
279
|
|
|
218
280
|
---
|
|
219
281
|
|
|
220
282
|
## Web Component
|
|
221
283
|
|
|
222
|
-
Works in every framework and plain HTML — uses native Custom Elements
|
|
284
|
+
Works in every framework and plain HTML — uses native Custom Elements.
|
|
223
285
|
|
|
224
286
|
**Plain HTML**
|
|
225
287
|
|
|
@@ -272,36 +334,82 @@ import "@yuktishaalaa/yuktai/web-component";
|
|
|
272
334
|
## Direct engine API
|
|
273
335
|
|
|
274
336
|
```ts
|
|
275
|
-
import { wcagPlugin } from "@yuktishaalaa/yuktai
|
|
337
|
+
import { wcagPlugin } from "@yuktishaalaa/yuktai";
|
|
276
338
|
|
|
277
339
|
// Apply all WCAG rules
|
|
278
|
-
const report = wcagPlugin.applyFixes({
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
340
|
+
const report = wcagPlugin.applyFixes({
|
|
341
|
+
enabled: true,
|
|
342
|
+
highContrast: false,
|
|
343
|
+
darkMode: false,
|
|
344
|
+
reduceMotion: false,
|
|
345
|
+
largeTargets: false,
|
|
346
|
+
speechEnabled: false,
|
|
347
|
+
colorBlindMode:"none",
|
|
348
|
+
autoFix: true,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
console.log(report.fixed); // number of fixes applied
|
|
352
|
+
console.log(report.scanned); // number of nodes scanned
|
|
353
|
+
console.log(report.renderTime); // time in ms
|
|
354
|
+
console.log(report.score); // accessibility score 0–100
|
|
355
|
+
console.log(report.details); // array of A11yFix objects
|
|
282
356
|
|
|
283
357
|
// Scan only — no DOM changes
|
|
284
358
|
const violations = wcagPlugin.scan();
|
|
285
359
|
|
|
286
|
-
//
|
|
360
|
+
// Announce to screen readers + visual alert + speech
|
|
361
|
+
wcagPlugin.announce("Form submitted successfully", "success");
|
|
362
|
+
wcagPlugin.announce("Email is required", "error");
|
|
363
|
+
|
|
364
|
+
// Browser speech — zero install
|
|
365
|
+
wcagPlugin.speak("Welcome to the dashboard");
|
|
366
|
+
|
|
367
|
+
// Visual alert for deaf users
|
|
368
|
+
wcagPlugin.showVisualAlert("Settings saved", "success");
|
|
369
|
+
|
|
370
|
+
// Focus trap for modals
|
|
371
|
+
const modal = document.querySelector("#my-modal") as HTMLElement;
|
|
372
|
+
wcagPlugin.trapFocus(modal);
|
|
373
|
+
|
|
374
|
+
// Auto-fix new DOM elements
|
|
287
375
|
wcagPlugin.startObserver({ enabled: true, autoFix: true });
|
|
288
376
|
wcagPlugin.stopObserver();
|
|
377
|
+
```
|
|
289
378
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## A11yConfig options
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
import { wcagPlugin, A11yConfig } from "@yuktishaalaa/yuktai";
|
|
385
|
+
|
|
386
|
+
const config: A11yConfig = {
|
|
387
|
+
enabled: true, // required
|
|
388
|
+
highContrast: false, // increase contrast
|
|
389
|
+
darkMode: false, // invert colours
|
|
390
|
+
reduceMotion: false, // disable animations
|
|
391
|
+
largeTargets: false, // 44×44px minimum touch targets
|
|
392
|
+
speechEnabled: false, // speak focused elements aloud
|
|
393
|
+
colorBlindMode: "none", // none | deuteranopia | protanopia | tritanopia | achromatopsia
|
|
394
|
+
autoFix: true, // MutationObserver watches for new elements
|
|
395
|
+
showPreferencePanel: true, // floating ♿ button + panel
|
|
396
|
+
showSkipLinks: true, // multi-target skip links
|
|
397
|
+
showAuditBadge: false, // score badge — dev only (localhost)
|
|
398
|
+
fontSizeMultiplier: 1, // font scale multiplier
|
|
399
|
+
timeoutWarning: 0, // seconds before session timeout warning (0 = off)
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
await wcagPlugin.execute(config);
|
|
293
403
|
```
|
|
294
404
|
|
|
295
405
|
---
|
|
296
406
|
|
|
297
407
|
## HTML elements covered
|
|
298
408
|
|
|
299
|
-
The engine covers every HTML element:
|
|
300
|
-
|
|
301
409
|
- **Headings** — `h1` through `h6`
|
|
302
|
-
- **Text** — `p`, `span`, `abbr`, `mark`, `time`, `meter`, `progress`,
|
|
410
|
+
- **Text** — `p`, `span`, `abbr`, `mark`, `time`, `meter`, `progress`, all inline elements
|
|
303
411
|
- **Images & media** — `img`, `area`, `svg`, `canvas`, `video`, `audio`, `iframe`, `object`, `embed`, `figure`
|
|
304
|
-
- **Interactive** — `button`, `a`, clickable `div
|
|
412
|
+
- **Interactive** — `button`, `a`, clickable `div` / `span`
|
|
305
413
|
- **Forms** — `input` (all types), `select`, `textarea`, `fieldset`, `label`
|
|
306
414
|
- **Tables** — `table`, `th`, `td`, `caption`
|
|
307
415
|
- **Lists** — `ul`, `ol`, `dl`, `li`, `dt`, `dd`
|
|
@@ -311,28 +419,87 @@ The engine covers every HTML element:
|
|
|
311
419
|
|
|
312
420
|
---
|
|
313
421
|
|
|
422
|
+
## WCAG coverage
|
|
423
|
+
|
|
424
|
+
| Standard | Criteria covered |
|
|
425
|
+
|---|---|
|
|
426
|
+
| WCAG 2.0 | 19 criteria |
|
|
427
|
+
| WCAG 2.1 | 7 criteria |
|
|
428
|
+
| WCAG 2.2 | 3 criteria (focus appearance, target size, timeout) |
|
|
429
|
+
| Beyond WCAG | SpeechSynthesis, visual alerts, keyboard cheatsheet, audit score, colour blind modes, dyslexia font |
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Accessibility features — no screen reader software needed
|
|
434
|
+
|
|
435
|
+
| Feature | Who it helps | How |
|
|
436
|
+
|---|---|---|
|
|
437
|
+
| SpeechSynthesis | Blind users without screen reader | Browser reads focused element aloud |
|
|
438
|
+
| Visual alerts | Deaf users | Sliding banner — no audio |
|
|
439
|
+
| Skip links | All keyboard users | Jump to main, nav, search |
|
|
440
|
+
| Focus trap | Keyboard users | Tab stays inside modals |
|
|
441
|
+
| Arrow key nav | Keyboard users | Navigate menus, tabs, listbox |
|
|
442
|
+
| Keyboard cheatsheet | All users | Press Alt+A |
|
|
443
|
+
| Colour blind modes | Colour blind users | Deuteranopia, protanopia, tritanopia, greyscale |
|
|
444
|
+
| High contrast | Low vision users | Filter-based contrast boost |
|
|
445
|
+
| Dark mode | Light sensitivity | Invert + hue rotate |
|
|
446
|
+
| Large targets | Motor impaired | 44×44px minimum |
|
|
447
|
+
| Timeout warning | Motor impaired | Warning before session expires |
|
|
448
|
+
| Dyslexia font | Dyslexia users | Atkinson Hyperlegible |
|
|
449
|
+
| Reduce motion | Vestibular disorder | Disables all animations |
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
314
453
|
## The report
|
|
315
454
|
|
|
316
455
|
After clicking **Apply settings**, the panel shows:
|
|
317
456
|
|
|
318
457
|
```
|
|
319
|
-
✓ 12 fixes
|
|
320
|
-
✓ 0 fixes needed · 534 nodes clean · 0.8ms
|
|
458
|
+
✓ 12 fixes · 534 nodes · 1.2ms · Score: 88/100
|
|
459
|
+
✓ 0 fixes needed · 534 nodes clean · 0.8ms · Score: 100/100
|
|
321
460
|
```
|
|
322
461
|
|
|
323
|
-
`0 fixes needed` is a passing result —
|
|
462
|
+
`Score: 100/100` means the page is fully accessible. `0 fixes needed` is a passing result — the page was already accessible before yuktai ran.
|
|
324
463
|
|
|
325
464
|
---
|
|
326
465
|
|
|
327
466
|
## Design principles
|
|
328
467
|
|
|
329
|
-
- **Zero id attributes** — no injected node ever gets an `id`. All injected nodes
|
|
330
|
-
- **Zero API keys** —
|
|
331
|
-
- **Zero framework lock-in** — `core/renderer.ts` has no framework imports.
|
|
332
|
-
- **
|
|
468
|
+
- **Zero id attributes** — no injected node ever gets an `id`. All injected nodes tracked via module-level JavaScript references. Never collides with host app ids.
|
|
469
|
+
- **Zero API keys** — runs entirely in the browser. No external calls, no telemetry, no cost.
|
|
470
|
+
- **Zero framework lock-in** — `core/renderer.ts` has no framework imports. Works in Node.js, browsers, and test environments.
|
|
471
|
+
- **Zero JAWS dependency** — uses standard browser APIs. Works with NVDA, VoiceOver, TalkBack, Narrator — all free.
|
|
472
|
+
- **One engine, many adapters** — all adapters import from `core/renderer.ts`. WCAG logic written once.
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Browser support
|
|
477
|
+
|
|
478
|
+
| Browser | Supported |
|
|
479
|
+
|---|---|
|
|
480
|
+
| Chrome 90+ | ✅ |
|
|
481
|
+
| Firefox 90+ | ✅ |
|
|
482
|
+
| Safari 15+ | ✅ |
|
|
483
|
+
| Edge 90+ | ✅ |
|
|
484
|
+
| Samsung Internet 14+ | ✅ |
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Framework compatibility
|
|
489
|
+
|
|
490
|
+
| Framework | Version | Supported |
|
|
491
|
+
|---|---|---|
|
|
492
|
+
| React | 17, 18, 19 | ✅ |
|
|
493
|
+
| Next.js | 13, 14, 15 | ✅ |
|
|
494
|
+
| Vue | 3+ | ✅ |
|
|
495
|
+
| Nuxt | 3+ | ✅ |
|
|
496
|
+
| Angular | 15+ | ✅ |
|
|
497
|
+
| Remix | Any | ✅ |
|
|
498
|
+
| Astro | Any | ✅ |
|
|
499
|
+
| Vanilla JS | ES2020+ | ✅ |
|
|
333
500
|
|
|
334
501
|
---
|
|
335
502
|
|
|
336
503
|
## License
|
|
337
504
|
|
|
338
|
-
|
|
505
|
+
ISC © Sandeep Miriyala — [Yuktishaalaa AI Lab]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuktishaalaa/yuktai",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.14",
|
|
4
4
|
"description": "Privacy-first, offline-first universal accessibility engine with WCAG auto-fix, OCR, voice, and Indic language AI support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
"access": "public",
|
|
19
19
|
"registry": "https://registry.npmjs.org/"
|
|
20
20
|
},
|
|
21
|
-
"
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0",
|
|
23
|
+
"npm": ">=8.0.0"
|
|
24
|
+
},
|
|
25
|
+
"files": ["dist", ".npmrc"],
|
|
22
26
|
"scripts": {
|
|
23
27
|
"dev": "tsup --watch",
|
|
24
28
|
"build": "tsup",
|
|
@@ -27,12 +31,14 @@
|
|
|
27
31
|
"author": "Sandeep Miriyala",
|
|
28
32
|
"license": "ISC",
|
|
29
33
|
"peerDependencies": {
|
|
30
|
-
"react": ">=
|
|
31
|
-
"react-dom": ">=
|
|
34
|
+
"react": ">=17",
|
|
35
|
+
"react-dom": ">=17",
|
|
36
|
+
"next": ">=13"
|
|
32
37
|
},
|
|
33
38
|
"peerDependenciesMeta": {
|
|
34
39
|
"react": { "optional": true },
|
|
35
|
-
"react-dom": { "optional": true }
|
|
40
|
+
"react-dom": { "optional": true },
|
|
41
|
+
"next": { "optional": true }
|
|
36
42
|
},
|
|
37
43
|
"dependencies": {
|
|
38
44
|
"idb-keyval": "^6.2.2",
|