# Accessibility Features Built Into SVG Diagrams in Diagram-Design: WCAG 2.1 AA Compliance

> Discover how Diagram-Design's SVG diagrams meet WCAG 2.1 AA standards with built-in accessibility features like semantic roles, descriptive text, and ARIA labeling. Improve your accessible diagrams.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: deep-dive
- Published: 2026-09-13

---

**The diagram-design repository enforces WCAG 2.1 AA accessibility standards through automated linting that requires every exported SVG to include semantic roles, descriptive text elements, and proper ARIA labeling.**

The cathrynlavery/diagram-design repository generates diagrams with built-in accessibility features that ensure compliance with web standards. Every SVG produced by the `export-diagram` command undergoes validation by the `lint_accessible_svgs` function in [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py), which enforces a strict contract for assistive technology compatibility. These accessibility features are not optional add-ons but mandatory requirements built directly into the export pipeline.

## Semantic Role Declaration

Every generated SVG must declare its semantic purpose using the **`role="img"`** attribute. This declaration signals to assistive technologies that the SVG represents meaningful content rather than decorative markup.

In [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py) at lines 49-51, the validator explicitly checks that `svg.attrs.get("role") != "img"` triggers an error if the role is missing or incorrect. This enforcement ensures screen readers interpret the diagram as an image rather than generic XML content.

## Accessible Name and Description Elements

The repository requires two specific child elements within every SVG to provide accessible text alternatives.

### The Title Element

The **`<title>`** element must be the first child of the `<svg>` root and contain non-empty text. According to lines 96-104 in [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py), the linter verifies presence, DOM order, and content emptiness. This element serves as the accessible name for the diagram, typically mirroring the diagram's heading or purpose.

### The Description Element

The **`<desc>`** element provides extended explanations for complex diagrams. Lines 108-112 enforce that this element exists and contains descriptive text, allowing screen reader users to understand visual relationships and data flows that sighted users perceive graphically.

## Programmatic Association via ARIA

The linter enforces proper ARIA labeling through the **`aria-labelledby`** attribute. At lines 52-55 and 78-84 in [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py), the code validates that `aria-labelledby` references both the `<title>` and `<desc>` element IDs. This creates a programmatic link between the accessible name (title) and accessible description (desc) for screen reader announcements.

## Strict ID Naming Conventions

To prevent ID collisions and ensure deterministic references, the repository implements a slug-based naming scheme.

### Prefixed Identifiers

Lines 122-134 of the linter mandate that `<title>` and `<desc>` IDs must use the format `<slug>-title` and `<slug>-desc`, where `<slug>` represents the diagram's identifier. This prevents namespace collisions when multiple SVGs appear on the same page.

### Duplicate Detection

The validator at lines 38-44 checks for duplicate IDs across the document, while lines 155-161 explicitly reject bare IDs like `id="title"` or `id="desc"` that lack the required slug prefix. These checks ensure that `aria-labelledby` references resolve to unique, predictable elements.

## Decorative SVG Handling

For purely decorative graphics that should be ignored by assistive technologies, the repository supports **`aria-hidden="true"`**. Lines 44-48 in [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py) implement an early-exit check that suppresses all other accessibility validations when this attribute is present. This allows designers to include ornamental background patterns or spacer graphics without triggering lint errors.

## Template Generation Support

When generating template SVGs during the design phase, [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py) accepts placeholder IDs like `[diagram-slug]-title` through the `allow_template_placeholders` parameter (lines 66-70). This flexibility supports iterative development while maintaining the eventual accessibility contract for production exports.

## Implementation Examples

### Valid Accessible SVG

The following snippet satisfies all accessibility requirements for a diagram with the slug `example-flowchart`:

```svg
<svg role="img"
     aria-labelledby="example-flowchart-title example-flowchart-desc"
     width="800" height="600"
     xmlns="http://www.w3.org/2000/svg">
  <title id="example-flowchart-title">Flowchart of Process</title>
  <desc id="example-flowchart-desc">A step-by-step flowchart showing the stages of the process.</desc>
  <!-- diagram graphics go here -->
</svg>

```

### Decorative SVG Exemption

For non-semantic decorative elements, use the exemption pattern:

```svg
<svg aria-hidden="true" width="800" height="600" xmlns="http://www.w3.org/2000/svg">
  <!-- purely decorative graphics -->
</svg>

```

## Testing and Validation

The accessibility rules are exercised by [`scripts/test-lint-a11y.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/test-lint-a11y.py), which confirms that the linter correctly accepts compliant SVGs while rejecting violations. When running the `export-diagram` command, any SVG failing these rules generates a lint error, preventing inaccessible diagrams from entering production workflows.

## Summary

- The **`lint_accessible_svgs`** function in [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py) enforces mandatory accessibility for all exported SVGs
- Every SVG must include **`role="img"`**, a `<title>` element, a `<desc>` element, and **`aria-labelledby`** referencing both
- IDs must follow the **`<slug>-title`** and **`<slug>-desc`** naming convention to prevent collisions
- **`aria-hidden="true"`** exempts decorative SVGs from accessibility requirements
- The test suite in [`scripts/test-lint-a11y.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/test-lint-a11y.py) validates compliance during the export process

## Frequently Asked Questions

### How does diagram-design validate SVG accessibility?

The repository uses the `lint_accessible_svgs` function located in [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py) to programmatically inspect every exported SVG. This linter checks for required attributes, element ordering, and ID naming conventions, failing the build if any WCAG 2.1 AA requirements are missing.

### What happens if an SVG fails the accessibility checks?

Any SVG that violates the accessibility contract triggers a lint error during the `export-diagram` command execution. The specific violation—such as missing title elements or incorrect ID formats—appears in the console output, preventing the inaccessible diagram from being committed or deployed.

### Can I use diagram-design for decorative SVGs that don't need accessibility?

Yes. By adding **`aria-hidden="true"`** to the SVG root element, you signal that the graphic is purely decorative. When detected at lines 44-48 of [`scripts/lint-skin.py`](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/lint-skin.py), this attribute suppresses all other accessibility checks, allowing the export to proceed without title or description elements.

### How are ID collisions prevented in generated SVGs?

The linter enforces a slug-based prefix system at lines 122-134, requiring all accessible name IDs to use the format `<diagram-slug>-title` and `<diagram-slug>-desc`. Additionally, lines 38-44 check for duplicate IDs within the document, ensuring unique identification even when multiple diagrams appear on the same HTML page.