Repository navigation
Dialog and dropdown focus rings render doubled: outline-hidden is a Tailwind v4 class on a v3 project #2454
Description
Problem
Every dialog's close button shows two focus indicators at once: the browser's default blue outline, plus a grey box from the component's own ring. They nest, so the control reads as bordered rather than focused.
The cause is a Tailwind version mismatch. dialog-content.svelte:36 carries:
focus:outline-hidden ... focus:ring-2 focus:ring-offset-2 ring-offset-background focus:ring-ring
outline-hidden is a Tailwind v4 class. This project is on v3 (packages/desktop-app/package.json:102 → "tailwindcss": "^3.4.17"), where the equivalent is outline-none. In v3 the class simply does not exist, so it compiles to nothing and the browser outline is never suppressed — leaving it to render underneath a ring-2 with a ring-offset-2 gap.
Nothing warns about this: an unknown utility is silently dropped, so it looks like working code.
Scope
Five shared UI components use the v4-only class; thirteen others correctly use outline-none, which is what makes this a mismatch rather than a convention:
ui/dialog/dialog-content.svelte— the visible one: affects every dialog in the appui/dropdown-menu/dropdown-menu-item.svelteui/dropdown-menu/dropdown-menu-checkbox-item.svelteui/dropdown-menu/dropdown-menu-radio-item.svelteui/dropdown-menu/dropdown-menu-sub-trigger.svelte
Two neighbouring components (tabs-trigger, switch) also pair focus:ring-2 with focus:ring-offset-2; they are worth eyeballing in the same pass even though their outline classes are correct.
Also worth settling: what colour is a focus ring?
DESIGN.md contradicts itself, and this issue is a good moment to resolve it rather than pick one silently:
- Line 218 and the Do's list (line 334) say primary (teal) — "Use
primary(teal) for all interactive affordances — buttons, links, focus rings, task indicators" - But
--ringis defined as neutral greyhsl(0 0% 63%)(line 27), and the Input spec (line 292) says "Focus ring usesringcolor"
So the documented intent is teal while the token is grey. Whichever is right, the doc and the token should agree, and DESIGN.md should be updated in the same change.
Proposed fix
- Replace
outline-hidden→outline-nonein the five files - Decide teal-vs-grey for
--ring, apply it, and correctDESIGN.mdso the two agree - Review whether
ring-offset-2is wanted at all — the offset gap is what makes the indicator read as a nested box; a plainring-2with no offset is usually cleaner on a small icon button
Acceptance criteria
- No
outline-hiddenremains inpackages/desktop-app/src/lib/components/ui/ - A focused dialog close button shows exactly one focus indicator
- The indicator is still clearly visible in both light and dark mode (this is an accessibility affordance — the fix must not simply remove it)
- Dropdown menu items show one indicator on keyboard focus
-
--ringandDESIGN.mdagree on the focus-ring colour - Verified with keyboard navigation (Tab), not just mouse — several of these only appear on
:focus, not:focus-visible
Context
Spotted while reviewing the relationships panel (#2432) against DESIGN.md. It is not caused by that work — dialog-content.svelte dates to the original refactor: Flatten repository structure to packages/ layout commit and is untouched by #2446. Filed separately because it is shared UI-kit styling affecting every dialog and dropdown in the app, which is a different review job from a single panel.
Activity
malibio commentedon Sep 8, 2026
Frontend: 4895 passed (232 test files)
Fix doubled focus rings in dialogs and dropdowns (closes #2454)
Fix doubled focus rings in dialogs and dropdowns (#2476)