@reticular/speakable 1.6.0 → 1.7.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 +86 -2
- package/dist/browser/index.d.ts +58 -0
- package/dist/browser/index.js +3978 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/chunk-WYFXGXYU.js +87 -0
- package/dist/chunk-WYFXGXYU.js.map +1 -0
- package/dist/cli.js +40 -19
- package/dist/cli.js.map +1 -1
- package/dist/harness/index.d.ts +57 -0
- package/dist/harness/index.js +176 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/index.js +44 -22
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +40 -19
- package/dist/mcp.js.map +1 -1
- package/dist/speakable-browser.global.js +10 -0
- package/dist/speakable-browser.global.js.map +1 -0
- package/dist/storybook/dist/chunk-3SVI6IQE.js +86 -0
- package/dist/storybook/dist/{chunk-CL3DJBD7.js → chunk-JSBRDJBE.js} +1 -11
- package/dist/storybook/dist/chunk-QVJRQZT5.js +16 -0
- package/dist/storybook/dist/index.js +6 -1
- package/dist/storybook/dist/manager.js +404 -5
- package/dist/storybook/dist/preview.js +2117 -47
- package/dist/storybook/dist/upgrade-5GL42JH4.js +7 -0
- package/dist/types-Blh0EnZF.d.ts +213 -0
- package/dist/upgrade-53M5VYGH.js +7 -0
- package/dist/upgrade-53M5VYGH.js.map +1 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -258,7 +258,47 @@ Severity levels:
|
|
|
258
258
|
|
|
259
259
|
### Storybook Integration
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
There are two ways to use Speakable with Storybook: the **in-editor addon** (see timelines while you develop) and the **CLI adapter** (analyze the whole library headlessly in CI).
|
|
262
|
+
|
|
263
|
+
**In-editor addon** — adds a "Screen Readers" panel with per-reader tabs, an Audit tab, plus **Timeline** and **Diff** tabs for interaction testing:
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// .storybook/main.ts
|
|
267
|
+
const config = {
|
|
268
|
+
addons: ['@reticular/storybook-addon-speakable'],
|
|
269
|
+
};
|
|
270
|
+
export default config;
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Static tabs work with zero config. To capture an interaction timeline for a story, add `parameters.speakable` with either a named built-in `pattern` or a full `sequence`:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
export const Dialog = {
|
|
277
|
+
render: () => renderDialog(),
|
|
278
|
+
parameters: {
|
|
279
|
+
speakable: {
|
|
280
|
+
pattern: 'modal-dialog', // 'modal-dialog' | 'combobox' | 'tabs' | 'accordion'
|
|
281
|
+
selectors: { trigger: 'button.open', container: '[role="dialog"]' },
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
export const Disclosure = {
|
|
287
|
+
render: () => renderDisclosure(),
|
|
288
|
+
parameters: {
|
|
289
|
+
speakable: {
|
|
290
|
+
sequence: {
|
|
291
|
+
description: 'Tab to the toggle and open it',
|
|
292
|
+
actions: [{ type: 'tab' }, { type: 'enter' }],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The sequence runs automatically after the story renders; the Timeline tab shows the resulting focus moves, ARIA state changes, live-region announcements, and dialog events. Click **Set baseline** on the Timeline tab, and the Diff tab will flag behavioral regressions (severity-classified) on subsequent renders. See the [addon README](./addon-speakable/README.md) for the full `parameters.speakable` reference.
|
|
300
|
+
|
|
301
|
+
**CLI adapter** — connect to a running Storybook instance to analyze behavior across your library headlessly:
|
|
262
302
|
|
|
263
303
|
```typescript
|
|
264
304
|
import { createStorybookAdapter, createStoryLoader } from '@reticular/speakable/storybook';
|
|
@@ -277,6 +317,50 @@ The Storybook pipeline discovers stories, loads them in isolation, runs interact
|
|
|
277
317
|
|
|
278
318
|
> See the [Runtime Analysis docs](https://getspeakable.dev/docs/runtime-analysis) for interactive demos and full API reference.
|
|
279
319
|
|
|
320
|
+
### Browser Bundle and Iframe Harness
|
|
321
|
+
|
|
322
|
+
For runtime testing outside Storybook — including web components (Lit, shadow DOM) that need a real browser — Speakable ships two transport-agnostic entry points.
|
|
323
|
+
|
|
324
|
+
**`@reticular/speakable/browser`** runs analysis and capture directly against live DOM in the current page:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { analyzeElement, captureTimeline } from '@reticular/speakable/browser';
|
|
328
|
+
|
|
329
|
+
// Static analysis of a live element (per-reader output + audit + stats + warnings)
|
|
330
|
+
const result = analyzeElement(document.querySelector('#widget'));
|
|
331
|
+
console.log(result.nvda);
|
|
332
|
+
|
|
333
|
+
// Capture an interaction timeline against the live document
|
|
334
|
+
const timeline = await captureTimeline(document, {
|
|
335
|
+
componentName: 'Menu',
|
|
336
|
+
sequence: {
|
|
337
|
+
description: 'open and arrow down',
|
|
338
|
+
actions: [{ type: 'click', selector: '#menu-btn' }, { type: 'arrowDown' }],
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**`@reticular/speakable/harness`** mounts a component into an iframe (via a URL or an HTML string), injects the bundle, and drives analysis over `postMessage` — so you can test any component in isolation in a real browser. Custom elements are awaited (`whenDefined` / Lit `updateComplete`) before analysis, and open shadow roots with `<slot>` projection are handled:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { createHarness } from '@reticular/speakable/harness';
|
|
347
|
+
|
|
348
|
+
const harness = createHarness({
|
|
349
|
+
target: { container: document.body }, // harness creates + owns the iframe
|
|
350
|
+
bundleUrl: '/speakable-browser.global.js', // the injectable IIFE bundle
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await harness.load({ html: '<my-widget>Content</my-widget>' });
|
|
354
|
+
const result = await harness.analyze('my-widget');
|
|
355
|
+
const timeline = await harness.captureTimeline({
|
|
356
|
+
componentName: 'MyWidget',
|
|
357
|
+
sequence: { description: 'toggle', actions: [{ type: 'click', selector: 'my-widget' }] },
|
|
358
|
+
});
|
|
359
|
+
harness.destroy();
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
The harness supports same-origin content only (`srcdoc` HTML or a same-origin URL); cross-origin URLs are rejected with a descriptive error, since a script can't be injected across origins. See [`examples/harness-demo.html`](./examples/harness-demo.html) for a runnable simple + complex (Lit-style shadow DOM) example.
|
|
363
|
+
|
|
280
364
|
## MCP Integration (AI Assistants)
|
|
281
365
|
|
|
282
366
|
Speakable includes a Model Context Protocol (MCP) server that lets AI coding assistants analyze accessibility in real-time. Ask your assistant "check if this component is accessible" and it calls Speakable automatically.
|
|
@@ -390,7 +474,7 @@ Speakable processes HTML through a four-stage pipeline:
|
|
|
390
474
|
- Replace manual screen reader testing with real assistive technology
|
|
391
475
|
- Guarantee WCAG compliance
|
|
392
476
|
- Perfectly replicate screen reader behavior (output is heuristic)
|
|
393
|
-
- Execute JavaScript in static analysis mode (use runtime
|
|
477
|
+
- Execute JavaScript in static analysis mode (use the CLI `runtime` command, the Storybook addon's Timeline, or the browser/harness entry points for dynamic content)
|
|
394
478
|
|
|
395
479
|
Use Speakable to catch issues early and reduce the manual testing burden. Validate critical flows with real screen readers.
|
|
396
480
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { I as InteractionSequence, A as AccessibilityTimeline } from '../types-Blh0EnZF.js';
|
|
2
|
+
export { a as AnalysisResult, b as AuditFinding, c as analyzeElement, d as analyzeElementWithUpgrade } from '../types-Blh0EnZF.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transport-agnostic interaction timeline capture.
|
|
6
|
+
*
|
|
7
|
+
* Wraps the core runtime engine (`src/runtime`) so any real-browser surface
|
|
8
|
+
* (Storybook addon, iframe harness, extension) can run an interaction
|
|
9
|
+
* sequence against a live document and receive a serializable timeline.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface CaptureOptions {
|
|
13
|
+
/** Component name for timeline metadata. */
|
|
14
|
+
componentName: string;
|
|
15
|
+
/** Story or variant name for timeline metadata. */
|
|
16
|
+
storyName?: string;
|
|
17
|
+
/** Ordered interaction sequence to execute during capture. */
|
|
18
|
+
sequence: InteractionSequence;
|
|
19
|
+
/** Settle period after the last action (ms). */
|
|
20
|
+
settlePeriod?: number;
|
|
21
|
+
/** Await custom-element upgrade before capture (default: true). */
|
|
22
|
+
awaitUpgrade?: boolean;
|
|
23
|
+
/** Maximum time to wait for custom elements to upgrade (ms). */
|
|
24
|
+
upgradeTimeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Attach the runtime engine to `document`, run the interaction sequence,
|
|
28
|
+
* and return a serializable accessibility timeline.
|
|
29
|
+
*
|
|
30
|
+
* When `awaitUpgrade` is enabled (default), waits for custom elements to
|
|
31
|
+
* upgrade first so web components (e.g. Lit) are analyzed post-hydration.
|
|
32
|
+
* Any upgrade warnings are appended to the timeline's `warnings` array.
|
|
33
|
+
*/
|
|
34
|
+
declare function captureTimeline(document: Document, options: CaptureOptions): Promise<AccessibilityTimeline>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Custom-element upgrade awaiting.
|
|
38
|
+
*
|
|
39
|
+
* Runs in a real browser (needs `customElements`). Waits for custom elements
|
|
40
|
+
* under a root to be defined and, where supported, to finish rendering
|
|
41
|
+
* (Lit's `updateComplete`) before analysis/capture proceeds. Bounded by a
|
|
42
|
+
* timeout so it never blocks indefinitely; unresolved tags become warnings.
|
|
43
|
+
*/
|
|
44
|
+
interface UpgradeWarning {
|
|
45
|
+
message: string;
|
|
46
|
+
tag: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Wait for custom elements under `root` to upgrade and render.
|
|
50
|
+
*
|
|
51
|
+
* - Awaits `customElements.whenDefined(tag)` for each custom tag (bounded).
|
|
52
|
+
* - Awaits any `updateComplete` promises found (bounded).
|
|
53
|
+
* - On timeout, resolves anyway and returns a warning per unresolved tag.
|
|
54
|
+
* - Never blocks indefinitely. No-op when `customElements` is unavailable.
|
|
55
|
+
*/
|
|
56
|
+
declare function awaitCustomElementsReady(root: Document | Element, timeoutMs?: number): Promise<UpgradeWarning[]>;
|
|
57
|
+
|
|
58
|
+
export { type CaptureOptions, type UpgradeWarning, awaitCustomElementsReady, captureTimeline };
|