diff --git a/lib/rules/template-no-invalid-aria-attributes.js b/lib/rules/template-no-invalid-aria-attributes.js
index 4a5c7a2a48..6eead8bf76 100644
--- a/lib/rules/template-no-invalid-aria-attributes.js
+++ b/lib/rules/template-no-invalid-aria-attributes.js
@@ -11,16 +11,22 @@ function isNumeric(value) {
return !Number.isNaN(Number(value));
}
-function isValidAriaValue(attrName, value) {
- const attrDef = aria.get(attrName);
- if (!attrDef) {
- return true;
- }
+// In aria-query 5.3.2, `allowundefined: true` is set only on the four
+// boolean-like ARIA state attributes — `aria-expanded`, `aria-hidden`,
+// `aria-grabbed`, `aria-selected` — whose WAI-ARIA 1.2 value tables list
+// the literal string `"undefined"` as a spec-valid value meaning "state
+// is not applicable" (e.g. https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+// The flag is nominally type-agnostic, but in practice this function only
+// green-lights `"undefined"` for that boolean-like subset; no non-boolean
+// ARIA attribute in aria-query currently sets `allowundefined`.
+function allowsUndefinedLiteral(attrDef, value) {
+ return value === 'undefined' && Boolean(attrDef.allowundefined);
+}
- if (value === 'undefined') {
- return Boolean(attrDef.allowundefined);
+function validateByType(attrDef, value) {
+ if (allowsUndefinedLiteral(attrDef, value)) {
+ return true;
}
-
switch (attrDef.type) {
case 'boolean': {
return isBoolean(value);
@@ -45,7 +51,9 @@ function isValidAriaValue(attrName, value) {
return isNumeric(value) && !isBoolean(value);
}
case 'token': {
- // aria-query stores boolean values as actual booleans, convert for comparison
+ // aria-query stores boolean values as actual booleans; stringify for comparison.
+ // The string literal 'undefined' that appears in some values arrays (e.g.
+ // aria-orientation) passes through this check naturally — no special-casing.
const permittedValues = attrDef.values.map((v) =>
typeof v === 'boolean' ? v.toString() : v
);
@@ -60,6 +68,14 @@ function isValidAriaValue(attrName, value) {
}
}
+function isValidAriaValue(attrName, value) {
+ const attrDef = aria.get(attrName);
+ if (!attrDef) {
+ return true;
+ }
+ return validateByType(attrDef, value);
+}
+
function getExpectedTypeDescription(attrName) {
const attrDef = aria.get(attrName);
if (!attrDef) {
diff --git a/tests/lib/rules/template-no-invalid-aria-attributes.js b/tests/lib/rules/template-no-invalid-aria-attributes.js
index 7f8e10b8e6..163792bf3e 100644
--- a/tests/lib/rules/template-no-invalid-aria-attributes.js
+++ b/tests/lib/rules/template-no-invalid-aria-attributes.js
@@ -31,7 +31,24 @@ ruleTester.run('template-no-invalid-aria-attributes', rule, {
'