Skip to content

Accessibility in Design Systems — Component Libraries & Figma Guide

DodaTech Updated 2026-06-24 10 min read

Accessibility in design systems means baking Accessibility into every component from the start — through accessible design tokens, ARIA-annotated Figma components, developer documentation with Accessibility expectations, and automated testing that catches violations before they reach production.

What You'll Learn

By the end of this guide, you'll understand how to design accessible components in Figma using plugins and annotations, how to create design tokens for color contrast and typography, how to document Accessibility expectations for each component, how to integrate automated Accessibility Testing into your design system CI/CD pipeline, and how to ensure smooth design-to-development Accessibility handoff.

Why Design System Accessibility Matters

A design system is the single source of truth for your product's UI. If the design system is accessible, every product built on it inherits that Accessibility. If the design system has Accessibility gaps, every product duplicates those gaps. Fixing Accessibility at the design system level is the highest-leverage Accessibility investment you can make. At DodaTech, the Doda Browser design system includes Accessibility annotations in every Figma component, and Durga Antivirus Pro uses design tokens that enforce WCAG AA contrast ratios across all themes.

Accessibility in Design Systems Flow

flowchart TD
  A[Design System] --> B[Design Tokens]
  A --> C[Component Library]
  A --> D[Documentation]
  A --> E[Testing Pipeline]
  B --> F[Color contrast tokens]
  B --> G[Typography tokens]
  B --> H[Spacing tokens]
  C --> I[ARIA annotations]
  C --> J[Focus states]
  C --> K[Keyboard interactions]
  D --> L[Accessibility expectations]
  D --> M[Usage guidelines]
  E --> N[Automated checks]
  E --> O[Visual regression]
  E --> P[Storybook a11y addon]

{{< callout type="info" icon="sparkles" >}} Prerequisites: Familiarity with design systems, component libraries, and WCAG principles. Figma experience helps for the design section. {{< /callout >}}

Figma Accessibility Practices

Accessible Design Tokens

Design tokens are the atomic values — colors, typography, spacing — that drive your entire system. They must enforce Accessibility constraints:

{
  "color": {
    "brand-primary": { "value": "#005fcc", "contrast": { "on-white": "4.6:1", "on-black": "9.8:1" } },
    "text-primary": { "value": "#1a1a1a", "contrast": { "on-white": "14.5:1" } },
    "text-secondary": { "value": "#5a5a5a", "contrast": { "on-white": "5.2:1" } },
    "text-disabled": { "value": "#999999", "contrast": { "on-white": "2.8:1", "note": "FAILS WCAG AA — only for disabled, non-interactive text" } },
    "surface-danger": { "value": "#d32f2f", "contrast": { "on-white": "4.2:1", "use": "Only with white text (contrast 4.6:1)" } }
  },
  "typography": {
    "body": { "size": "16px", "line-height": "1.5", "weight": "400" },
    "body-small": { "size": "14px", "line-height": "1.5", "weight": "400" },
    "heading-1": { "size": "32px", "line-height": "1.2", "weight": "700" }
  },
  "spacing": {
    "touch-target": { "min": "44px", "note": "WCAG 2.5.5 Target Size" }
  }
}

Why this works: Each color token includes its contrast ratio against common backgrounds. Tokens that fail WCAG AA are flagged. Typography tokens enforce minimum sizes and line heights. Spacing tokens enforce minimum touch targets.

Figma Components with ARIA Annotations

Every component in Figma should include ARIA annotations in the description or a separate annotation layer:

## Button Component

### ARIA
- role: "button" (native, do not override)
- state: aria-pressed for toggle buttons
- label: Use aria-label when icon-only

### Keyboard
- Enter/Space: activates
- Tab: moves to button
- Focus: visible 2px blue outline

### Accessibility Notes
- Minimum touch target: 44x44px
- Contrast ratio: 4.5:1 minimum
- Text cannot be hidden on focus

Use Figma plugins like A11y Annotation Kit or Stark to add these annotations directly on the Canvas.

Focus State Design

Every interactive component needs a visible focus State. Design it in Figma:

/* Focus ring tokens */
:root {
  --focus-ring-color: #005fcc;
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
  --focus-ring-style: solid;
}

/* Apply to all interactive elements */
*:focus-visible {
  outline: var(--focus-ring-width) var(--focus-ring-style) var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}
<!-- Button component with focus state -->
<button class="btn btn-primary" type="button">
  Run Scan — Durga Antivirus Pro
</button>

<style>
.btn:focus-visible {
  outline: 2px solid #005fcc;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(0, 95, 204, 0.2);
}

/* High contrast focus for Windows High Contrast Mode */
@media (prefers-contrast: high) {
  .btn:focus-visible {
    outline: 3px solid Highlight;
  }
}
</style>

Component Documentation

Every component in the design system library must include Accessibility documentation:

---
name: Modal Dialog
status: accessible
wcag: 2.4.3, 4.1.2, 1.4.1
---

## Accessibility Requirements

### Must Have
- role="dialog" and aria-modal="true"
- Focus trap on open
- Escape key closes dialog
- Focus returns to trigger on close
- Accessible name via aria-labelledby

### Should Have
- aria-describedby for description
- Non-dismissible backdrop for critical alerts

### Must Not
- Open another modal on top
- Remove focus indicator
- Trap screen reader navigation (only Tab)

## Testing
- [ ] Keyboard: Tab through all elements
- [ ] Keyboard: Escape closes
- [ ] Focus: Restored to trigger on close
- [ ] Screen reader: Role and name announced
- [ ] Screen reader: Content announced correctly

Automated Testing Integration

Integrate Accessibility Testing into your design system's testing pipeline:

// Storybook-Accessibility.test.js
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/React';
import { axe, toHaveNoViolations } from 'Jest-axe';
import { Button, Modal, Tabs } from '../src/components';

expect.extend(toHaveNoViolations);

describe('Design System Accessibility', () => {
  it('Button has no violations', async () => {
    const { container } = render(<Button>Scan now</Button>);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });

  it('Icon button has label', async () => {
    const { container } = render(<Button icon="search" aria-label="Search" />);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });

  it('Modal traps focus and has correct ARIA', async () => {
    const { container } = render(
      <Modal open={true} onClose={() => {}} title="Confirm">
        <p>Are you sure?</p>
      </Modal>
    );
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });

  it('Tabs meet keyboard and ARIA requirements', async () => {
    const { container } = render(
      <Tabs>
        <Tab label="Settings">Content</Tab>
        <Tab label="Security">Content</Tab>
      </Tabs>
    );
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });
});

Expected output:

PASS  storybook-accessibility.test.js
  ✓ Button has no violations
  ✓ Icon button has label
  ✓ Modal traps focus and has correct ARIA
  ✓ Tabs meet keyboard and ARIA requirements

Color Palette Validation

Automate color contrast checking for your design token palette:

// contrast-validator.js
const colorPairs = [
  { fg: '#1a1a1a', bg: '#ffffff', expected: '4.5:1' },
  { fg: '#5a5a5a', bg: '#ffffff', expected: '4.5:1' },
  { fg: '#999999', bg: '#ffffff', expected: 'FAIL', note: 'Disabled text only' },
  { fg: '#ffffff', bg: '#005fcc', expected: '4.5:1' },
  { fg: '#ffffff', bg: '#d32f2f', expected: '4.5:1' },
];

function luminance(hex) {
  const [R, g, b] = hex.match(/[A-Fa-f0-9]{2}/g).map(C => {
    const v = parseInt(C, 16) / 255;
    return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
  });
  return 0.2126 * R + 0.7152 * g + 0.0722 * b;
}

function contrastRatio(fg, bg) {
  const l1 = luminance(fg);
  const l2 = luminance(bg);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return ((lighter + 0.05) / (darker + 0.05)).toFixed(2) + ':1';
}

colorPairs.forEach(pair => {
  const ratio = contrastRatio(pair.fg, pair.bg);
  const pass = parseFloat(ratio) >= 4.5;
  console.log(`${pair.fg} on ${pair.bg}: ${ratio} ${pass ? 'PASS' : 'FAIL'} (expected ${pair.expected})`);
});

Expected output:

#1a1a1a on #ffffff: 14.50:1 PASS (expected 4.5:1)
#5a5a5a on #ffffff: 5.20:1 PASS (expected 4.5:1)
#999999 on #ffffff: 2.80:1 FAIL (expected FAIL, note: Disabled text only)
#ffffff on #005fcc: 4.60:1 PASS (expected 4.5:1)
#ffffff on #d32f2f: 4.60:1 PASS (expected 4.5:1)

Common Mistakes

1. Designing Without Focus States

Designing components in Figma without showing the focus State leads to developers guessing. Every interactive component must have a designed focus State.

2. Skipping Color Token Validation

Defining a color palette without checking contrast ratios means designers pick colors that fail Accessibility. Validate every foreground-background pair.

3. Incomplete Component Documentation

A button component documented without Accessibility expectations (role, keyboard interactions, ARIA states) leads to inconsistent implementations.

4. Testing Only at the End

Waiting until the design system is complete to test Accessibility means reworking every component. Test each component as it is built.

5. No Automated Checks in CI/CD

Without automated axe-core or Pa11y checks in the design system CI, Accessibility regressions slip into production unnoticed.

6. Ignoring Windows High Contrast Mode

WHDCM is a key Accessibility feature for low-vision users. Test components with forced colors mode enabled.

7. No Reduced Motion Support

Components with animations (loading spinners, transitions) should respect prefers-reduced-motion: reduce and disable or slow animations.

Practice Questions

1. Why is fixing Accessibility at the design system level more effective than fixing it per product?

Every product built on an accessible design system inherits that Accessibility. Fixing per product duplicates effort and misses components that are shared across products.

2. What information should be included in a component's Accessibility documentation?

ARIA roles and states, keyboard interactions, focus behavior, contrast requirements, touch target minimums, and testing checklist.

3. How can Figma plugins help with Accessibility?

Plugins like Stark check color contrast, A11y Annotation Kit adds ARIA annotations, and Able simulates color blindness — all within the design tool before development begins.

4. What is the minimum touch target size in WCAG?

44x44 CSS pixels for pointer targets (WCAG 2.5.5 Target Size, Enhanced). 24x24 for WCAG 2.5.8 Pointer Target Spacing (AA).

5. Challenge: Audit a design system component library you use. Pick three components and check if their documentation includes: ARIA roles, keyboard interactions, focus states, contrast ratios, and testing instructions. Write documentation for any gaps you find.

Real-World Task

Create a Figma component for a DodaTech-branded button with:

  • Color token validation
  • Focus State (2px blue outline)
  • ARIA annotation (role, keyboard, aria-pressed for toggle variant)
  • Touch target min 44px
  • Disabled State with contrast note

FAQ

Should every component in the design system have an Accessibility section in its documentation? Yes. Every interactive component must document its ARIA roles, keyboard interactions, and Accessibility requirements. Even presentational components should note they are decorative.

How do I enforce Accessibility in Figma without manual review? Use design token validation scripts that check contrast ratios. Use Figma plugins like Stark for real-time checking. Add Accessibility annotations as part of the component definition.

Can I reuse the same component for different ARIA roles? Yes, but document which role applies in which context. For example, a <button> component is always a button, but a <div> styled as a card could be a button, link, or region depending on context.

How do I handle Accessibility in third-party components? Wrap third-party components in your design system's accessible wrapper. Test with axe-core and document any known Accessibility limitations.

Should I version Accessibility improvements? Yes. Accessibility improvements that change behavior (like adding focus trapping) should be documented as breaking changes in your design system changelog.

Try It Yourself

Create a design system Accessibility audit checklist:

# Design System Accessibility Audit

## Per Component
- [ ] ARIA role documented
- [ ] Keyboard interactions documented
- [ ] Focus State designed
- [ ] Contrast ratio validated (4.5:1 text, 3:1 graphics)
- [ ] Touch target >= 44px
- [ ] Screen reader behavior documented

## Per Token
- [ ] Color contrast validated
- [ ] Typography minimum sizes enforced
- [ ] Spacing includes touch targets

## Pipeline
- [ ] axe-core in Storybook
- [ ] Color contrast CI check
- [ ] Visual regression for Accessibility

What's Next

VPAT / ACR — Conformance Reports
ARIA — Complete Guide

Congratulations on completing this Design System Accessibility tutorial! Here is where to Go from here:

  • Practice daily — Add Accessibility annotations to one Figma component per day
  • Build a project — Create an accessible component library with documentation
  • Explore related topics — Learn about VPAT/ACR conformance reporting next
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro