Sooner or later a design team ships a carefully chosen off-white — off-blue, off-cream, off-anything — and then opens the app on a $120 phone and watches it render as dirty gray. Same hex, same build, different glass. Nothing is broken. The panel edited the color, the way cheap panels edit every color, and the tint was small enough to edit away. There's a number that predicts this before the phone is ever in the room, and it's readable straight off the hex.
A cheap panel edits your color four ways
The first edit is compression. An entry-level LCD covers a fraction of sRGB: independent panel measurements routinely land between 55 and 70 percent of the gamut, with large per-color errorsrc. Chroma — how far a color sits from the gray of equal lightness — is the budget a color spends on looking like itself, and compression taxes it. A tint that started with almost none arrives with none.
The second is temperature. sRGB assumes a D65 white, the 6,500 K standard daylight the whole pipeline calibrates againstsrc. Budget modules ship visibly cooler — uncalibrated panels drift well into the 7,000ssrc — because a blue-ish white reads as brighter under store lighting, and brighter sells. That blue cast isn't a spot defect. It sits across the entire grayscale, at a magnitude comparable to a subtle warm tint, which means the panel can cancel your background outright. Warm cream in the design file, clinical white on the glass.
The third edit runs the other way. A budget AMOLED often has a native gamut wider than sRGB, and its default vivid mode stretches sRGB content across the whole thingsrc. Your quiet tint renders at roughly double saturation and suddenly has an opinion. The same hex now describes two different apps, and which one ships depends on which failure the panel prefers.
The last is resolution — of color, not pixels. Plenty of cheap panels are 6-bit with FRC, faking the missing levels by flickering between the ones it hassrc. A soft near-white gradient develops visible steps, and two surfaces a few values apart stop reading as design and start reading as a rendering bug.
Channel spread is a chroma meter you already have
Predicting whether a tint survives doesn't need a colorimeter. Pull the hex apart instead: spread is the distance between the highest and lowest of the three channels. It's a crude proxy for chroma, and crude is enough. The paper blue a designer picks for a reading canvas, #F2F5FA, has channels 242, 245 and 250 — a spread of 8, about 3 percent of the range.
Eight out of 255 sounds too small a number to reason about at all. It's exactly the right size, because it matches the size of the enemy: a few hundred kelvin of white-point error shifts the grayscale by the same handful of channel values. Below a spread of about 10 — 4 percent — your color and the panel's error are the same magnitude, so the panel wins half the time, and you don't get a vote in which half. The tint may render as intended, as gray, or tinted the other direction, and all three outcomes pass the same QA build.
The check works anywhere a hex code is visible. When the outer pairs of digits sit within about 0x0A of each other, the color is inside the error bar.
#F2F5FA's channels sit 8 apart — inside the ≈10 a budget panel can miss by. The converted #E6ECF6 doubles the spread and steps the ceiling down to 246.Scale the tint instead of forking it
The reflex fix is a second palette for cheap devices, and a second palette forks the brand — two sets of approvals, two directions of drift. The conversion that forks nothing keeps the hue and raises its insistence. A channel's identity lives in its distance from the strongest channel; multiply the distances and the color becomes more of what it already was.
// spread = strongest channel minus weakest — the chroma proxy. const spread = (rgb) => Math.max(...rgb) - Math.min(...rgb); // Same hue, more insistence: keep each channel's distance from the // ceiling, multiply it by k, and hang the result from a ceiling // stepped down 4 — the step pure-white cards will sit on. function budgetTint([r, g, b], k = 2) { const max = Math.max(r, g, b); const maxNew = max - 4; return [r, g, b].map(c => maxNew - (max - c) * k); } budgetTint([0xF2, 0xF5, 0xFA]); // → [230, 236, 246] = #E6ECF6
On the paper blue the distances from the 250 ceiling are 8, 5 and 0. Doubled, they're 16, 10 and 0, hung from a ceiling stepped down to 246 — which is #E6ECF6. Spread 16, same hue, twice the presence, and pure #FFFFFF cards now sit a clear step above the canvas instead of dissolving into it. The same arithmetic works on warm creams, mint-greens, blush-pinks. Hue never changes; only its volume does.
The perceptual version of the same move: convert to OKLCH, multiply C by about 2, take a hundredth off L, keep Hsrc. HSL is the one space to avoid for this; its saturation number is close to meaningless near white. Either way lightness barely moves, so text contrast lands where it already was — the accessibility checks pass again for the same reason they passed before, and they still get re-run.
Users never see the second tier
Tier one is the color-managed world — iPhones, flagship Androids — and it keeps the original tint with near-white cards. Tier two is everything else: the converted tint, pure-white cards, hairlines nudged one step darker. Ink, accents and type stay identical across both. Nobody sees a different theme; they see the same brand, still legible.
Choosing the tier doesn't need a panel database. The platform's low-RAM flagsrc tracks device class closely enough to pick a default, and device class tracks panel class. The misfires — and the people who simply prefer one look — are covered by a three-way setting tucked under appearance: Auto, Soft, Rich.
Emulators show you your monitor's opinion
None of this is checkable in a design tool or an emulator; both render on the calibrated monitor already on your desk. The verdict comes from the cheapest device in the test drawer, at full brightness and again near 40 percent, which is where phones actually live indoors.
Four things get read off the glass. The canvas reads as a color, and never as a white that needs cleaning. A card's edge shows without tilting the phone. A canvas-to-white gradient runs smooth, with no steps. And the photos still out-color the chrome — the moment the background wins that contest, the tint went too far. Four yeses on the worst device closes the ticket.
The rule compresses to a sentence: under 4 percent of channel spread, a tint sits inside a cheap panel's error bar, and the fix is the same hue with its channel distances doubled and the ceiling stepped down so white cards keep their edge. Hue is the brand. Spread is the volume knob.
Somewhere in the next design review there's a $120 test phone going around the table, brightness thumbed down to 40, and the canvas on it still reads blue. Nobody at the table can tell which tier they're holding. That's the whole feature.