ally-widget 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/README.md +527 -0
- package/dist/ally-widget.cjs.js +4468 -0
- package/dist/ally-widget.esm.js +4466 -0
- package/dist/ally-widget.js +4471 -0
- package/dist/ally-widget.min.js +93 -0
- package/dist/ally-widget.min.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
# Ally Widget
|
|
2
|
+
|
|
3
|
+
A lightweight, framework-free accessibility widget for any website. Drop it in with a single `<script>` tag or import it as an npm package — it adds a floating panel of accessibility controls that users can customize per-session.
|
|
4
|
+
|
|
5
|
+
- **No dependencies** — vanilla JS, ~154 KB minified
|
|
6
|
+
- **Three delivery formats** — CDN (IIFE), ESM (bundlers), CJS (Node/SSR)
|
|
7
|
+
- **Languages** — English and Nepali built-in
|
|
8
|
+
- **WCAG 2.x aligned** — focus trapping, ARIA roles, keyboard navigation throughout
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
### CDN / Direct script tag
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js"></script>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or download `dist/ally-widget.min.js` and host it yourself.
|
|
21
|
+
|
|
22
|
+
### npm
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install ally-widget
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
// ESM bundler (Vite, webpack, Rollup…)
|
|
30
|
+
import AllyWidget from 'ally-widget';
|
|
31
|
+
|
|
32
|
+
// CommonJS (Node / SSR)
|
|
33
|
+
const AllyWidget = require('ally-widget');
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Script tag (zero config)
|
|
41
|
+
|
|
42
|
+
The widget auto-initialises on `DOMContentLoaded`. Just include the script:
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<script src="dist/ally-widget.min.js"></script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A button appears at the bottom-right corner. Press **Alt + A** to toggle the panel.
|
|
49
|
+
|
|
50
|
+
### Configure before loading
|
|
51
|
+
|
|
52
|
+
Set `window.AllyWidgetOptions` **before** the script tag:
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<script>
|
|
56
|
+
window.AllyWidgetOptions = {
|
|
57
|
+
position: 'bottom-right',
|
|
58
|
+
lang: 'ne', // 'en' | 'ne'
|
|
59
|
+
primaryColor: '#e11d48',
|
|
60
|
+
poweredByText: 'My Company',
|
|
61
|
+
poweredByUrl: 'https://mycompany.com'
|
|
62
|
+
};
|
|
63
|
+
</script>
|
|
64
|
+
<script src="dist/ally-widget.min.js"></script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### ESM / npm
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
import AllyWidget from 'ally-widget';
|
|
71
|
+
|
|
72
|
+
const widget = new AllyWidget({
|
|
73
|
+
position: 'bottom-left',
|
|
74
|
+
primaryColor: '#0ea5e9',
|
|
75
|
+
disableFeatures: ['hide-images', 'annotations'],
|
|
76
|
+
onFeatureToggle(key, enabled) {
|
|
77
|
+
analytics.track('a11y_toggle', { key, enabled });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
widget.startAccessibleWebWidget();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## All Options
|
|
87
|
+
|
|
88
|
+
| Option | Type | Default | Description |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `position` | `string` | `'bottom-right'` | `'bottom-right'` \| `'bottom-left'` \| `'top-right'` \| `'top-left'` |
|
|
91
|
+
| `offset` | `[number, number]` | `[20, 20]` | `[horizontal, vertical]` px from edge |
|
|
92
|
+
| `size` | `string` | `'52px'` | Toggle button size — px, em, rem, or % |
|
|
93
|
+
| `lang` | `string` | browser lang | `'en'` or `'ne'` |
|
|
94
|
+
| `storageKey` | `string` | `'ally-wgt'` | localStorage key — change to avoid collisions when multiple instances share a domain |
|
|
95
|
+
| `keyboardShortcut` | `boolean` | `true` | `Alt+A` toggles the panel. Set `false` to disable |
|
|
96
|
+
| `icon` | `string` | `'default'` | Built-in variant name or a raw SVG string for the toggle button icon |
|
|
97
|
+
| `icons` | `object` | — | Override any icon in the full icon set (see [Icon Keys](#icon-keys)) |
|
|
98
|
+
| `poweredByText` | `string` | `'Ally Widget'` | Footer link label |
|
|
99
|
+
| `poweredByUrl` | `string` | widget repo URL | Footer link href |
|
|
100
|
+
| `disableFeatures` | `string[]` | `[]` | Feature keys to hide from the panel (see [Feature Keys](#feature-keys)) |
|
|
101
|
+
| `featureOverrides` | `object` | `{}` | Override `label` or `icon` for any feature button |
|
|
102
|
+
| `theme` | `object` | — | All theme tokens nested under one key (alternative to passing tokens at the top level) |
|
|
103
|
+
| `ttsNativeVoiceName` | `string` | — | Exact browser voice name to prefer for TTS |
|
|
104
|
+
| `ttsNativeVoiceLang` | `string` | — | BCP-47 language tag for TTS voice selection (e.g. `'ne-NP'`) |
|
|
105
|
+
| `ttsRate` | `number` | `1` | Speech rate — `0.5` to `2` |
|
|
106
|
+
| `ttsPitch` | `number` | `1` | Speech pitch — `0` to `2` |
|
|
107
|
+
| `onOpen` | `function` | — | Called when the panel opens |
|
|
108
|
+
| `onClose` | `function` | — | Called when the panel closes |
|
|
109
|
+
| `onFeatureToggle` | `function(key, enabled)` | — | Called on every feature toggle |
|
|
110
|
+
| `onReset` | `function` | — | Called when the user resets all settings |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Theme Customization
|
|
115
|
+
|
|
116
|
+
Pass any of these tokens directly in options, or group them under `theme: { … }`:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
window.AllyWidgetOptions = {
|
|
120
|
+
// Option A — flat
|
|
121
|
+
primaryColor: '#e11d48',
|
|
122
|
+
borderRadius: '8px',
|
|
123
|
+
|
|
124
|
+
// Option B — grouped (same result)
|
|
125
|
+
theme: {
|
|
126
|
+
primaryColor: '#e11d48',
|
|
127
|
+
borderRadius: '8px'
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Theme tokens
|
|
133
|
+
|
|
134
|
+
| Token | Default | Notes |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `primaryColor` | `#4f46e5` | Main brand color — button, active states |
|
|
137
|
+
| `primaryColorLight` | `#818cf8` | Lighter tint |
|
|
138
|
+
| `primaryColorDark` | `#3730a3` | Darker shade |
|
|
139
|
+
| `backgroundColor` | `#ffffff` | Panel background |
|
|
140
|
+
| `textColor` | `#1f2937` | Panel text |
|
|
141
|
+
| `textColorInverted` | `#ffffff` | Text on primary background |
|
|
142
|
+
| `cardBackground` | `#f9fafb` | Feature card background |
|
|
143
|
+
| `borderColor` | `#e5e7eb` | Dividers and borders |
|
|
144
|
+
| `focusRingColor` | `#4f46e5` | Keyboard focus ring |
|
|
145
|
+
| `hoverColor` | `#f3f4f6` | Button hover background |
|
|
146
|
+
| `activeColor` | `#ede9fe` | Active/selected button background |
|
|
147
|
+
| `borderRadius` | `12px` | Panel corner radius |
|
|
148
|
+
| `buttonBorderRadius` | `50%` | Toggle button shape |
|
|
149
|
+
| `headerHeight` | `56px` | Panel header height |
|
|
150
|
+
| `focusBorderWidth` | `2px` | Focus ring width |
|
|
151
|
+
| `focusOutlineOffset` | `2px` | Focus ring offset |
|
|
152
|
+
| `zIndex` | `9999` | Stacking context |
|
|
153
|
+
| `buttonSize` | `52px` | Alias for `size` |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Feature Keys
|
|
158
|
+
|
|
159
|
+
Use these keys with `disableFeatures` and `featureOverrides`.
|
|
160
|
+
|
|
161
|
+
### Text
|
|
162
|
+
|
|
163
|
+
| Key | Label |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `text-scale` | Font Size (slider) |
|
|
166
|
+
| `bold-text` | Font Weight |
|
|
167
|
+
| `line-spacing` | Line Height |
|
|
168
|
+
| `letter-spacing` | Letter Spacing |
|
|
169
|
+
| `readable-text` | Dyslexia Font |
|
|
170
|
+
|
|
171
|
+
### Color & Contrast
|
|
172
|
+
|
|
173
|
+
| Key | Label |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `contrast-toggle` | Contrast (cycles light → dark) |
|
|
176
|
+
| `saturation-toggle` | Saturation (cycles low → high) |
|
|
177
|
+
| `invert-colors` | Invert Colors |
|
|
178
|
+
|
|
179
|
+
### Reading Aids
|
|
180
|
+
|
|
181
|
+
| Key | Label |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `highlight-links` | Highlight Links |
|
|
184
|
+
| `highlight-title` | Highlight Titles |
|
|
185
|
+
| `reading-aid` | Reading Guide |
|
|
186
|
+
| `simple-layout` | Simplify Layout |
|
|
187
|
+
|
|
188
|
+
### Interaction
|
|
189
|
+
|
|
190
|
+
| Key | Label |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `large-pointer` | Big Cursor |
|
|
193
|
+
| `pause-motion` | Stop Animations |
|
|
194
|
+
| `hide-images` | Hide Images |
|
|
195
|
+
| `high-contrast-mode` | High Contrast |
|
|
196
|
+
|
|
197
|
+
### Speech
|
|
198
|
+
|
|
199
|
+
| Key | Label |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `text-to-speech` | Text to Speech |
|
|
202
|
+
|
|
203
|
+
### Dev mode only (`?ally-dev=true`)
|
|
204
|
+
|
|
205
|
+
| Key | Label |
|
|
206
|
+
|---|---|
|
|
207
|
+
| `annotations` | Annotations (axe-core overlays) |
|
|
208
|
+
| `accessibility-report` | Accessibility Report |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Disabling Features
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
window.AllyWidgetOptions = {
|
|
216
|
+
disableFeatures: [
|
|
217
|
+
'hide-images',
|
|
218
|
+
'readable-text', // remove Dyslexia Font button
|
|
219
|
+
'annotations',
|
|
220
|
+
'accessibility-report'
|
|
221
|
+
]
|
|
222
|
+
};
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Feature Label & Icon Overrides
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
window.AllyWidgetOptions = {
|
|
231
|
+
featureOverrides: {
|
|
232
|
+
'bold-text': {
|
|
233
|
+
label: 'Bold'
|
|
234
|
+
},
|
|
235
|
+
'text-to-speech': {
|
|
236
|
+
label: 'Read Aloud',
|
|
237
|
+
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">…</svg>'
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Icon Keys
|
|
246
|
+
|
|
247
|
+
Pass a partial `icons` object to swap out any icon in the set:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
window.AllyWidgetOptions = {
|
|
251
|
+
icons: {
|
|
252
|
+
largePointer: '<svg>…</svg>',
|
|
253
|
+
pauseMotion: '<svg>…</svg>',
|
|
254
|
+
readingAid: '<svg>…</svg>',
|
|
255
|
+
textToSpeech: '<svg>…</svg>',
|
|
256
|
+
highContrast: '<svg>…</svg>',
|
|
257
|
+
simplifyLayout: '<svg>…</svg>',
|
|
258
|
+
boldText: '<svg>…</svg>',
|
|
259
|
+
lineSpacing: '<svg>…</svg>',
|
|
260
|
+
letterSpacing: '<svg>…</svg>',
|
|
261
|
+
hideImages: '<svg>…</svg>',
|
|
262
|
+
dyslexiaFont: '<svg>…</svg>',
|
|
263
|
+
highlightLinks: '<svg>…</svg>',
|
|
264
|
+
highlightTitle: '<svg>…</svg>',
|
|
265
|
+
adjustFontSize: '<svg>…</svg>',
|
|
266
|
+
contrast: '<svg>…</svg>',
|
|
267
|
+
invertColors: '<svg>…</svg>',
|
|
268
|
+
saturation: '<svg>…</svg>',
|
|
269
|
+
annotations: '<svg>…</svg>',
|
|
270
|
+
accessibilityReport: '<svg>…</svg>',
|
|
271
|
+
reset: '<svg>…</svg>',
|
|
272
|
+
accessibility: '<svg>…</svg>' // toggle button default icon
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Language
|
|
280
|
+
|
|
281
|
+
The widget ships with English (`en`) and Nepali (`ne`).
|
|
282
|
+
|
|
283
|
+
```js
|
|
284
|
+
window.AllyWidgetOptions = { lang: 'ne' };
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The language picker in the panel footer lets users switch at runtime. The selection is persisted to localStorage.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Text to Speech
|
|
292
|
+
|
|
293
|
+
The widget uses the browser's native [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API) — no external service or API key required.
|
|
294
|
+
|
|
295
|
+
When TTS is enabled, click any text element on the page to hear it read aloud.
|
|
296
|
+
|
|
297
|
+
**Nepali TTS** — modern browsers (Chrome, Edge) include a `ne-NP` voice. To force a specific voice:
|
|
298
|
+
|
|
299
|
+
```js
|
|
300
|
+
window.AllyWidgetOptions = {
|
|
301
|
+
ttsNativeVoiceLang: 'ne-NP',
|
|
302
|
+
ttsNativeVoiceName: 'Google नेपाली', // optional — exact voice name
|
|
303
|
+
ttsRate: 0.9,
|
|
304
|
+
ttsPitch: 1.0
|
|
305
|
+
};
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
If the requested voice is unavailable the widget falls back to the browser's default voice.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Event Callbacks
|
|
313
|
+
|
|
314
|
+
```js
|
|
315
|
+
window.AllyWidgetOptions = {
|
|
316
|
+
onOpen() {
|
|
317
|
+
console.log('Panel opened');
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
onClose() {
|
|
321
|
+
console.log('Panel closed');
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
onFeatureToggle(key, enabled) {
|
|
325
|
+
// key — feature key string (e.g. 'bold-text')
|
|
326
|
+
// enabled — boolean (true = on, false = off)
|
|
327
|
+
analytics.track('a11y_feature', { key, enabled });
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
onReset() {
|
|
331
|
+
console.log('All settings cleared');
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Data Attributes
|
|
339
|
+
|
|
340
|
+
You can configure the widget via attributes on the script tag instead of `window.AllyWidgetOptions`:
|
|
341
|
+
|
|
342
|
+
```html
|
|
343
|
+
<script
|
|
344
|
+
src="dist/ally-widget.min.js"
|
|
345
|
+
data-ally-lang="ne"
|
|
346
|
+
data-ally-position="bottom-left"
|
|
347
|
+
data-ally-size="60px"
|
|
348
|
+
data-ally-offset="24,24"
|
|
349
|
+
></script>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Supported attributes: `data-ally-lang`, `data-ally-position`, `data-ally-offset`, `data-ally-size`, `data-ally-icon`.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Framework Integration
|
|
357
|
+
|
|
358
|
+
### Next.js (App Router)
|
|
359
|
+
|
|
360
|
+
The widget is browser-only. Use `AllyWidget.init()` inside `useEffect` so it never runs on the server.
|
|
361
|
+
|
|
362
|
+
```jsx
|
|
363
|
+
// components/AllyWidgetLoader.jsx
|
|
364
|
+
'use client'
|
|
365
|
+
import { useEffect } from 'react'
|
|
366
|
+
|
|
367
|
+
export default function AllyWidgetLoader() {
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
import('ally-widget').then(({ default: AllyWidget }) => {
|
|
370
|
+
AllyWidget.init({
|
|
371
|
+
position: 'bottom-right',
|
|
372
|
+
primaryColor: '#6366f1',
|
|
373
|
+
lang: 'en'
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
}, [])
|
|
377
|
+
|
|
378
|
+
return null
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
```jsx
|
|
383
|
+
// app/layout.jsx
|
|
384
|
+
import AllyWidgetLoader from '@/components/AllyWidgetLoader'
|
|
385
|
+
|
|
386
|
+
export default function RootLayout({ children }) {
|
|
387
|
+
return (
|
|
388
|
+
<html lang="en">
|
|
389
|
+
<body>
|
|
390
|
+
{children}
|
|
391
|
+
<AllyWidgetLoader />
|
|
392
|
+
</body>
|
|
393
|
+
</html>
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Next.js (Pages Router / `_document`)
|
|
399
|
+
|
|
400
|
+
```jsx
|
|
401
|
+
// pages/_app.jsx
|
|
402
|
+
import { useEffect } from 'react'
|
|
403
|
+
|
|
404
|
+
export default function App({ Component, pageProps }) {
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
import('ally-widget').then(({ default: AllyWidget }) => {
|
|
407
|
+
AllyWidget.init({ primaryColor: '#6366f1' })
|
|
408
|
+
})
|
|
409
|
+
}, [])
|
|
410
|
+
|
|
411
|
+
return <Component {...pageProps} />
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Next.js — CDN via `next/script` (zero npm install)
|
|
416
|
+
|
|
417
|
+
```jsx
|
|
418
|
+
// app/layout.jsx
|
|
419
|
+
import Script from 'next/script'
|
|
420
|
+
|
|
421
|
+
export default function RootLayout({ children }) {
|
|
422
|
+
return (
|
|
423
|
+
<html lang="en">
|
|
424
|
+
<body>
|
|
425
|
+
{children}
|
|
426
|
+
<Script id="ally-config" strategy="beforeInteractive">{`
|
|
427
|
+
window.AllyWidgetOptions = { primaryColor: '#6366f1' };
|
|
428
|
+
`}</Script>
|
|
429
|
+
<Script
|
|
430
|
+
src="https://cdn.jsdelivr.net/npm/ally-widget/dist/ally-widget.min.js"
|
|
431
|
+
strategy="afterInteractive"
|
|
432
|
+
/>
|
|
433
|
+
</body>
|
|
434
|
+
</html>
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Nuxt 3
|
|
440
|
+
|
|
441
|
+
```js
|
|
442
|
+
// plugins/ally-widget.client.js ← the .client suffix means browser-only
|
|
443
|
+
import AllyWidget from 'ally-widget'
|
|
444
|
+
|
|
445
|
+
export default defineNuxtPlugin(() => {
|
|
446
|
+
AllyWidget.init({
|
|
447
|
+
position: 'bottom-right',
|
|
448
|
+
primaryColor: '#6366f1'
|
|
449
|
+
})
|
|
450
|
+
})
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Laravel / Blade
|
|
454
|
+
|
|
455
|
+
```html
|
|
456
|
+
{{-- resources/views/layouts/app.blade.php --}}
|
|
457
|
+
<script>
|
|
458
|
+
window.AllyWidgetOptions = {
|
|
459
|
+
position: 'bottom-right',
|
|
460
|
+
primaryColor: '#6366f1',
|
|
461
|
+
poweredByText: '{{ config("app.name") }}'
|
|
462
|
+
};
|
|
463
|
+
</script>
|
|
464
|
+
<script src="{{ asset('js/ally-widget.min.js') }}"></script>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### `AllyWidget.init()` reference
|
|
468
|
+
|
|
469
|
+
```js
|
|
470
|
+
// Returns the widget instance (or null when called server-side)
|
|
471
|
+
const instance = AllyWidget.init(options)
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Accepts the same options object as the constructor. Handles `DOMContentLoaded` automatically — safe to call before the document is ready.
|
|
475
|
+
|
|
476
|
+
### Disabling auto-init
|
|
477
|
+
|
|
478
|
+
When importing via npm in a framework, the CDN auto-init still fires if the module loads in a browser context. Suppress it with `autoInit: false`:
|
|
479
|
+
|
|
480
|
+
```html
|
|
481
|
+
<script>
|
|
482
|
+
window.AllyWidgetOptions = { autoInit: false };
|
|
483
|
+
</script>
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Or when the module is loaded purely programmatically (e.g. inside `useEffect`), auto-init is already skipped because the module executes after `DOMContentLoaded` has already fired and you're calling `AllyWidget.init()` yourself.
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Dev Mode
|
|
491
|
+
|
|
492
|
+
Append `?ally-dev=true` to any URL to enable the Annotations and Accessibility Report tools. These run an axe-core scan and overlay issue markers on the page — useful during development, hidden in production.
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
https://yoursite.com/page?ally-dev=true
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Keyboard Shortcut
|
|
501
|
+
|
|
502
|
+
**Alt + A** toggles the panel on any page. Disable it with `keyboardShortcut: false`.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Publishing to npm
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
# Set your token
|
|
510
|
+
export NPM_TOKEN=your_token_here # or put it in .env
|
|
511
|
+
|
|
512
|
+
# Build then publish
|
|
513
|
+
npm run build
|
|
514
|
+
npm publish
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
The `.npmrc` at the project root reads `NPM_TOKEN` automatically:
|
|
518
|
+
|
|
519
|
+
```
|
|
520
|
+
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## License
|
|
526
|
+
|
|
527
|
+
MIT — see [LICENSE](LICENSE)
|