---
name: contrast-readability-guard
description: Prevent unreadable same-color or low-contrast text/background combinations before shipping UI. Use when building, editing, or QAing web pages, landing pages, cards, buttons, hero sections, screenshots, or any visual design with foreground text over a colored/image/gradient background.
triggers:
  - contrast check
  - readability check
  - text on background
  - white on white
  - unreadable text
  - font color background color
  - visual QA contrast
  - WCAG contrast
---

# Contrast Readability Guard

## Contract

Before claiming a UI is ready, this skill guarantees:

- Text is visibly readable against its actual rendered background, not just theoretically styled.
- Large text, normal text, buttons, cards, nav, overlays, and mobile sticky CTAs are checked separately.
- Same-color and near-same-color foreground/background mistakes are caught before deploy.
- Image and gradient overlays are treated as risky until verified visually or with computed styles.
- Failures produce exact fix instructions: selector/location, current colors, required replacement, and proof needed.

## Required standard

Use WCAG contrast as the floor, not the ceiling:

- Normal text under 24px regular / 18.66px bold: minimum 4.5:1.
- Large text at least 24px regular or 18.66px bold: minimum 3:1.
- UI components and focus states: minimum 3:1.
- For customer-facing marketing pages, prefer 7:1 for body text when practical.

If text appears unreadable in a screenshot, the screenshot wins over code intent.

## Workflow

1. **List all text surfaces.** Include hero, cards, badges, buttons, nav, mobile nav, footer, overlays, forms, and any horizontal scroll cards.
2. **Find inherited color risk.** Flag containers where text color is inherited from a dark parent but child background is light, or vice versa. This is the classic `text-white` parent + `bg-white` card bug.
3. **Check actual pairings.** For each surface, compare foreground text color against the rendered background color/gradient/image overlay behind it.
4. **Handle images/gradients conservatively.** Require a dark/light overlay strong enough for the worst area behind the text. If uncertain, use a solid text panel or stronger overlay.
5. **Patch explicitly.** Put text color on the same component that owns the background. Do not rely on distant inheritance for cards or buttons.
6. **Verify after patch.** Rebuild/reload and inspect the actual rendered page or screenshot. Run a forbidden low-contrast scan where possible and visually inspect mobile.

## Implementation rules

- Every light card must set a dark text color on the card or heading itself.
- Every dark card must set a light text color on the card or heading itself.
- Avoid `text-white` unless the immediate background is dark or has a verified dark overlay.
- Avoid `text-[var(...)]` unless the variable is known at that component boundary.
- For reusable components used in both light and dark sections, pass an explicit `tone`/`variant` prop or set local text colors inside the component.
- Never put a light-background component inside a dark `text-white` parent without resetting text color on the component.
- Verify hover/focus states too; a button can pass at rest and fail on hover.

## Quick code patterns

### Bad

```tsx
<section className="bg-brown text-white">
  <Card className="bg-white">
    <h2>Call Before You Go</h2>
  </Card>
</section>
```

The card inherits `text-white`, making white text on white background.

### Good

```tsx
<section className="bg-brown text-white">
  <Card className="bg-white text-[#24170f]">
    <h2 className="text-[#24170f]">Call Before You Go</h2>
  </Card>
</section>
```

### Better reusable component

```tsx
function FeatureCard({ tone = 'light' }) {
  const toneClass = tone === 'light'
    ? 'bg-white text-[#24170f]'
    : 'bg-[#24170f] text-white';

  return <article className={toneClass}>...</article>;
}
```

## QA checklist

- [ ] Desktop hero headline readable.
- [ ] Mobile hero headline readable.
- [ ] Every card heading readable.
- [ ] Every card body paragraph readable.
- [ ] Every CTA readable at rest, hover, and focus.
- [ ] Mobile sticky nav readable.
- [ ] Footer readable.
- [ ] No `text-white` inherited into `bg-white` or light cards.
- [ ] No dark text inherited into dark cards.
- [ ] Screenshot inspected for real-world readability.

## Output format

```text
Contrast verdict: PASS / FAIL

Failures:
- Location: <file/component/selector or screenshot area>
  Problem: <foreground> on <background> is unreadable / below ratio
  Fix: <exact class/style change>
  Proof required: <screenshot or computed contrast result>

Passed surfaces:
- ...
```

## Anti-patterns

- Assuming Tailwind class intent equals rendered contrast.
- Checking only the hero and ignoring cards/buttons/mobile nav.
- Relying on inherited text color in reusable cards.
- Using `text-white` over pale image areas without a verified overlay.
- Saying “looks fine” without screenshot or computed evidence.
- Shipping customer-facing pages with selected/highlighted-looking text caused by color collisions.
