Skip to content

Commit 372b51e

Browse files
committed
fix(template-no-noninteractive-tabindex): walk role tokens for first recognised role per ARIA §4.1 (Copilot review)
1 parent ce6e22f commit 372b51e

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

lib/rules/template-no-noninteractive-tabindex.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,23 @@ module.exports = {
174174
// tabindex="0" when the panel's content isn't itself focusable, so
175175
// keyboard users can page through panels. jsx-a11y's recommended
176176
// default exempts `tabpanel` for the same reason.
177+
//
178+
// Walk tokens for the first RECOGNISED role (ARIA 1.2 §4.1 role-
179+
// fallback), matching the `roleStatus()` helper above. This is
180+
// consistent with the rule's own role-walk logic and matches browser
181+
// behavior: `role="xxyxyz tabpanel"` resolves to `tabpanel` because
182+
// UAs skip unknown tokens. LLM-generated templates sometimes emit
183+
// such sequences; honoring spec fallback prevents false positives.
177184
const roleAttr = findAttr(node, 'role');
178185
if (roleAttr?.value?.type === 'GlimmerTextNode') {
179-
const firstToken = roleAttr.value.chars.trim().toLowerCase().split(/\s+/u)[0];
180-
if (allowedRoles.has(firstToken)) {
181-
return;
186+
const tokens = roleAttr.value.chars.trim().toLowerCase().split(/\s+/u);
187+
for (const token of tokens) {
188+
if (roles.has(token)) {
189+
if (allowedRoles.has(token)) {
190+
return;
191+
}
192+
break; // first recognised role wins; further tokens are fallbacks
193+
}
182194
}
183195
}
184196

tests/lib/rules/template-no-noninteractive-tabindex.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ ruleTester.run('template-no-noninteractive-tabindex', rule, {
3737
'<template><div role="tabpanel" tabindex="0"></div></template>',
3838
'<template><section role="tabpanel" tabindex="0" aria-labelledby="tab-1">Content</section></template>',
3939

40+
// Role-fallback list with unknown leading token — UAs walk to the first
41+
// recognised token (`tabpanel`) per WAI-ARIA §4.1. The allowlist match
42+
// should honor the same fallback. (LLM guardrail: models sometimes
43+
// emit speculative-fallback sequences like this.)
44+
'<template><div role="xxyxyz tabpanel" tabindex="0"></div></template>',
45+
4046
// Config: allow additional non-interactive roles.
4147
{
4248
code: '<template><div role="region" tabindex="0" aria-label="Scroll area"></div></template>',

0 commit comments

Comments
 (0)