---
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
```
The card inherits `text-white`, making white text on white background.
### Good
```tsx
```
### Better reusable component
```tsx
function FeatureCard({ tone = 'light' }) {
const toneClass = tone === 'light'
? 'bg-white text-[#24170f]'
: 'bg-[#24170f] text-white';
return ...;
}
```
## 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:
Problem: on is unreadable / below ratio
Fix:
Proof required:
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.