Play+ Accessibility Helper: playa11y
Introduction
In the Play+ ecosystem, accessibility is not an afterthought—it’s a core component of our "Design that Breathes" philosophy. We believe that great design is inclusive design, and an application that isn’t accessible to everyone is incomplete. This guide is based on the principle of Inclusive by Design.
The playa11y
helper provides a foundational toolkit to automate accessibility best practices, reduce boilerplate code, and empower developers to build experiences that are usable by everyone, regardless of their abilities. This aligns directly with our Inclusive pillar by embedding WCAG 2.2 AA standards directly into the development workflow, ensuring every user feels a sense of belonging and trust.
The Three Pillars of Accessibility Enforcement
1. Automated Tooling (Linter & CI)
- Our
playlint
ESLint config includes thejsx-a11y
plugin. - All PRs run an
axe-core
audit. - If critical violations are found, the build fails (non-negotiable quality gate).
2. Accessible-by-Default Components
-
All core Play+ components:
- Include full keyboard navigation
- Clear focus states
- Compliant contrast ratios
- Correct ARIA roles (e.g., modals, tabs)
3. Developer Practices & the playa11y
Toolkit
- Developers own the final layer of a11y.
- This guide and toolkit make it easy to implement best practices.
Package Info
Package / Path | Description |
---|---|
Golden Path (Recommended) | Pre-installed: /system/play.a11y.ts |
Uplift Path (Coming Soon) | @playplus/a11y |
Folder Reference
Directory | Purpose & Guidelines |
---|---|
/system/ | Core Play+ helpers (e.g., play.a11y.ts ) |
/config/ | User-overridable configs (e.g., play.a11y.config.json ) |
Helper - Pillars Alignment
Pillar | How This Helper Aligns |
---|---|
Inclusive | Primary Pillar: Enforces WCAG 2.2 AA for diverse ability support |
Intuitive | Ensures predictable, seamless experience for assistive technologies |
Engaging | Allows graceful degradation of motion, ensuring delight without discomfort |
Helper Overview
The playa11y
helper is a lightweight utility that abstracts the complexity of accessibility. It automates common tasks, avoids boilerplate, and prevents errors.
Key Features
- Live region announcements
- Safe focus management
- Reduced motion detection
- Dev-only contrast ratio checks
Its goal is to make "the right way the easy way."
Config Options
Config Variable | Default Value | Description | Recommended Value |
---|---|---|---|
liveRegionPoliteness | "polite" | Default level for screen reader messages | "polite" |
focusRing.color | "#E91E63" | Color of focus ring (from token) | $color-border-focus |
focusRing.offset | "2px" | Distance between element and focus ring | $space-1 |
focusRing.width | "2px" | Thickness of focus ring | 2px |
Helper Methods
Method Name | Description | Signature | ||
---|---|---|---|---|
announce | Announces a message to screen readers | `announce(message: string, politeness?: 'polite' | 'assertive'): void` | |
focus | Safely moves focus to an element | `focus(element: HTMLElement | null | undefined): void` |
prefersReducedMotion | Detects user's motion preference | prefersReducedMotion(): boolean | ||
getContrast | Calculates contrast ratio (dev only) | getContrast(color1: string, color2: string): number |
Usage Examples
React: Managing Focus and Announcements
import React, { useRef, useEffect } from 'react';
import { playa11y } from '../system/play.a11y';
function AddUserForm({ onUserAdded }) {
const nameInputRef = useRef(null);
const successMessageRef = useRef(null);
useEffect(() => {
playa11y.focus(nameInputRef.current);
}, []);
const handleSubmit = (e) => {
e.preventDefault();
const newUserName = nameInputRef.current?.value || "New User";
onUserAdded(newUserName);
playa11y.announce(`User ${newUserName} was successfully added.`);
playa11y.focus(successMessageRef.current);
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="userName">User Name</label>
<input id="userName" ref={nameInputRef} type="text" />
<button type="submit">Add User</button>
<div ref={successMessageRef} tabIndex={-1} role="alert"></div>
</form>
);
}
Angular: Using Angular CDK
import { Component, ElementRef, ViewChild, AfterViewInit, inject } from '@angular/core';
import { LiveAnnouncer, FocusMonitor } from '@angular/cdk/a11y';
@Component({
selector: 'app-add-user-form',
template: `...`
})
export class AddUserFormComponent implements AfterViewInit {
@ViewChild('nameInput') nameInput!: ElementRef;
@ViewChild('successMessage') successMessage!: ElementRef;
private announcer = inject(LiveAnnouncer);
private focusMonitor = inject(FocusMonitor);
ngAfterViewInit() {
this.focusMonitor.focusVia(this.nameInput.nativeElement, 'program');
}
handleSubmit() {
this.announcer.announce('User successfully added.', 'polite');
this.focusMonitor.focusVia(this.successMessage.nativeElement, 'program');
}
}
The Play+ Accessibility Checklist
Semantic Structure
- Use semantic HTML5 (
<main>
,<nav>
, etc.) - Follow logical heading hierarchy (
<h1>
to<h2>
) - Use
<p>
,<ul>
,<ol>
for text, avoid<div>
for layout
Keyboard Navigation
- All interactive elements are keyboard accessible
- Focus order matches visual order
- No keyboard traps
Forms & Inputs
- Each input has a
<label for="...">
- Errors use
aria-describedby
- Buttons have meaningful labels
Images & Media
-
<img>
elements havealt
, oralt=""
if decorative - Videos have captions/audio descriptions if needed
Dynamic Content
- Use
playa11y.announce()
for updates - Manage focus for modals/tooltips
- Minimize animation if
prefersReducedMotion()
is true
Manual Verification
- Navigate with only keyboard
- Use screen reader (VoiceOver, NVDA)
- Test UI at 200% zoom
⚠️ Common Pitfalls
- Icon-only buttons missing
aria-label
- Skipped heading levels
- Custom controls without full keyboard support
Summary
Accessibility is a shared responsibility and a mark of true craftsmanship. At Play+, we make it automatic, built-in, and empowering.
- 🧪 Catch Issues Early: Linters and CI audits
- 🧱 Accessible Foundations: Ready-to-use, WCAG-compliant components
- 🛠 Developer Empowerment: The
playa11y
helper and checklists