Aller au contenu principal

Chromium SVG Accessibility

How SVG elements appear in Blink's accessibility (AX) tree. SVG is a first-class citizen in the AX tree — not an opaque blob — and the role mapping is implemented in the same AXNodeObject machinery HTML uses, with SVG-aware branches.

No dedicated AX class hierarchy

Unlike older versions of Blink (and unlike WebKit), there are no AXSVGRoot / AXSVGShape etc. classes. SVG elements use AXNodeObject with conditional logic:

// third_party/blink/renderer/modules/accessibility/ax_node_object.cc
if (node && node->IsSVGElement()) {
if (GetLayoutObject()->IsSVGImage()) {
return ax::mojom::blink::Role::kImage;
}
if (IsA<SVGSVGElement>(node)) {
return GetLayoutObject()->IsSVGRoot() ? ax::mojom::blink::Role::kSvgRoot
: ax::mojom::blink::Role::kGroup;
}
if (IsA<SVGSymbolElement>(node)) {
if (GetLayoutObject()->IsSVGViewportContainer()) {
return ax::mojom::blink::Role::kGroup;
}
}
if (GetLayoutObject()->IsSVGShape()) {
return ax::mojom::blink::Role::kGraphicsSymbol;
}
if (GetLayoutObject()->IsSVGForeignObject()) {
return ax::mojom::blink::Role::kGroup;
}
// ...
}

Role mapping is done at NativeRole() / DetermineRoleValue() time, based on the element type and the layout object's IsSVG*() predicates.

Default role mapping

SVG elementDefault roleNotes
<svg> (root)kSvgRootMaps to graphics-document per SVG-AAM
<svg> (nested)kGroupAuthor opt-in to kRegion via role="group" etc.
<symbol>kGroup (when LayoutSVGViewportContainer)Otherwise hidden
<g>(generic — usually filtered)Inherits role from children unless author sets one
<path>, <circle>, <rect>, <ellipse>, <line>, <polygon>, <polyline>kGraphicsSymbolPer SVG-AAM
<image>kImage
<foreignObject>kGroupChildren fall back to HTML AX mapping
<text>, <tspan>(text content)Exposed via accessible name
<title>(consumed)Becomes the parent's accessible name
<desc>(consumed)Becomes the parent's accessible description
Resource elements (<defs>, <clipPath>, <mask>, <filter>, <marker>, <linearGradient>, ...)hiddenNot in the AX tree

The kSvgRoot mapping for root <svg> is technically a Chromium extension beyond the SVG-AAM spec (which currently maps all <svg> to graphics-document); see the comment in ax_node_object.cc referencing w3c/svg-aam#18.

Accessible name and description

<title> and <desc> children are picked up as the accessible name and description for the parent SVG element:

<svg role="img" aria-labelledby="title">
<title id="title">A red circle</title>
<desc>Used as a status indicator.</desc>
<circle cx="50" cy="50" r="40" fill="red"/>
</svg>

This is the idiomatic pattern for SVG content that's semantically a single image. Without role="img", screen readers walk the SVG subtree as a graphics document and announce children individually — usually not what the author wants for an icon.

Author opt-ins

Author markupEffect
<svg role="img" aria-label="...">Treated as a single image with the given name
<svg role="img" aria-labelledby="t" aria-describedby="d">Same, with linked title/desc
<svg role="presentation"> or <svg role="none">Removed from AX tree (decorative)
<svg aria-hidden="true">Removed from AX tree
<g role="group" aria-label="...">Group is exposed with name
<path role="graphics-symbol" aria-label="...">Per-shape labeling

Per the WAI-ARIA Graphics Module, additional roles are available: graphics-document, graphics-object, graphics-symbol. Blink honors these and maps them through ax::mojom::blink::Role::kGraphics* values.

Hit-test ↔ AX integration

When an assistive technology issues a "what's at this point" query, the SVG hit-test path (see hit-testing.md) returns an SVGElement, and the AX tree's AXObjectFromAXID / AccessibleNodeForId machinery resolves it to its AXNodeObject. pointer-events: none on an SVG element does not remove it from the AX tree by itself — accessibility is independent of pointer hit-testing.

Special cases

<use> clones

A <use> element exposes the cloned subtree's accessible content (so an icon defined once and instantiated 50 times is announced consistently). Events retarget through <use>, but accessibility traversal sees the clones; aria-label on the <use> itself can override the clone's name.

SVG-as-image (SVGImage)

When SVG is loaded as <img src="x.svg"> or background-image: url(x.svg), the isolated SVGImage document is not part of the host's AX tree. The host's <img> element appears in the AX tree as kImage, with the alt attribute as its accessible name. Internal SVG structure is opaque from the host's perspective.

Inline SVG inside <foreignObject>

A <foreignObject> containing HTML re-enters HTML's AX path for its descendants. The boundary is automatic — the AX tree just continues walking the DOM.

Files

FileRole
modules/accessibility/ax_node_object.ccSVG role mapping, accessible-name extraction (no separate AXSVG class)
modules/accessibility/ax_object.ccCommon predicates (IsA<SVGElement>-aware)
modules/accessibility/ax_object_cache.ccElement → AXObject mapping
modules/accessibility/ax_enums.hRole enum (incl. kSvgRoot, kGraphicsSymbol, ...)

See also