Skip to content
Development

A focus ring that shouldn't have been full-strength

By Victor Da Luz
sveltecssaccessibilitydev-loggreenhouse

A UI review flagged a bright green horizontal line running the full width of Greenhouse’s window, sitting right below the topbar on every single launch. It looked like a rendering bug. It was actually a focus ring doing its job at full strength when it should have been whispering instead of shouting.

Why the line was there at all

Greenhouse’s dashboard swaps in and out several full-window views by destroying and recreating a Svelte {#if} branch. That’s a real accessibility problem: when a view closes and the dashboard’s zones grid comes back, keyboard focus silently drops to the document body, with nothing to tell a sighted keyboard user where they landed. The fix from an earlier pass was to treat this exactly like a client-side route change - focus the destination programmatically, same convention React Router and similar tools use for AJAX-loaded views.

That fix works. The side effect is that the resulting :focus-visible box-shadow fires on every mount, including the very first time the app opens, before the user has touched a key. And because the element being focused is the entire content landmark (not a compact heading), the accent line spans the whole window. Fully saturated, every load, no exceptions. Reads exactly like a stray border because visually, it is one.

What I didn’t do

The tempting fix is to just stop calling focus() on the initial mount. I didn’t do that - it would undo the actual accessibility behavior the line exists to serve. A real keyboard user closing a view still needs to see where they landed; removing the visual entirely just because most loads aren’t a real keyboard interaction throws out the baby with the bathwater.

What I did instead

Left the focus() call untouched. Softened only the color: box-shadow: inset 0 3px 0 0 color-mix(in srgb, var(--focus-ring) 45%, transparent). Still visible, still does its job for someone tabbing through the app, no longer reads as an unrelated decorative line on an ordinary open.

The verification detour

Here’s the part worth writing down. :focus-visible doesn’t fire inside the WebDriver-automation harness this project uses for UI verification, since a driven window never reports real OS focus - a known, documented gap from an earlier issue. So checking “does the softened line actually look softer” needed a real, normally-focused window.

My first instinct was to launch the app directly and take a screenshot to compare. Bad instinct on a multi-monitor desktop: a full-screen capture, and then a screen-region capture aimed at the app’s reported window coordinates, both ended up showing something else entirely - unrelated windows that happened to occupy that same screen real estate. Nothing sensitive left the machine, both images were deleted immediately, but it was a good reminder that “capture the screen and crop mentally” is not a safe default when you don’t control what else is on that screen.

The fix for the fix: stop touching the OS screen at all. I wrote a tiny static HTML page reproducing the exact CSS variables and both box-shadow values side by side, served it locally, and screenshotted it through a browser automation tool instead - that only ever captures page content, never the desktop. Side by side, the difference was obvious: same shape, visibly less saturated.

The addendum, same day

That isolated swatch was honest about what it proved (color math, rendering support) and honest about what it didn’t (how the real line reads against the real window). Turns out that gap mattered - 45% looked right in the swatch and was still too strong against the actual app. Cut it further to a 2px line at 20% opacity, checked against the real running window this time, and that one held up.

The lesson holds either way: a proxy that measures the right property (color delta) can still miss the actual question (does this look right in context) if the context itself is what the screenshot-avoidance workaround couldn’t safely reach. Worth the extra round rather than shipping the swatch-only answer as done.

Related reading