summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/eight-flowers-remain.md5
-rw-r--r--packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts44
2 files changed, 32 insertions, 17 deletions
diff --git a/.changeset/eight-flowers-remain.md b/.changeset/eight-flowers-remain.md
new file mode 100644
index 000000000..9ef571e10
--- /dev/null
+++ b/.changeset/eight-flowers-remain.md
@@ -0,0 +1,5 @@
+---
+"astro": patch
+---
+
+Fixes false positives in the dev overlay audit when multiple `role` values exist.
diff --git a/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts b/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts
index 2b4943908..48240317e 100644
--- a/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts
+++ b/packages/astro/src/runtime/client/dev-toolbar/apps/audit/a11y.ts
@@ -29,6 +29,8 @@ import { aria, roles } from 'aria-query';
import { AXObjectRoles, elementAXObjects } from 'axobject-query';
import type { AuditRuleWithSelector } from './index.js';
+const WHITESPACE_REGEX = /\s+/;
+
const a11y_required_attributes = {
a: ['href'],
area: ['alt', 'aria-label', 'aria-labelledby'],
@@ -496,13 +498,17 @@ export const a11y: AuditRuleWithSelector[] = [
if (is_semantic_role_element(role, element.localName, getAttributeObject(element))) {
return;
}
- const { requiredProps } = roles.get(role)!;
- const required_role_props = Object.keys(requiredProps);
- const missingProps = required_role_props.filter((prop) => !element.hasAttribute(prop));
- if (missingProps.length > 0) {
- (element as any).__astro_role = role;
- (element as any).__astro_missing_attributes = missingProps;
- return true;
+
+ const elementRoles = role.split(WHITESPACE_REGEX) as ARIARoleDefinitionKey[];
+ for(const elementRole of elementRoles) {
+ const { requiredProps } = roles.get(elementRole)!;
+ const required_role_props = Object.keys(requiredProps);
+ const missingProps = required_role_props.filter((prop) => !element.hasAttribute(prop));
+ if (missingProps.length > 0) {
+ (element as any).__astro_role = elementRole;
+ (element as any).__astro_missing_attributes = missingProps;
+ return true;
+ }
}
},
},
@@ -522,16 +528,20 @@ export const a11y: AuditRuleWithSelector[] = [
match(element) {
const role = getRole(element);
if (!role) return false;
- const { props } = roles.get(role)!;
- const attributes = getAttributeObject(element);
- const unsupportedAttributes = aria.keys().filter((attribute) => !(attribute in props));
- const invalidAttributes: string[] = Object.keys(attributes).filter(
- (key) => key.startsWith('aria-') && unsupportedAttributes.includes(key as any)
- );
- if (invalidAttributes.length > 0) {
- (element as any).__astro_role = role;
- (element as any).__astro_unsupported_attributes = invalidAttributes;
- return true;
+
+ const elementRoles = role.split(WHITESPACE_REGEX) as ARIARoleDefinitionKey[];
+ for(const elementRole of elementRoles) {
+ const { props } = roles.get(elementRole)!;
+ const attributes = getAttributeObject(element);
+ const unsupportedAttributes = aria.keys().filter((attribute) => !(attribute in props));
+ const invalidAttributes: string[] = Object.keys(attributes).filter(
+ (key) => key.startsWith('aria-') && unsupportedAttributes.includes(key as any)
+ );
+ if (invalidAttributes.length > 0) {
+ (element as any).__astro_role = elementRole;
+ (element as any).__astro_unsupported_attributes = invalidAttributes;
+ return true;
+ }
}
},
},