Use cases
Accessibility Checker
Build a WCAG compliance checker with ColorAssembly
Create a comprehensive accessibility checker for color combinations.
Implementation
'use client';
import { useState, useEffect } from 'react';
import { loadColorAssembly, type ColorAssemblyAPI } from '@cymbol/colorassembly';
export function AccessibilityChecker() {
const [api, setApi] = useState<ColorAssemblyAPI | null>(null);
const [fgColor, setFgColor] = useState('#000000');
const [bgColor, setBgColor] = useState('#ffffff');
useEffect(() => {
loadColorAssembly().then(setApi);
}, []);
if (!api) return <div>Loading...</div>;
const fg = api.transforms.hexToRgb({ hex: fgColor });
const bg = api.transforms.hexToRgb({ hex: bgColor });
const wcag = api.analyze.checkContrast(fg, bg);
const apca = api.analyze.checkApca(fg, bg);
return (
<div className="max-w-4xl mx-auto p-8 space-y-8">
<h1 className="text-3xl font-bold">Accessibility Checker</h1>
{/* Color Inputs */}
<div className="grid md:grid-cols-2 gap-4">
<div>
<label className="block mb-2 font-semibold">Foreground (Text)</label>
<input
type="color"
value={fgColor}
onChange={(e) => setFgColor(e.target.value)}
className="w-full h-16 rounded-lg"
/>
</div>
<div>
<label className="block mb-2 font-semibold">Background</label>
<input
type="color"
value={bgColor}
onChange={(e) => setBgColor(e.target.value)}
className="w-full h-16 rounded-lg"
/>
</div>
</div>
{/* Preview */}
<div
className="p-8 rounded-lg"
style={{ backgroundColor: bgColor, color: fgColor }}
>
<h2 className="text-2xl font-bold mb-4">Sample Heading</h2>
<p className="mb-4">
This is sample body text to preview the color combination.
Make sure it's readable and meets accessibility standards.
</p>
<button className="px-4 py-2 border rounded">Button</button>
</div>
{/* Results */}
<div className="grid md:grid-cols-2 gap-6">
<div className="p-6 border rounded-lg">
<h3 className="text-xl font-bold mb-4">WCAG 2.1</h3>
<div className="text-4xl font-bold mb-4">{wcag.ratio.toFixed(2)}:1</div>
<div className="space-y-2">
<ResultBadge label="Normal Text AA (4.5:1)" pass={wcag.normalTextAA} />
<ResultBadge label="Normal Text AAA (7:1)" pass={wcag.normalTextAAA} />
<ResultBadge label="Large Text AA (3:1)" pass={wcag.largeTextAA} />
<ResultBadge label="Large Text AAA (4.5:1)" pass={wcag.largeTextAAA} />
</div>
</div>
<div className="p-6 border rounded-lg">
<h3 className="text-xl font-bold mb-4">APCA</h3>
<div className="text-4xl font-bold mb-4">Lc {Math.abs(apca.lc).toFixed(1)}</div>
<div className="space-y-2">
<ResultBadge label="Body Text (75+)" pass={apca.meetsBodyText} />
<ResultBadge label="Large Text (60+)" pass={apca.meetsLargeText} />
<ResultBadge label="Non-text (45+)" pass={apca.meetsNonText} />
</div>
</div>
</div>
</div>
);
}
function ResultBadge({ label, pass }: { label: string; pass: boolean }) {
return (
<div className={`flex items-center gap-2 ${pass ? 'text-green-600' : 'text-red-600'}`}>
<span className="font-bold">{pass ? '✓' : '✗'}</span>
<span>{label}</span>
</div>
);
}