Skip to content

Commit 6bd197f

Browse files
committed
fix(a11y): add aria-describedby to svg and make star icons decorative
- Icon.astro: add aria-describedby to normalizedProps when desc is provided and icon is meaningful, binding <svg> to its <desc id> (WCAG 1.1.1 / 4.1.2) - map.astro: remove per-star title props so each icon is decorative; the <figure aria-label> and <figcaption> carry the collective meaning and screen readers no longer announce each star separately
1 parent dd99139 commit 6bd197f

2 files changed

Lines changed: 11 additions & 8 deletions

File tree

demo/src/pages/map.astro

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ import Base from "../layouts/base.astro";
99
<!--
1010
Star rating example: wrap the group in a <figure> with a <figcaption>
1111
so screen readers understand the collective meaning (WCAG 1.1.1 / 1.3.1).
12-
Each Icon is decorative within the group — aria-hidden is set automatically.
12+
Each Icon is decorative within the group — no title prop, so aria-hidden
13+
is applied automatically by Icon.astro.
1314
-->
1415
<figure aria-label="Rating: 4 out of 5 stars">
15-
{Array.from({ length: 5 }, (_, i) => (
16-
<Icon
17-
name="ic:outline-star"
18-
title={i < 4 ? `Star ${i + 1}` : `Empty star ${i + 1}`}
19-
/>
16+
{Array.from({ length: 5 }, () => (
17+
<Icon name="ic:outline-star" />
2018
))}
2119
<figcaption class="visually-hidden">4 out of 5 stars</figcaption>
2220
</figure>

packages/core/components/Icon.astro

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,17 @@ const renderData = iconToSVG(iconData);
113113
// - If a title is provided the icon is meaningful → role="img"
114114
// - Otherwise it is decorative → aria-hidden="true"
115115
const isDecorative = !title;
116+
const descId = desc ? `${id}:desc` : undefined;
116117
const normalizedProps = {
117118
...(renderData.attributes as Partial<IconifyIconBuildResult["attributes"]>),
118119
// Prevent SVGs from being focusable in IE/Edge and some browsers
119120
focusable: "false" as const,
120-
// Decorative icons must be hidden from the accessibility tree (WCAG 1.1.1)
121-
...(isDecorative ? { "aria-hidden": "true" as const } : { role: "img" as const }),
121+
// Decorative icons must be hidden from the accessibility tree (WCAG 1.1.1).
122+
// Meaningful icons get role="img" and, when a desc is provided, aria-describedby
123+
// pointing to the stable <desc id> so assistive technologies read the description.
124+
...(isDecorative
125+
? { "aria-hidden": "true" as const }
126+
: { role: "img" as const, ...(descId ? { "aria-describedby": descId } : {}) }),
122127
...props,
123128
};
124129
// biome-ignore lint/correctness/noUnusedVariables: used in the Astro template below, outside Biome's JS scope

0 commit comments

Comments
 (0)