function
<Component />
return
const
interface
{ }
color.primary
TS
() => {}
spacing.xl
dev
tokens
[ ]
code
<T>
type
async
font.sans
JS
TypeScript
export
[React, ...skills]
components
import
border.radius
props
default
?.
tokens
shadow.lg
await
&&
class
<Props>
accessibility
variables
transition
JSX
</>
theme
motion
( )
hooks
state
utils
wcag
responsive
grid
flex
scale
{ml}
Back to work
ACCESSIBILITY

a11y Companion

Canvas-native accessibility review workflow for Figma and FigJam. v3.0.0 pairs The A11Y Project Checklist with live contrast inspection, three on-canvas audits, native Dev Mode annotations, evidence-bound sign-off, and a canonical Canvas Record.

Role
Creator & Maintainer
Timeline
2023 - Present
Technologies
Figma Widget APITypeScriptWCAG 2.2TanStack StartDesign TokensAccessibility

Impact

400+

Active users on Figma Community across design teams at companies of all sizes

20 locales

Checklist and interface translated through a term-protected pipeline

v3.0.0

Audit platform, native annotations, and evidence-bound sign-off

The Problem

The first version of a11y Companion solved a workflow problem: The A11Y Project Checklist lived on a website, and designers needed it inside the file they were working in. That version worked, and teams adopted it. Using it surfaced a harder problem.

A checked box is a claim, not a measurement. Nothing in the widget knew whether the file actually met the criterion beside the checkmark.

  • Teams could complete a checklist against a file that failed contrast
  • Findings lived in the widget and never reached the canvas or Dev Mode
  • A completed review left no durable record once the file moved on
  • Approvals had no way to expire when the design changed underneath them
  • Nothing exported in a form a developer or auditor could act on

Version 3.0.0 keeps the checklist and gives every item a way to be measured, annotated, signed, and recorded.

The Solution

a11y Companion 3.0.0 turns the widget from a checklist into a review workflow that produces evidence.

On-Canvas Audits

Three audits run directly against the selected canvas scope, sharing a capped 25-finding slot with failures sorted ahead of warnings. Counts stay truthful past the cap rather than reporting only what fits.

  • Targets: fails below 24px per WCAG 2.5.8, warns below 44px, with a 2000-node traversal budget
  • Text: per-segment font size, line height, justification, line length, and all-caps checks
  • Tokens: local variable collections across modes, alias chains resolved to depth 10, alpha-composited ratios
  • Per-finding Jump to select and zoom the offending layer
  • Severity glyphs that never rely on color alone

Contrast Inspection That Refuses False Confidence

Watch mode re-checks the selected layer on every selection change through a keep-alive bridge. Ownership is shared state with last-writer-wins takeover, so the team can see who is driving.

  • Thresholds evaluated at full precision, so a borderline pair cannot round into a pass
  • A failing pair keeps an honest specimen while a separate line states the verdict
  • Color vision preview for protanopia, deuteranopia, and tritanopia, labeled as simulation
  • WCAG contrast stays the normative result; the preview never issues a second grade
  • Explicit notices for pattern, shader, and video fills instead of a silent failure

Native Annotations and Canvas Record

Findings leave the widget and become artifacts the rest of the team already knows how to read.

  • Audit findings promote to Figma Dev Mode annotations, individually or in bulk
  • Read-modify-write preserves existing annotations and stays idempotent on re-run
  • Annotations link the relevant WCAG Understanding page and cite the properties that failed
  • Canvas Record compiles progress, sign-offs, audit summaries, and priority findings into one Figma frame
  • Re-running Record refreshes the same widget-owned frame instead of creating duplicates

Sign-Off Bound to Evidence

A section can be signed by a named reviewer, and the signature fingerprints what was actually reviewed: item ids, completion state, and checklist revision. When items change underneath a signed section, the stamp turns amber and the section reads as needing re-review.

Portable Reports

Export as Markdown, CSV, or JSON. Reports carry run owner and time, scope, full counts, grouped evidence, and an explicit disclosure when rows are representative rather than complete.

Technical Implementation

Working Against the Widget Runtime

Most of the hard bugs in v3 came from the runtime behaving differently than the API surface suggests:

  • Empty spacer AutoLayouts default to 100px tall, silently inflating every badge and chip row until each spacer pinned height=0
  • Module memory is cleared between interactions, so a two-click confirm never reached its second click; pending state moved to figma.clientStorage
  • Watch ownership needed the durable sessionId rather than in-memory identity to survive the same reset
  • Export and clipboard access run through a hidden iframe bridge, since the widget sandbox has neither

Tuning the Token Audit on Real Files

Early token pairing matched on names containing "text" and "bg". Testing against Vercel's Geist file showed why that fails: ramp-named systems use gray/100 through gray/1000, and the audit was checking roughly twelve pairs. It now infers role from the trailing numeric step, reading high steps as ink and pairing them against low steps and explicit surfaces in the same family, with the pair cap raised to 800.

Enforced Design System

The widget's own design system is checked by scripts rather than convention, and the checks run in lint:

  • Variable architecture and design-system conformance checks
  • Automated AA contrast verification across the token set with shade-step suggestions
  • Theme baseline snapshots that fail on unreviewed drift
  • Bundle size budgets, kept under ceiling by minifying syntax while preserving identifiers

Translation Pipeline

Twenty locales are generated through a pipeline that shields WCAG codes and inline markup from machine translation. Missing keys fall back to English per key, so a newly shipped command renders in English rather than as a blank menu item while its translations are reviewed.

Product Site Parity

The product site is statically prerendered and gated by a Chrome-based check that exercises audit interactions and control geometry at 390, 768, 1000, and 1440px. The site's inspector demo is held to the production widget's exact specimen size, card inset, and vision grid.

Impact & Adoption

The widget has 400+ users across teams from startups to Fortune 500 enterprises, and v3 changes what those teams can prove:

  • Accessibility work becomes measurable rather than self-reported, because audits run against the file instead of a checkbox

  • Findings reach engineering through native Dev Mode annotations that carry the WCAG reference and the failing property

  • Reviews survive the file as a Canvas Record and as Markdown, CSV, or JSON exports

  • Approvals expire honestly when the design changes under a signed section

  • Twenty locales put the checklist in front of teams that do not work in English

The widget passes its own checklist. Checkboxes meet WCAG 1.4.11 non-text contrast, severity is never carried by color alone, and WCAG references render correctly in both themes. Holding the tool to the standard it measures caught defects in the tool itself.

© 2026 Mark Learst.Crafted with precision
privacy
v2026.1.0