SelectMenu/Select: highlighted option fails WCAG 1.4.11 — ~1:1 contrast between highlight and menu background
Nobody has claimed this yet.
- 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/viteplugin), reproducible with the default theme anywhere, including the docs demos - Latest release checked: v4.8.2 (theme unchanged on
v4branch)
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:
src/theme/select.ts#L20(inherited bySelectMenuviadefuFninsrc/theme/select-menu.ts):
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
DropdownMenu:src/theme/dropdown-menu.ts#L37—data-highlighted:before:bg-elevated/50CommandPalette:src/theme/command-palette.ts#L125—data-highlighted:not-data-disabled:before:bg-elevated/50InputMenupresumably as well (shares the select theme)
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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