What an accessibility audit found in my own SwiftUI badges
I ran a narrow accessibility-only audit pass on Deep Cut Atlas before its v1 launch - contrast, VoiceOver labels, Dynamic Type. Two of the findings turned out to be the same bug wearing different clothes, and fixing them taught me something about how translucent-tint UI badges quietly fail contrast checks no matter what color you pick.
The bug: same-hue text on its own tinted background
The app has small colored “pill” badges - one shows the recording type (Album/EP/Single/Compilation) in a color-coded chip, another marks an Apple-editorial “Featured” release in gold. Both used the same pattern: text colored a hue, sitting on a background that’s the same hue at 18% opacity.
It looks fine. It reads as “on-brand.” And it measures as illegible. I actually computed the WCAG contrast ratios by hand (relative luminance, the real formula, not a guess): the orange badge came out to 1.73:1, blue to 2.87:1, and the yellow “Featured” badge - the worst offender in the whole app - to 1.27:1. WCAG AA wants 4.5:1 for this text size. None of these were close.
The insight that made this click: alpha-blending a color into a light neutral background pulls both the text and the backdrop toward the same point in luminance space. It doesn’t matter which hue you pick - same-color-on-own-tint is structurally low-contrast. Swapping orange for blue doesn’t fix it; you’re just failing differently.
The fix
Decouple the text color from the tint. The badges now use .primary (which iOS guarantees contrasts well against system backgrounds in both light and dark mode) for the actual text, and a small colored dot - or, for the Featured badge, the existing star icon - carries the color-coding instead. You still get the at-a-glance color scan; you just don’t need to squint to read the label anymore.
A smaller, related fix: a filter-chip’s selected state used white text on the app’s accent-color fill - 4.02:1 in light mode, 3.65:1 in dark, both just under the 4.5:1 minimum. WCAG has an exception: bold text at this size only needs 3:1. So instead of touching colors, I just made the selected label bold. Free pass, one word in the code.
Lesson: “it looks fine to me” is not a contrast check. Actually computing the ratio (or running an automated contrast checker) turns a vague “is this readable” into a yes/no you can act on - and once you see the same-hue-on-own-tint pattern once, you start noticing it everywhere.
Related reading
A two-line accessibility fix that needed a real phone to prove
VoiceOver couldn't tell which playlist was linked. The fix was .accessibilityAddTraits(.isSelected). Proving it meant an unlock, a rebuild, and a mirroring tool that couldn't find its window.
Verifying a fix at an extreme setting found a second bug
The long titles truncated exactly as designed. The one-word badge - the field 'too short to ever need a line limit' - hyphenated itself into 'Al-bum' stacked in a capsule.
I only fixed the screenshot I was asked about, not the ones that were also broken
One recaptured marketing shot looked done - until the deflating question: is this really every screenshot? The other three in the same set were stale too, each in a different way.