Skip to content

Dialog and dropdown focus rings render doubled: outline-hidden is a Tailwind v4 class on a v3 project #2454

Closed

Description

@malibio

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 app
  • ui/dropdown-menu/dropdown-menu-item.svelte
  • ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
  • ui/dropdown-menu/dropdown-menu-radio-item.svelte
  • ui/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 --ring is defined as neutral grey hsl(0 0% 63%) (line 27), and the Input spec (line 292) says "Focus ring uses ring color"

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

  1. Replace outline-hidden → outline-none in the five files
  2. Decide teal-vs-grey for --ring, apply it, and correct DESIGN.md so the two agree
  3. Review whether ring-offset-2 is wanted at all — the offset gap is what makes the indicator read as a nested box; a plain ring-2 with no offset is usually cleaner on a small icon button

Acceptance criteria

  • No outline-hidden remains in packages/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
  • --ring and DESIGN.md agree 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

  1. malibio commented on Sep 8, 2026

    @malibio
    CollaboratorAuthor

    Frontend: 4895 passed (232 test files)

  2. self-assigned this
    on Sep 8, 2026
  3. added 2 commits that reference this issue on Sep 8, 2026
    63f3518
    855c490
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions