Skip to main content
Generates accessible color combinations that meet WCAG contrast requirements.

Usage

import { useAccessibleColor } from "@servicetitan/anvil2";

function ColorfulBadge({ color }) {
  const colors = useAccessibleColor(color, "light");

  return (
    <span
      style={{
        color: colors.foreground,
        backgroundColor: colors.background,
        borderColor: colors.border,
      }}
    >
      Badge Text
    </span>
  );
}

Parameters

ParameterTypeDescription
hexstringThe hex color to generate accessible colors for (e.g., "#FF0000")
mode"light" | "dark"The color mode for context-aware generation

Return Value

PropertyTypeDescription
foregroundstring | undefinedAccessible text color (4.5:1 contrast ratio)
backgroundstring | undefinedThe background color
borderstring | undefinedAccessible border color (3:1 contrast ratio)

Contrast Requirements

The hook ensures colors meet WCAG accessibility standards:
  • Text (foreground): 4.5:1 contrast ratio against background
  • Border: 3:1 contrast ratio against page background
  • Colors are iteratively adjusted up to 16 times to meet requirements

Examples

Dynamic Badge Colors

function CategoryBadge({ category }) {
  const categoryColors = {
    urgent: "#FF0000",
    warning: "#FFA500",
    info: "#0066CC",
    success: "#00AA00",
  };

  const colors = useAccessibleColor(categoryColors[category], "light");

  return (
    <span
      style={{
        color: colors.foreground,
        backgroundColor: colors.background,
        border: `1px solid ${colors.border}`,
        padding: "4px 8px",
        borderRadius: "4px",
      }}
    >
      {category}
    </span>
  );
}

Theme-Aware Colors

function ThemedTag({ color }) {
  const { mode } = usePrefersColorScheme();
  const colors = useAccessibleColor(color, mode);

  return (
    <span
      style={{
        color: colors.foreground,
        backgroundColor: colors.background,
      }}
    >
      Tag
    </span>
  );
}

User-Selected Colors

function CustomColorPicker({ userColor }) {
  const colors = useAccessibleColor(userColor, "light");

  return (
    <div>
      <div
        style={{
          backgroundColor: colors.background,
          color: colors.foreground,
          padding: "16px",
        }}
      >
        Preview with accessible text
      </div>
      <Text size="small" subdued>
        Text color adjusted for {">"}4.5:1 contrast ratio
      </Text>
    </div>
  );
}

Algorithm Details

Light Mode

  1. Determines if the background is light or dark using contrast comparison
  2. Selects white or dark text based on background
  3. Ensures 4.5:1 contrast ratio for text readability
  4. Falls back to black text if contrast is insufficient
  5. Generates border color with 3:1 contrast against page background

Dark Mode

  1. Starts with the provided color for both foreground and background
  2. Ensures background meets darkness threshold for dark themes
  3. Iteratively adjusts foreground and background for 4.5:1 contrast
  4. Prioritizes background darkening to maintain theme consistency
  5. Generates border color with 3:1 contrast against page background
Last modified on January 23, 2026