Product Designer
Building a Component Library Spec With Accessibility Baked In
Building a Component Library Spec With Accessibility Baked In
Key Takeaway
We generate a full accessible component library β React + Tailwind code, ARIA labels, keyboard navigation, screen reader support β in 30 minutes instead of 4 weeks.
The Problem
Every product team has the same conversation at some point:
"We need a component library." "Great. Let's allocate 2-4 weeks." "We don't have 2-4 weeks." "Then we'll use a UI kit and customize later."
They never customize later. What happens instead: developers copy-paste from the UI kit, tweak inline styles, accessibility gets skipped because "we'll add it in the polish pass." The polish pass never happens. Six months later you have 14 different button variants in production, none of them keyboard-navigable, and your Lighthouse accessibility score is 47.
I've seen this at PyratzLabs. I've seen it at every startup I've been adjacent to. Component libraries are a tax nobody wants to pay upfront. But the debt compounds with interest.
The real problem isn't time. It's that building a proper component library requires three overlapping skill sets: design systems thinking, frontend engineering, and accessibility expertise. Most startups have zero of these as dedicated roles.
The Solution
Our UI/UX Pro Max agent generates complete component specs with production-ready React + Tailwind code. Not mockups. Not design tokens. Actual components with every state, every ARIA attribute, every keyboard interaction.
Accessibility isn't a layer added on top. It's baked into the generation. Every component ships with:
- ARIA labels and roles
- Keyboard navigation (Tab, Enter, Escape, Arrow keys)
- Screen reader announcements
- Focus management
- Color contrast compliance (WCAG AA minimum)
The Process (with code/config snippets)
You describe what you need in plain text:
View details
Generate a component library for a B2B SaaS dashboard:
- Button (primary, secondary, ghost, danger, sizes: sm/md/lg)
- Text Input (with label, error, helper text, prefix/suffix)
- Modal (with focus trap, close on Escape, accessible title)
- Card (with header, body, footer, clickable variant)
- Navigation sidebar (collapsible, with active states, keyboard nav)
- Dropdown Select (searchable, multi-select, accessible)
- Toast/Notification (auto-dismiss, screen reader announce)
- Data Table (sortable, selectable rows, pagination)
The agent produces full component files. Here's what a Button looks like:
tsxShow code
// Button.tsx
import { forwardRef, ButtonHTMLAttributes } from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
import { Loader2 } from 'lucide-react';
const buttonVariants = cva(
// Base styles with focus-visible ring for keyboard navigation
'inline-flex items-center justify-center rounded-md font-medium transition-colors ' +
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ' +
'disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
primary: 'bg-indigo-600 text-white hover:bg-indigo-700 focus-visible:ring-indigo-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500',
ghost: 'hover:bg-gray-100 text-gray-700 focus-visible:ring-gray-500',
danger: 'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
},
},
defaultVariants: { variant: 'primary', size: 'md' },
}
);
interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
loading?: boolean;
icon?: React.ReactNode;
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, loading, icon, children, disabled, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(buttonVariants({ variant, size }), className)}
disabled={disabled || loading}
aria-disabled={disabled || loading}
aria-busy={loading}
{...props}
>
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden="true" />}
{icon && !loading && <span className="mr-2" aria-hidden="true">{icon}</span>}
{children}
</button>
);
}
);
Button.displayName = 'Button';
export { Button, buttonVariants };
export type { ButtonProps };
Every component comes with a spec sheet:
markdownShow code
## Button β Spec Sheet
### States
| State | Visual | Keyboard | Screen Reader |
|----------|---------------------------------|----------------|------------------------|
| Default | Standard styling | Tab to focus | Reads label |
| Hover | Darker background | N/A | N/A |
| Focus | Ring outline (2px offset) | Tab | "Button, [label]" |
| Active | Slight scale/press effect | Enter or Space | N/A |
| Disabled | 50% opacity, no pointer events | Skipped in tab | "Button, [label], dimmed" |
| Loading | Spinner icon, disabled state | Skipped in tab | "Button, [label], busy"|
### A11y Checklist
- [x] Minimum touch target: 44x44px (sm is 32px β add padding zone)
- [x] Color contrast: 4.5:1 minimum (all variants pass AA)
- [x] Focus indicator visible on keyboard nav
- [x] aria-disabled instead of removing from DOM
- [x] aria-busy for loading state
- [x] Icon marked aria-hidden (decorative)
The Modal component includes a focus trap β critical for accessibility:
tsxShow code
// Focus trap hook used by Modal
function useFocusTrap(ref: RefObject<HTMLElement>, isOpen: boolean) {
useEffect(() => {
if (!isOpen || !ref.current) return;
const focusableElements = ref.current.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstEl = focusableElements[0] as HTMLElement;
const lastEl = focusableElements[focusableElements.length - 1] as HTMLElement;
firstEl?.focus();
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') { /* close modal */ }
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === firstEl) {
e.preventDefault();
lastEl?.focus();
} else if (!e.shiftKey && document.activeElement === lastEl) {
e.preventDefault();
firstEl?.focus();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [isOpen, ref]);
}
The Results
| Metric | Manual Build | Agent-Generated |
|---|---|---|
| Time to full library | 2-4 weeks | 30 minutes |
| Components with a11y | ~30% (retroactive) | 100% (built-in) |
| WCAG AA compliance | Partial | Full |
| Keyboard navigation | Often missing | Every component |
| Screen reader tested | Rarely | Spec'd per state |
| Consistency across components | Varies by developer | Uniform patterns |
| Documentation | Separate effort | Generated alongside code |
The output isn't a starting point. It's a production-ready library. We shipped Artificial-Lab's dashboard using agent-generated components with zero accessibility audit failures.
Try It Yourself
Describe your component needs to the UI/UX Pro Max agent. Be specific about variants and states. The agent generates React + Tailwind by default, but you can request Vue, Svelte, or vanilla HTML. Accessibility is non-negotiable β it's always included.
Start with the core 8 components above. You'll have a library in half an hour that would take a team a month.
Accessibility isn't a feature. It's a baseline. The only reason it gets skipped is because it's tedious. Machines don't skip tedious.
Related case studies
Frontend Developer
From 'This UI Sucks' to Production-Ready in One Prompt
We described what was wrong with a UI component in plain English and got back production-ready React and Tailwind code with proper spacing, accessibility attributes, and responsive behavior in 45 seconds.
Product Designer
Our AI Agent Designed a Complete Design System
We described our brand in plain text and received a complete design system in 3 hours: color palette with semantic tokens, typography scale, component library, spacing system, and a Figma file β replacing a 2-4 week agency sprint.
Design Lead
We Audited 400 Figma Components for Accessibility in 8 Minutes
Our AI agent ran a full WCAG compliance audit across 400 Figma components in 8 minutes β checking color contrast ratios, minimum text sizes, touch target sizes, and interactive state coverage.
Want results like these?
Start free with your own AI team. No credit card required.