ColorAssembly
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>
  );
}

On this page