@surea11y/core 1.5.0 → 1.6.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/CHANGELOG.md +193 -149
- package/README.md +27 -6
- package/docs/ACT_RULE_MAPPING.md +243 -0
- package/docs/API_STABILITY.md +2 -2
- package/docs/BINDING_AUTHORS_GUIDE.md +3 -3
- package/docs/DESIGN_CHALLENGES.md +301 -0
- package/docs/ENGINE_OPTIONS.md +16 -4
- package/docs/I18N.md +4 -4
- package/docs/INTEGRATION.md +1 -1
- package/docs/LIMITATIONS.md +6 -4
- package/docs/REPORT.md +1 -1
- package/docs/RULE_AUTHORING.md +53 -25
- package/docs/RULE_CATALOG.md +1878 -169
- package/docs/RULE_TAXONOMY.md +2 -2
- package/docs/TROUBLESHOOTING.md +2 -2
- package/docs/WCAG_CONFORMANCE.md +25 -9
- package/package.json +3 -7
- package/src/baseline.js +3 -3
- package/src/checks/automatic/area-alt-present.js +2 -2
- package/src/checks/automatic/aria-allowed-attr.js +68 -10
- package/src/checks/automatic/aria-allowed-role.js +2 -2
- package/src/checks/automatic/aria-braille-equivalent.js +3 -3
- package/src/checks/automatic/aria-conditional-attr.js +5 -5
- package/src/checks/automatic/aria-deprecated-role.js +1 -1
- package/src/checks/automatic/aria-hidden-body.js +2 -2
- package/src/checks/automatic/aria-hidden-focus.js +5 -5
- package/src/checks/automatic/aria-prohibited-attr.js +18 -18
- package/src/checks/automatic/aria-prohibited-children.js +130 -37
- package/src/checks/automatic/aria-required-attr.js +60 -12
- package/src/checks/automatic/aria-required-children.js +21 -14
- package/src/checks/automatic/aria-required-parent.js +61 -9
- package/src/checks/automatic/aria-role-name-present.js +36 -22
- package/src/checks/automatic/aria-valid-attr-value.js +15 -12
- package/src/checks/automatic/aria-valid-attr.js +1 -1
- package/src/checks/automatic/autocomplete-valid.js +2 -2
- package/src/checks/automatic/binary-control-name-present.js +27 -5
- package/src/checks/automatic/button-name-present.js +92 -6
- package/src/checks/automatic/combobox-name-present.js +26 -6
- package/src/checks/automatic/contrast-computable.js +32 -0
- package/src/checks/automatic/contrast-enhanced.js +21 -1
- package/src/checks/automatic/contrast-minimum.js +21 -1
- package/src/checks/automatic/css-orientation-lock.js +96 -19
- package/src/checks/automatic/definition-list-children-valid.js +7 -8
- package/src/checks/automatic/deprecated-elements-not-used.js +1 -1
- package/src/checks/automatic/dialog-name-present.js +20 -2
- package/src/checks/automatic/duplicate-id-aria.js +5 -3
- package/src/checks/automatic/duplicate-id.js +198 -0
- package/src/checks/automatic/embed-text-alternative-present.js +2 -2
- package/src/checks/automatic/form-control-programmatic-label-present.js +19 -2
- package/src/checks/automatic/form-control-single-label.js +1 -1
- package/src/checks/automatic/iframe-focusable-content.js +63 -7
- package/src/checks/automatic/iframe-name-present.js +37 -3
- package/src/checks/automatic/iframe-title-unique.js +1 -1
- package/src/checks/automatic/img-alt-present.js +12 -4
- package/src/checks/automatic/label-in-name.js +172 -18
- package/src/checks/automatic/link-in-text-block.js +10 -10
- package/src/checks/automatic/link-name-present.js +22 -1
- package/src/checks/automatic/list-children-valid.js +6 -6
- package/src/checks/automatic/listbox-name-present.js +28 -8
- package/src/checks/automatic/listitem-parent-valid.js +4 -4
- package/src/checks/automatic/menuitem-name-present.js +20 -2
- package/src/checks/automatic/meta-refresh-no-exceptions.js +25 -14
- package/src/checks/automatic/meta-refresh-timing-absent.js +14 -7
- package/src/checks/automatic/meter-name-present.js +23 -4
- package/src/checks/automatic/nested-interactive-controls-absent.js +5 -5
- package/src/checks/automatic/option-name-present.js +23 -4
- package/src/checks/automatic/page-title-present.js +21 -3
- package/src/checks/automatic/presentational-children-focusable-absent.js +330 -0
- package/src/checks/automatic/progressbar-name-present.js +23 -4
- package/src/checks/automatic/role-img-alt-present.js +64 -16
- package/src/checks/automatic/searchbox-name-present.js +28 -8
- package/src/checks/automatic/server-side-image-map-absent.js +1 -1
- package/src/checks/automatic/slider-name-present.js +27 -6
- package/src/checks/automatic/spinbutton-name-present.js +28 -8
- package/src/checks/automatic/summary-name-present.js +18 -2
- package/src/checks/automatic/svg-image-text-alternative-present.js +1 -1
- package/src/checks/automatic/svg-text-alternative-present.js +13 -10
- package/src/checks/automatic/tab-name-present.js +21 -2
- package/src/checks/automatic/table-headers-attr-valid.js +43 -8
- package/src/checks/automatic/table-th-has-data-cells.js +61 -5
- package/src/checks/automatic/target-size-minimum.js +71 -53
- package/src/checks/automatic/td-has-header.js +5 -5
- package/src/checks/automatic/textbox-name-present.js +28 -8
- package/src/checks/automatic/tooltip-name-present.js +21 -2
- package/src/checks/automatic/treeitem-name-present.js +23 -4
- package/src/checks/automatic/valid-lang.js +92 -7
- package/src/checks/automatic/video-poster-text-alternative-present.js +1 -1
- package/src/checks/manual/accesskeys-manual.js +3 -3
- package/src/checks/manual/area-alt-decorative-manual.js +7 -0
- package/src/checks/manual/area-alt-quality-manual.js +6 -0
- package/src/checks/manual/aria-checked-state-mismatch-manual.js +5 -5
- package/src/checks/manual/aria-text-manual.js +4 -4
- package/src/checks/manual/bypass-blocks-present-manual.js +44 -26
- package/src/checks/manual/canvas-text-alternative-quality-manual.js +8 -0
- package/src/checks/manual/css-focus-indicator-suppressed-manual.js +444 -0
- package/src/checks/manual/embed-text-alternative-quality-manual.js +8 -0
- package/src/checks/manual/empty-heading-manual.js +58 -11
- package/src/checks/manual/empty-table-header-manual.js +8 -8
- package/src/checks/manual/focus-order-semantics-manual.js +15 -15
- package/src/checks/manual/form-control-label-quality-manual.js +453 -0
- package/src/checks/manual/form-control-programmatic-label-quality-manual.js +2 -2
- package/src/checks/manual/heading-order-manual.js +3 -3
- package/src/checks/manual/heading-quality-manual.js +338 -0
- package/src/checks/manual/identical-links-same-purpose-manual.js +73 -12
- package/src/checks/manual/image-redundant-alt-manual.js +4 -4
- package/src/checks/manual/img-alt-decorative-manual.js +211 -52
- package/src/checks/manual/img-alt-quality-manual.js +7 -0
- package/src/checks/manual/input-image-alt-decorative-manual.js +8 -0
- package/src/checks/manual/input-image-alt-quality-manual.js +5 -0
- package/src/checks/manual/label-title-only-manual.js +4 -4
- package/src/checks/manual/landmark-banner-is-top-level-manual.js +7 -7
- package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +9 -9
- package/src/checks/manual/landmark-main-is-top-level-manual.js +6 -6
- package/src/checks/manual/landmark-no-duplicate-banner-manual.js +6 -6
- package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +6 -6
- package/src/checks/manual/landmark-no-duplicate-main-manual.js +2 -2
- package/src/checks/manual/landmark-one-main-manual.js +6 -6
- package/src/checks/manual/landmark-unique-manual.js +9 -9
- package/src/checks/manual/link-name-quality-manual.js +161 -32
- package/src/checks/manual/media-transcript-present-manual.js +2 -3
- package/src/checks/manual/meta-viewport-large-manual.js +2 -2
- package/src/checks/manual/mouse-only-event-handlers-manual.js +9 -9
- package/src/checks/manual/no-autoplay-audio-manual.js +3 -3
- package/src/checks/manual/object-text-alternative-quality-manual.js +8 -0
- package/src/checks/manual/p-as-heading-manual.js +4 -4
- package/src/checks/manual/page-has-heading-one-manual.js +6 -6
- package/src/checks/manual/page-title-patterns-manual.js +26 -3
- package/src/checks/manual/presentation-role-conflict-manual.js +55 -25
- package/src/checks/manual/region-manual.js +19 -19
- package/src/checks/manual/scope-attr-valid-manual.js +2 -2
- package/src/checks/manual/scrollable-region-focusable-manual.js +7 -7
- package/src/checks/manual/skip-link-manual.js +5 -5
- package/src/checks/manual/svg-text-alternative-quality-manual.js +9 -0
- package/src/checks/manual/tabindex-manual.js +2 -2
- package/src/checks/manual/table-duplicate-name-manual.js +2 -2
- package/src/checks/manual/table-fake-caption-manual.js +2 -2
- package/src/checks/manual/video-caption-manual.js +3 -3
- package/src/checks/manual-review.js +17 -1
- package/src/core.js +8965 -1647
- package/src/report.js +2 -2
- package/surea11y.browser.js +3768 -611
- package/surea11y.i18n.de.js +1 -1
- package/surea11y.i18n.es.js +1 -1
- package/surea11y.i18n.fr.js +1 -1
- package/bin/surea11y-core.js +0 -20
package/docs/RULE_AUTHORING.md
CHANGED
|
@@ -52,7 +52,16 @@ function runInPage(ctx) { /* see Runtime Contract */ }
|
|
|
52
52
|
module.exports = { id, meta, runInPage };
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
One optional fourth export: `applicability(ctx)`, a predicate the engine calls before
|
|
56
|
+
`runInPage` to decide whether the rule is in scope for this run at all. Fourteen rules
|
|
57
|
+
use it today (see §11.2). Export it alongside the other three when you need it:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
module.exports = { id, meta, runInPage, applicability };
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Nothing else. `npm run validate:rules` enforces exactly this set, and rejects a fifth
|
|
64
|
+
export.
|
|
56
65
|
|
|
57
66
|
---
|
|
58
67
|
|
|
@@ -78,7 +87,7 @@ Examples observed:
|
|
|
78
87
|
|
|
79
88
|
## 4) Meta Contract (all keys used by current rules)
|
|
80
89
|
|
|
81
|
-
Every rule defines a `meta` object.
|
|
90
|
+
Every rule defines a `meta` object. Across the shipped ruleset, the union of meta keys is:
|
|
82
91
|
|
|
83
92
|
### 4.1 Required top-level keys
|
|
84
93
|
|
|
@@ -177,8 +186,7 @@ occurrences.push(helpers.reportOccurrence(el, { summary: '…', hint: '…' }));
|
|
|
177
186
|
```
|
|
178
187
|
|
|
179
188
|
It attaches the element for the engine to finalize, which is how `selector`,
|
|
180
|
-
`html` and `structuralPath` get filled in centrally instead of in each
|
|
181
|
-
124 rules.
|
|
189
|
+
`html` and `structuralPath` get filled in centrally instead of in each rule.
|
|
182
190
|
|
|
183
191
|
**This is a performance contract, not just a convenience.** Every occurrence
|
|
184
192
|
gets a `structuralPath`. Given the element, the engine computes it directly.
|
|
@@ -268,15 +276,19 @@ const nodes = helpers.queryAllSmart
|
|
|
268
276
|
: helpers.queryAll('img');
|
|
269
277
|
```
|
|
270
278
|
|
|
271
|
-
Shadow traversal is
|
|
279
|
+
Shadow traversal is on by default. It is the caller who opts out:
|
|
272
280
|
```js
|
|
273
|
-
engineOptions: { includeShadowDom:
|
|
281
|
+
engineOptions: { includeShadowDom: false } // light DOM only
|
|
274
282
|
```
|
|
283
|
+
So write the rule assuming open shadow roots are in scope; `queryAllSmart` honours the
|
|
284
|
+
caller's choice for you. Closed roots are unreachable either way.
|
|
275
285
|
|
|
276
286
|
### 6.2 Reporting note for Shadow DOM
|
|
277
287
|
|
|
278
|
-
Selectors do not pierce shadow boundaries, so a `selector` may not uniquely locate
|
|
279
|
-
|
|
288
|
+
Selectors do not pierce shadow boundaries, so a `selector` may not uniquely locate a node
|
|
289
|
+
inside a shadow root — which is why `html` matters as the "which element" signal there.
|
|
290
|
+
You get both for free by reporting the element through `helpers.reportOccurrence` (§4.3);
|
|
291
|
+
there is nothing extra to do for shadow DOM specifically.
|
|
280
292
|
|
|
281
293
|
---
|
|
282
294
|
|
|
@@ -290,7 +302,8 @@ data: {
|
|
|
290
302
|
}
|
|
291
303
|
```
|
|
292
304
|
|
|
293
|
-
|
|
305
|
+
Pass the `eligInfo` you already computed for the element; the fallback object above is
|
|
306
|
+
for the case where a rule has none to give.
|
|
294
307
|
|
|
295
308
|
---
|
|
296
309
|
|
|
@@ -319,34 +332,48 @@ Manual:
|
|
|
319
332
|
|
|
320
333
|
---
|
|
321
334
|
|
|
322
|
-
## 9) Occurrence object shape
|
|
335
|
+
## 9) Occurrence object shape
|
|
323
336
|
|
|
324
|
-
|
|
337
|
+
Report the element and let the engine finish the object (§4.3):
|
|
325
338
|
|
|
326
339
|
```js
|
|
327
|
-
occurrences.push(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
340
|
+
occurrences.push(
|
|
341
|
+
helpers.reportOccurrence(el, {
|
|
342
|
+
summary: '…',
|
|
343
|
+
hint: '…',
|
|
344
|
+
i18n: { summaryKey: '…', hintKey: '…', params: { element: 'img' } },
|
|
345
|
+
data: { visibilityFilter: eligInfo || { targetSet: 'acc', accEligible: null, reasons: [] } }
|
|
346
|
+
})
|
|
347
|
+
);
|
|
335
348
|
```
|
|
336
349
|
|
|
337
|
-
|
|
338
|
-
- `selector` (or sometimes `selectorStr`)
|
|
339
|
-
- `html`
|
|
350
|
+
What a rule supplies:
|
|
340
351
|
- `summary`
|
|
341
352
|
- `hint`
|
|
342
353
|
- `i18n` (`summaryKey`, `hintKey`, `params`)
|
|
343
354
|
- `data` (includes `visibilityFilter`)
|
|
344
355
|
|
|
356
|
+
What the engine fills in from the reported element:
|
|
357
|
+
- `selector`
|
|
358
|
+
- `html`
|
|
359
|
+
- `structuralPath`
|
|
360
|
+
|
|
361
|
+
Setting `selector`/`html` yourself still works and still wins — a handful of rules whose
|
|
362
|
+
finding is not a single element (the contrast rules report text runs) do exactly that. It
|
|
363
|
+
is the exception, not the pattern to copy.
|
|
364
|
+
|
|
345
365
|
---
|
|
346
366
|
|
|
347
367
|
## 10) Structured doc comment block
|
|
348
368
|
|
|
349
|
-
Keep the structured header comment (`@
|
|
369
|
+
Keep the structured header comment (`@check`, `@atomic`, `@summary`, `@standard`, `@sc`,
|
|
370
|
+
`@applicability`, `@expectation`). The id goes on `@check` — `@rule` is not a tag this
|
|
371
|
+
repo uses. `docs/RULE_TEMPLATE.js` has the full block to copy.
|
|
372
|
+
|
|
373
|
+
`@applicability` and `@expectation` are consumer-facing: `scripts/generate-rule-catalog.js`
|
|
374
|
+
reads them straight from the source and publishes them per rule in
|
|
375
|
+
[`RULE_CATALOG.md`](./RULE_CATALOG.md#rule-reference). Write them for someone deciding
|
|
376
|
+
whether a result applies to their page, and rerun `npm run docs:rule-catalog` after editing them.
|
|
350
377
|
|
|
351
378
|
---
|
|
352
379
|
|
|
@@ -452,8 +479,9 @@ After adding or changing any fixture, regenerate the index:
|
|
|
452
479
|
npm run fixtures:index
|
|
453
480
|
```
|
|
454
481
|
|
|
455
|
-
This writes `tests/fixtures/INDEX.md` (human-readable)
|
|
482
|
+
This writes `tests/fixtures/INDEX.md` (human-readable), `tests/fixtures/index.json`
|
|
456
483
|
(machine-readable — every rule, its fixture path, and parsed pass/fail/cantTell case
|
|
457
|
-
counts, for external tooling to enumerate and load fixtures directly)
|
|
484
|
+
counts, for external tooling to enumerate and load fixtures directly) and
|
|
485
|
+
`tests/fixtures/index.html` (the same listing as a browsable page). Commit all three
|
|
458
486
|
alongside the fixture and test changes. A rule shipped without its fixture is treated
|
|
459
487
|
the same as a rule shipped without tests — not done.
|