44 * Native-interactive HTML element classification, shared across rules that need to
55 * ask "does this HTML tag natively expose interactive UI to keyboard / AT users?".
66 *
7- * The set is hand-curated rather than derived from a single authority because
8- * aria-query, axobject-query, HTML-AAM, WAI-ARIA, and browser reality disagree on
9- * several rows. Decision rationale is documented per-tag:
7+ * Hand-curated rather than derived directly from axobject-query because
8+ * axobject-query disagrees with browser reality on several rows (notably
9+ * audio/video where axobject-query marks them unconditionally widget, but
10+ * browsers only render keyboard UI when `controls` is set). Decision
11+ * rationale is documented per-tag:
1012 *
11- * | Element | Behavior | Rationale |
12- * |-------------------------------------------------|----------------------|-----------|
13- * | button, select, textarea, iframe, embed, | Interactive | aria-query/axobject-query widget + universally-accepted |
14- * | summary, details | | |
15- * | input (except type=hidden) | Interactive | Same as above, minus hidden |
16- * | option, datalist | Interactive | aria-query roles option/listbox; axobject widget; HTML-AAM |
17- * | a[href], area[href] | Interactive (cond.) | HTML-AAM: anchor interactivity requires href |
18- * | audio[controls], video[controls] | Interactive | Browsers only render focusable UI with `controls` |
19- * | audio, video (no controls) | NOT interactive | No keyboard semantics without controls; browsers agree |
20- * | object | Interactive | axobject-query EmbeddedObjectRole |
21- * | canvas | Interactive | axobject-query CanvasRole widget; bias toward no-FP |
22- * | input[type=hidden] | NOT interactive | HTML spec: no UI, no focus, no AT exposure |
23- * | menuitem | NOT interactive | Deprecated; no longer rendered in Chrome/Edge/Safari/FF |
24- * | label | NOT interactive | axobject-query LabelRole is structure, not widget |
13+ * | Element | Behavior | Rationale |
14+ * |------------------------------------------|----------------------|-----------------------------------------------------------------------------------------------|
15+ * | button, select, textarea, embed, summary | Interactive | axobject-query widget; universally accepted. |
16+ * | iframe | Interactive | axobject-query types it `window` (not widget), but iframe IS focusable and delegates focus. |
17+ * | details | Interactive | axobject-query types it `structure`, but <details> is a keyboard-activatable disclosure. |
18+ * | input (except type=hidden) | Interactive | axobject-query widget for every type except `hidden` (which has no entry). |
19+ * | option, datalist | Interactive | axobject-query widget (ListBoxOptionRole / ListBoxRole). |
20+ * | canvas | Interactive | axobject-query widget (CanvasRole); convention + no-false-positive bias. |
21+ * | a[href], area[href] | Interactive (cond.) | HTML-AAM: anchor interactivity requires href. (area has no axobject-query entry — pragmatic.) |
22+ * | audio[controls], video[controls] | Interactive | Stricter than axobject-query (which marks bare audio/video as widget). Browsers only render |
23+ * | | | keyboard-operable UI when `controls` is present. |
24+ * | audio, video (no controls) | NOT interactive | Matches browser behavior; axobject-query would disagree here. |
25+ * | input[type=hidden] | NOT interactive | HTML spec: no UI, no focus, no AT exposure. axobject-query has no entry. |
26+ * | menuitem | NOT interactive | Deprecated HTML; removed from all major browsers despite axobject-query still listing it. |
27+ * | label | NOT interactive | axobject-query LabelRole is structure, not widget. |
28+ * | object | NOT interactive | axobject-query has no entry for <object>; no authoritative source backs "interactive by default." |
2529 */
2630
2731// Unconditionally-interactive HTML tags (no attribute dependencies).
@@ -35,7 +39,6 @@ const UNCONDITIONAL_INTERACTIVE_TAGS = new Set([
3539 'details' ,
3640 'option' ,
3741 'datalist' ,
38- 'object' ,
3942 'canvas' ,
4043] ) ;
4144
0 commit comments