nuxt / nuxt/ui

SelectMenu/Select: highlighted option fails WCAG 1.4.11 — ~1:1 contrast between highlight and menu background

Open
#6,588 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v5
Dominant language
TypeScript
Stars
6.9k
Forks
1.1k
Avg merge
1d 7h
Merged PRs (30d)
57

Description

Environment
  • @nuxt/ui 4.8.1 (Vue + @nuxt/ui/vite plugin), reproducible with the default theme anywhere, including the docs demos
  • Latest release checked: v4.8.2 (theme unchanged on v4 branch)
Is this bug related to Nuxt or Vue?

Both (default theme, framework-independent)

Package

v4.x

Version

v4.8.1 / v4 branch

Reproduction

Open any USelectMenu/USelect demo on the docs with the default theme, e.g. https://ui.nuxt.com/docs/components/select-menu, open the menu and hover an option or navigate with arrow keys. The highlight is barely perceptible in light mode and effectively invisible in dark mode. (No custom playground needed — this is the default theme on the docs site itself; happy to provide a play.ui.nuxt.com link if required.)

Description

Where the problem is

The highlighted (hovered / keyboard-active) option paints its background via a ::before overlay using bg-elevated/50:

data-highlighted:not-data-disabled:before:bg-elevated/50

With the default neutral: 'slate' palette this computes to:

Mode Menu background (bg-default) Highlight (bg-elevated/50 blended) Contrast ratio
light #ffffff slate-100 @ 50% → ≈ #f8fafc ≈ 1.03:1
dark slate-900 #0f172a slate-800 @ 50% → ≈ #162032 ≈ 1.1:1

WCAG 2.1 SC 1.4.11 Non-text Contrast requires 3:1 for visual information that identifies UI component states. Both modes are an order of magnitude below that.

This is more than a cosmetic issue: inside an open listbox/combobox, DOM focus stays on the trigger/input and arrow-key navigation moves only data-highlighted. The highlight therefore is the focus indicator for keyboard users (SC 2.4.7 Focus Visible), and at ~1:1 contrast keyboard users — especially low-vision users — cannot tell which option is active.

Same pattern in sibling components

Also note the active variant (text-highlighted before:bg-elevated, e.g. select.ts active.true) is solid bg-elevated — slate-100 on white ≈ 1.07:1 — though the selected state at least has the trailing check icon as a non-color indicator.

Suggested direction

A solid, sufficiently dark/light highlight — the classic pattern used by native selects and most design systems — passes comfortably, e.g. primary-600 with inverted text in light mode (white on indigo-600 ≈ 6.3:1; bg vs white menu ≈ 6.3:1) and primary-400 with inverted text in dark mode (slate-900 on indigo-400 ≈ 5.8:1). Alternatively keep it neutral but raise the tone well past elevated/50 and pair it with a visible non-background cue.

Workaround we ship today (app config / vite plugin ui option):

select: {
  variants: {
    active: {
      true: { item: 'data-highlighted:not-data-disabled:text-inverted data-highlighted:not-data-disabled:before:bg-(--ui-color-primary-600) dark:data-highlighted:not-data-disabled:before:bg-(--ui-color-primary-400)' },
      false: {
        item: 'data-highlighted:not-data-disabled:text-inverted data-highlighted:not-data-disabled:before:bg-(--ui-color-primary-600) dark:data-highlighted:not-data-disabled:before:bg-(--ui-color-primary-400)',
        itemLeadingIcon: 'group-data-highlighted:not-group-data-disabled:text-inverted'
      }
    }
  }
}

This came out of a WCAG audit of our app (the audit flagged the default USelect highlight at ~1:1) — happy to open a PR if you agree on the direction.

Additional context

Related in spirit to #1284 (default theme failing an accessibility audit), but a different component and criterion.

Logs

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the highlighted-option variants in src/theme/select.ts and compare the related rules in src/theme/select-menu.ts, src/theme/dropdown-menu.ts, and src/theme/command-palette.ts; InputMenu reportedly shares the select theme. Reproduce the default-theme behavior in the SelectMenu or Select docs demo, then verify that the chosen highlight treatment provides at least 3:1 non-text contrast in light and dark modes without losing disabled or selected-state cues.

Written by the indexing model from the issue text.

Assessment

Tech stack
tailwindcss, typescript
Domain
accessibility, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.