# Accessible Charts

**MUST** · **ID:** `design-accessible-charts` · **Category:** design
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/design-accessible-charts

> MUST: Accessible charts (color-blind-friendly palettes)

Use color-blind-friendly palettes

About 8% of men and 0.5% of women have color vision deficiencies. Use color palettes designed for color blindness (avoid red-green combinations), and supplement color with patterns, labels, or textures in data visualizations.

## Bad — do not do this

`design-accessible-charts-bad`

```tsx
export function AccessibleChartsBad() {
  const data = [
    { label: 'Active', value: 45, swatch: 'bg-green-500' },
    { label: 'Pending', value: 30, swatch: 'bg-red-500' },
    { label: 'Inactive', value: 25, swatch: 'bg-yellow-500' },
  ];

  return (
    <div className="w-full max-w-sm">
      <div className="bg-card border border-border rounded-lg p-4">
        <h3 className="font-semibold mb-3">User Status</h3>
        <div className="flex h-4 rounded-full overflow-hidden mb-4">
          {data.map((item) => (
            <div
              key={item.label}
              className={item.swatch}
              style={{ width: `${item.value}%` }}
            />
          ))}
        </div>
        <div className="flex gap-4">
          {data.map((item) => (
            <div key={item.label} className="flex items-center gap-2">
              <div className={`w-3 h-3 rounded-sm ${item.swatch}`} />
              <span className="text-xs">{item.value}%</span>
            </div>
          ))}
        </div>
        <p className="mt-3 text-xs text-muted-foreground">
          Red/green color scheme is indistinguishable for color blind users (~8% of men).
        </p>
      </div>
      <p className="text-xs text-error mt-4">
        Red/green colors - invisible to color blind users
      </p>
    </div>
  );
}
```

## Good — do this

`design-accessible-charts-good`

```tsx
type Texture = 'solid' | 'stripes' | 'dots';

const data: { label: string; value: number; swatch: string; texture: Texture }[] = [
  { label: 'Active', value: 45, swatch: 'bg-primary', texture: 'solid' },
  { label: 'Pending', value: 30, swatch: 'bg-warning', texture: 'stripes' },
  { label: 'Inactive', value: 25, swatch: 'bg-muted-foreground', texture: 'dots' },
];

/** Real textures, so a segment is identifiable without relying on its colour. */
function textureStyle(texture: Texture): React.CSSProperties | undefined {
  if (texture === 'stripes') {
    return {
      backgroundImage:
        'repeating-linear-gradient(45deg, rgba(0,0,0,0.4) 0 3px, transparent 3px 7px)',
    };
  }
  if (texture === 'dots') {
    return {
      backgroundImage: 'radial-gradient(rgba(0,0,0,0.45) 1.2px, transparent 1.4px)',
      backgroundSize: '6px 6px',
    };
  }
  return undefined;
}

export function AccessibleChartsGood() {
  return (
    <div className="w-full max-w-sm">
      <div className="bg-card border border-border rounded-lg p-4">
        <h3 className="font-semibold mb-3">User status</h3>
        <div className="flex h-5 rounded-full overflow-hidden mb-4">
          {data.map((item) => (
            <div
              key={item.label}
              className={item.swatch}
              style={{ width: `${item.value}%`, ...textureStyle(item.texture) }}
            />
          ))}
        </div>
        <div className="flex gap-4">
          {data.map((item) => (
            <div key={item.label} className="flex items-center gap-2">
              <div
                className={`w-3 h-3 rounded-sm ${item.swatch}`}
                style={textureStyle(item.texture)}
              />
              <span className="text-xs">
                {item.label} {item.value}%
              </span>
            </div>
          ))}
        </div>
      </div>
      <p className="text-xs text-success mt-4">
        Each segment carries a distinct texture (solid, striped, dotted) as well as a colour and a text label, so the
        chart still reads in greyscale
      </p>
    </div>
  );
}
```

## References

- [Designing for Color Blindness](https://www.smashingmagazine.com/2024/02/designing-for-colorblindness/)
- [Data Visualization Accessibility](https://www.w3.org/WAI/tutorials/images/complex/)
