Language Selector
Language switcher with two-language toggle and multi-language dropdown modes.
Reference: USWDS documentation ↗
Variants
Multi Language
<flex-language-selector>
<button type="button" class="flex-language-selector__button" aria-expanded="false" aria-controls="lang-menu">Languages</button>
<ul class="flex-language-selector__menu" id="lang-menu" hidden="">
<li><a href="/es" class="flex-language-selector__link" lang="es">Español</a></li>
<li><a href="/fr" class="flex-language-selector__link" lang="fr">Français</a></li>
<li><a href="/zh" class="flex-language-selector__link" lang="zh">中文</a></li>
</ul>
</flex-language-selector>Two Language
<flex-language-selector data-variant="two"><a href="/es" class="flex-language-selector__link" lang="es">Espa\u00f1ol</a></flex-language-selector>Contract
Class mapping
| USWDS | Flex | Notes |
|---|---|---|
usa-language-container | <flex-language-selector> (custom element) | Container element |
usa-button (language trigger) | .flex-language-selector__button | Button to toggle language menu |
usa-language__submenu | .flex-language-selector__menu | Dropdown menu with language options |
usa-language__submenu-item a | .flex-language-selector__link | Individual language link |
usa-language (two-language) | flex-language-selector[data-variant="two"] | Two-language variant (simple link, no dropdown) |
Verified properties
displayfont-sizepositionIntentional differences
color: ours = var(--flex-blue-vivid-60), USWDS = #005ea2
We use a semantic token for primary link color; USWDS uses the exact primary value
font-size: ours = 0.87rem, USWDS = 16px (default)
Two-language variant uses compact sizing matching USWDS --small variant
font-weight: ours = 700, USWDS = 400
Two-language variant uses bold to emphasize the language toggle link
text-decoration: ours = none, USWDS = underline
We remove underline for cleaner appearance; underline appears on hover
display: ours = block, USWDS = inline
Block display gives consistent click target sizing within the flex container
Behavior promises
- ✓ Button click toggles menu visibility and aria-expanded
- ✓ Outside click closes menu
- ✓ Escape key closes menu and returns focus to button
- ✓ Two-language variant renders as simple link (no JS)
Source CSS
/* flex-language-selector — USWDS Language Selector conformance
Language switcher: two-language toggle or multi-language dropdown */
flex-language-selector {
display: flex;
align-items: center;
justify-content: flex-end;
position: relative;
&[data-variant="two"] {
display: inline-flex;
& .flex-language-selector__link {
padding: 0.25rem 0.5rem;
font-weight: 700;
font-size: 0.87rem;
}
}
}
/* --- Multi-language button --- */
.flex-language-selector__button {
display: inline-flex;
align-items: center;
gap: 0.25rem;
background-color: transparent;
border: 0;
color: var(--flex-blue-vivid-60);
font-size: 0.87rem;
font-weight: 700;
cursor: pointer;
padding: 0.5rem 0.75rem;
min-inline-size: 3rem;
block-size: 2rem;
text-decoration: none;
&:hover {
text-decoration: underline;
color: var(--flex-blue-warm-vivid-70);
}
&:focus {
outline: 0.25rem solid var(--flex-blue-vivid-40);
outline-offset: 0;
}
&::after {
content: "";
display: inline-block;
inline-size: 0.75rem;
block-size: 0.75rem;
background-color: currentColor;
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z'/%3E%3C/svg%3E");
mask-size: contain;
mask-repeat: no-repeat;
transition: transform 0.15s ease;
}
&[aria-expanded="true"]::after {
transform: rotate(180deg);
}
}
/* --- Dropdown menu --- */
.flex-language-selector__menu {
position: absolute;
inset-block-start: 100%;
inset-inline-end: 0;
z-index: 400;
list-style: none;
margin: 0;
padding-block: 0.25rem;
padding-inline: 0;
background-color: var(--flex-color-surface);
border: 1px solid var(--flex-gray-cool-10);
box-shadow: 0 0.25rem 0.5rem rgb(0 0 0 / 10%);
min-inline-size: 10rem;
& li {
margin: 0;
padding: 0;
}
}
/* --- Language links --- */
.flex-language-selector__link {
display: block;
padding: 0.5rem 1rem;
color: var(--flex-blue-vivid-60);
text-decoration: none;
font-size: 0.93rem;
white-space: nowrap;
&:hover {
text-decoration: underline;
color: var(--flex-blue-warm-vivid-70);
background-color: var(--flex-gray-5);
}
&:focus {
outline: 0.25rem solid var(--flex-blue-vivid-40);
outline-offset: -0.25rem;
}
&:visited {
color: var(--flex-violet-vivid-70);
}
}
A digital services project by Flexion