C-52 · Components · Navigation
All elements · LanguageSelector on the Components page
Last checked 2026-10-22 · Awaiting approval · Accessibility tests
Approve LanguageSelector
Approving as
LanguageSelector
C-52WCAG 2.2 AA · Passed
Lets the user read the app in another language (USWDS Language selector). A button naming the current
language opens a Menu of languages, each
written in its own language and script with its lang, and glossed in the page's language;
an app with two languages uses a single link to the other one. UI only: choosing a language here changes
the button, not the page.
| Axis | Values |
|---|---|
| Form |
menu (__trigger + __menu) · toggle (__toggle, a link to
the other language)
|
__trigger › __current[lang] |
a C-01 Button with aria-haspopup="menu"; a visually hidden "Language:" and the
current language in its own name
|
__menu › .qf-menu__item[role="menuitemradio"][aria-checked] |
one item per language; the current one checked |
.qf-menu__label[lang] · [dir="rtl"] ·
.qf-menu__description
|
the language's own name, its lang (and dir for right-to-left scripts),
and its name in the page's language
|
__toggle[lang][hreflang] |
the other language's own name, as a link |
Guidance
When to use
- Let users switch the app's interface language when it is offered in more than one, for example to meet a limited-English-proficiency plan (Executive Order 13166).
- Use the menu form for three or more languages, and the two-language toggle when there are exactly two.
- Put it in the same place on every page, usually the shell's utility strip at the top end, so users who cannot read the current language can still find it.
When to consider something else
- When the choice is a per-document property, such as the language of a document's text, use a Select in its properties instead. See Select.
- When the setting is part of a user's profile with other preferences, put a Select on the profile page and keep this control for quick switching. See Select.
- When only one page or document is translated, link to its translation in the page instead of offering an app-wide switch.
Usability
- Write every language in its own language and script (Español, Tiếng Việt, 中文, العربية), so a reader of that language recognizes it.
- Add the name in the page's language as the item's description (Spanish), so others can tell what each one is.
- Show the current language on the button, in that language, and mark it checked in the menu.
- Keep the order stable: the page's language first, then the rest in a fixed order (by number of users, or alphabetically by English name).
- Use the
languageicon beside the name; it is recognized across languages.
- Don't use flags; a flag is a country, not a language.
- Don't translate the language names into the current language only; a Vietnamese reader looking for "Tiếng Việt" may not recognize "Vietnamese".
- Don't open the selector by hover alone.
- Don't hide it in a settings page; users who cannot read the current language cannot navigate there.
Accessibility
Keyboard
| Keys | Action |
|---|---|
| Enter, Space or Arrow Down | On the button, opens the menu and moves focus to the first language (as Menu). |
| Arrow Down / Arrow Up | Moves between languages, wrapping at the ends. |
| Home / End | Moves to the first or last language. |
| Enter or Space | Chooses the focused language, closes the menu and returns focus to the button. |
| Escape | Closes the menu without a change and returns focus to the button. |
| Tab | On the toggle, it is one link in the Tab order; Enter follows it. |
Roles and names
-
The button is a C-01 Button with
aria-haspopup="menu",aria-expandedandaria-controls; its name is a visually hidden "Language:" plus the current language, e.g. "Language: English". -
The menu is
role="menu"with anaria-label("Language"); each language is amenuitemradiowitharia-checked, exactly one checked. -
Each language's own name carries
lang(anddir="rtl"for Arabic, Hebrew, Persian and Urdu) so screen readers pronounce it in that language (3.1.2 Language of Parts); the trigger's current name carries the same. - The description in the page's language has no
langof its own. -
The toggle is a link (
a) withlangandhreflangset to the other language; it navigates, so it is not a button. -
Once the language changes, the app must update
lang(anddir) on<html>(3.1.1 Language of Page).
Focus
- Opening the menu moves focus into it; choosing or Escape returns focus to the button.
- The button, items and toggle show the shared focus ring.
Other requirements
-
Right-to-left names are isolated (
unicode-bidi: isolate), so they do not reorder the button's icon and chevron, and they line up with the other names in the menu. - The button is a full control height (2rem) and menu items at least 2rem tall, meeting 2.5.8.
- The fonts must cover every script offered; Avenir Next has no Chinese or Arabic glyphs, so those names fall back to the system font.
- This element is UI only in the design system: wiring it to translations, saving the choice and reloading strings is the app's job.
Test your implementation too. This guidance covers the element as specified here. How a screen composes it (labels, order, announcements) still needs its own keyboard and screen reader check.
Accessibility tests
Passed the gate (run 358b316 on 2026-10-22): no finding in any theme. Awaiting a person’s approval of the checklist below.
| Area | Automated | A person checks |
|---|---|---|
| General | Passed | Color is never the only cue, text alternatives say the same thing as the graphic, and the element reads the same in the light and the dark theme. |
| Zoom | Passed | At 200% zoom, at 320 CSS pixels wide and with the WCAG text-spacing overrides, nothing is lost, clipped or overlapping. |
| Keyboard | Passed | Every row of the Keyboard table works, focus is always visible and never trapped, and the tab order follows the reading order. (6 rows) |
| Screen reader | Passed | With NVDA or JAWS on Windows and VoiceOver on macOS, names, roles and states are announced as Roles and names says, and changes are announced once. (6 items) |
Approving. When the checklist passes on the checked run, record it with
bun run a11y:approve -- language-selector --by "Your name" and commit
a11y/approvals.json with the rebuilt pages.
All test status
Package
Consumers link one stylesheet, css/classic/index.css, which includes every element. The
files below are where this element is defined.
Latest updates
| Date | Version | Breaking | Description |
|---|---|---|---|
| 2026-10-20 | 0.1 | No |
LanguageSelector (C-52), UI only, modeled on the USWDS Language selector: a Button naming the
current language in that language (__current[lang], after a visually hidden
"Language:") opens a C-07 Menu of menuitemradio languages, each its own name with
lang (and dir="rtl" for Arabic) glossed in the page's language, the
current one checked; a two-language app uses __toggle, a link in the other language
with lang and hreflang. Keyboard is the Menu's. Adds the Tabler
language glyph to the sprite (components/language-selector.css)
|