Skip to content

fix(AvatarStatusDot): resolve WCAG 2.1 SC 1.4.1 use-of-color failure#4218

Closed
gonzoblasco wants to merge 1 commit into
facebook:mainfrom
gonzoblasco:fix/avatar-status-dot-wcag-1-4-1
Closed

fix(AvatarStatusDot): resolve WCAG 2.1 SC 1.4.1 use-of-color failure#4218
gonzoblasco wants to merge 1 commit into
facebook:mainfrom
gonzoblasco:fix/avatar-status-dot-wcag-1-4-1

Conversation

@gonzoblasco

Copy link
Copy Markdown

Summary

Resolves #4143

The AvatarStatusDot component relied on colour alone to convey status meaning (green=success, gray=neutral, red=error), which fails WCAG 2.1 Success Criterion 1.4.1 (Use of Color).

Users with colour vision deficiencies (deuteranopia, protanopia, tritanopia, achromatopsia) cannot distinguish between the three status states, and screen readers have no way to announce the status when no label is provided.

Solution

Two-pronged approach to ensure status is never conveyed by colour alone:

1. Default icons per variant (shape differentiation)

When no icon prop is provided, a default SVG icon is rendered inside the dot based on the variant:

Variant Default icon Shape conveys
success ✓ checkmark positive/accepted
neutral − dash neutral/pending
error ✕ cross negative/rejected

These icons are rendered at medium+ avatar sizes (≥ 40px) where there is enough room. At the smallest tier (≤ 36px, dotSize=10px), icons are not legible, so shape differentiation falls back to the accessible label.

2. Default accessible labels per variant (screen reader support)

When no label prop is provided, a default label is derived from the variant ("Success", "Neutral", "Error"). This ensures:

  • Screen readers always announce the status meaning
  • At the smallest avatar tier (where icons can't render), the label still conveys meaning
  • The role="img" attribute is always set when there's a label

Backward compatibility

  • Explicit label prop still takes precedence over the default
  • Explicit icon prop still takes precedence over the default
  • icon={null} explicitly suppresses the default icon
  • Consumers already passing these props see zero change

What this fixes

Before (WCAG 1.4.1 fail):

// Colour alone — no shape, no label, screen reader silent
<AvatarStatusDot variant="success" />

After (WCAG 1.4.1 pass):

// Same call, now renders a checkmark icon + "Success" label automatically
<AvatarStatusDot variant="success" />

Testing

Added 8 test cases covering:

  • Default label per variant (success, neutral, error)
  • Explicit label overriding default
  • Default icon rendering at medium+ sizes
  • No default icon at smallest avatar tier
  • icon={null} suppression
  • Different icons rendered for different variants

Checklist

  • Changes are backward compatible
  • No new dependencies (inline SVGs, no icon library needed)
  • Tests added
  • JSDoc updated with WCAG rationale
  • Follows existing code patterns (StyleX, variant map, module augmentation)

The AvatarStatusDot component relied on colour alone to convey status
(success=green, neutral=gray, error=red), which fails WCAG 2.1 Success
Criterion 1.4.1 (Use of Color).

Changes:
- Add default icons per variant (checkmark, dash, cross) so shape
  reinforces meaning alongside colour at medium+ avatar sizes
- Add default accessible labels per variant so screen readers always
  announce status meaning, including at the smallest avatar tier
  where icons cannot be rendered
- Make  fall back to a sensible default per variant
- Allow  to explicitly suppress the default icon
- Update JSDoc with WCAG 1.4.1 rationale

Backward compatible: explicit  and  props still take
precedence. Consumers who already pass these props see no change.

Closes facebook#4143
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview, Comment Jul 22, 2026 11:04pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Jul 22, 2026
@github-actions github-actions Bot added community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge needs:design-review Affects visuals — Design should review labels Jul 22, 2026

@humbertovirtudes humbertovirtudes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for digging into the 1.4.1 use-of-color failure on the status dot, this is a real gap and the instinct to add both a default per-variant label and a non-color mark is the right one.

The blocker is not the code, it is that this collides with #4157, which solves the same WCAG 1.4.1 issue on the same component a different way. The two cannot both land, so we need to converge on one approach before either moves.

Having read both, I think #4157 is the stronger base to build on: it pairs each variant with a distinct built-in shape (filled dot / ring / minus bar) drawn as a stroked SVG, which stays legible at the smallest avatar sizes where an icon cannot render at all. This PR's default-icon path only differentiates at medium and larger sizes.

Where this PR is clearly additive and worth carrying over into that effort:

  • The default per-variant accessible label (so an unlabeled dot is not silent) is valuable. Worth noting #4157 documents that a label on the dot is currently not announced inside Avatar because the Avatar root is role="img", which prunes descendant semantics, so let us make sure the label actually reaches AT as part of whichever approach wins.
  • If we keep default labels, "Online" / "Busy" / "Away" read better than "Success" / "Neutral" / "Error" for a presence dot.

Could you sync with @AKnassa on #4157 and fold the label piece in there? Genuinely good problem to have two solutions; let us land the best combined one.

@gonzoblasco

Copy link
Copy Markdown
Author

Closing in favour of #4157, which solves the same WCAG 1.4.1 issue with SVG shapes that work at every avatar size — a stronger approach than the icon-based one here.

The default accessible label per variant piece from this PR is being carried over into #4157 (with presence-oriented naming: "Online"/"Away"/"Busy" per @humbertovirtudes's suggestion).

Thanks for the review and the clear direction — the combined result will be better than either PR alone.

@gonzoblasco

Copy link
Copy Markdown
Author

Closing per maintainer feedback — converging with #4157.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] WCAG 2.1 A 1.4.1 fail - use of colour failure, accessibility

2 participants