Skip to content

Composite translucent colors in the contrast check - #243

Merged
vicmaster merged 1 commit into
masterfrom
contrast-alpha-composite
Nov 8, 2026
Merged

vicmaster merged 1 commit into
masterfrom
contrast-alpha-composite

Conversation

@vicmaster

Copy link
Copy Markdown
Owner

canvas_evaluate's contrast check walked up from a text node to the nearest fill and used it as solid paint. It dropped the fill's alpha. A translucent fill was judged as its opaque color, so a common selected-state tint, rgba(245,158,11,0.08) over a near-black #14110c panel, was judged as solid amber. Light text on it was reported at 1.72:1 as a WCAG error; the real contrast is 13.45:1. A fully transparent fill, rgba(0,0,0,0), was read as solid black. Translucent text colors were treated as opaque too.

This showed up when the published viewer was imported and evaluated with framesmith itself: its selected breakpoint button got a false contrast error. App screens use tinted selected and hover fills on almost every list and toolbar, so the bug would misfire constantly on them.

The fix

  • The check now collects fills from the text's parent upward until it reaches an opaque one. Translucent fills (rgba or #RRGGBBAA) are blended over what is behind them. When no opaque fill is behind them, they are blended over the white page, which is what the renderer shows.
  • A translucent text color is blended into that background before the ratio is computed.
  • transparent, none and zero-alpha fills are see-through; the walk continues past them.
  • A fill the parser cannot read, such as hsl() or a named color, still stops the walk and skips the check, as before, so nothing is measured against the wrong background.
  • When blending happened, the message names both the fill and the result, for example rgba(255, 255, 255, 0.1) (composited: #1a1a1a). The autofix color is picked against the blended background.

The fix cuts both ways. A faint white wash over black is now judged dark, so dark text on it fails, where it used to pass against "white".

The canvas_evaluate docstring and docs/GUIDELINES.md now say contrast is measured as rendered.

Testing

  • New test-contrast-alpha.ts (fast tier, 10 checks): the viewer's tint, the same tint as #RRGGBBAA, a white wash over black, a fully transparent fill, translucent text, a tint with no opaque fill behind it, the transparent keyword, an unreadable hsl() fill, the composited color in the message, and an unchanged opaque pair. Seven of the checks failed on the old code.
  • npm run test:fast: 82/82.
  • npm run test:full: 119/119. Fifteen browser tests launch Puppeteer directly and need PUPPETEER_EXECUTABLE_PATH; they were run with it set.

The check took the nearest fill as solid paint and dropped its alpha, so an
8% amber tint on a near-black panel was judged as solid amber (1.72:1, a
false error; the real pair is 13.45:1), and rgba(0,0,0,0) read as black.
Translucent fills now blend over the fills behind them (or the white page),
translucent text blends into that, and "transparent" is see-through.
@vicmaster
vicmaster merged commit 9cf8f42 into master Nov 8, 2026
3 checks passed
@vicmaster
vicmaster deleted the contrast-alpha-composite branch November 8, 2026 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant