I run a service that publishes landing pages for other people's brands. Every page gets the client's own logo in the header bar, and the bar comes in three colours: white, the brand colour, or a neutral dark.
Picking the bar looked like the most trivial decision in the system. It produced a bug that is invisible to every signal you would normally trust. The page renders. The logo file is served. Every other gate is green. And half the client's name is not there.
A client wordmark, transparent PNG: the word in white, a second word in the brand blue, hyphen between them. On the brand-blue bar, the blue word measured 1.15:1. Gone. On the white bar, the white word measured 1.00:1. Perfectly gone.
Two bars, and each one erased a different half of the same logo. There was no correct answer in the set, which is how the neutral third bar got built.
That is the first lesson: with two options it is easy to find one that scores acceptably on average, and averages are exactly the wrong statistic here.
The obvious implementation is: composite the logo over the bar, compute contrast per pixel, average it, pick the winner.
It fails because it cannot tell these two situations apart: A whole word vanished. A faint interior highlight faded.
Both remove a similar quantity of low-contrast ink. Only one is a defect. Average them into a single number and the word disappearing looks like a mild dip.
A word owns its horizontal slice of the mark — nothing else is in those columns. A lost interior detail shares its columns with ink that still reads.
Composite over the bar (alpha included — this is what the eye actually receives), then slice the mark's bounding box into full-height columns and ask which ones went dark:
Whitespace never enters the calculation — a column with no ink simply isn't there. The VISCOLMIN_INK floor handles the case that is left: a column holding a few stray anti-aliased pixels, where two of them fading would otherwise read as a hole.
My first threshold was WCAG's 3.0:1 for non-text contrast. It rejected logos that were obviously fine on screen: a solid green icon at ~2.1:1, an orange one at ~2.2:1. Both perfectly readable.
WCAG's 3:1 is calibrated for thin UI strokes. A chunky solid mark carries far more ink per glyph and survives well below it. So the floor is two-tier:
A column only counts as lost when its ink cannot manage even 1.8:1 — truly gone (white-on-white at 1.00, blue-on-blue at 1.15), not merely muted. The separate "crisp" share still uses the strict threshold, so a muted mark scores low without being failed outright.
Every threshold in that block is pinned by a calibration test against real rendered headers. When you are hand-tuning constants against human perception, the test suite is the only thing standing between you and re-breaking it in three months.
Everything above applies to transparent logos, where the bar shows through and the question is can I still read the ink.
An opaque logo — one with its own baked-in background plate — is legible on every bar by construction. The ink question is meaningless. The real question is whether the bar matches the plate, because if it does not, the logo renders as a visible pasted rectangle sitting on your header.
Judging an opaque logo by ink contrast doesn't merely fail to help. It actively selects the wrong bar, because maximum ink contrast is often precisely the bar that clashes hardest with the plate.
