Sélecteur d’en-tête entre un contexte racine (un groupe, une organisation, tous les magasins) et l’un de ses membres, construit sur le menu : le déclencheur indique où vous êtes, la liste peut arriver après le premier rendu avec une ligne de chargement, un bouton facultatif ramène à la racine, et un événement change annulable permet à l’application de confirmer ou de refuser le changement.
context switcher, scope switcher, organization switcher, company switcher, workspace switcher, team switcher, tenant, multi-company, group view, app header, back office, erp, menuitemradio, async list
When to use
A back office moves between a consolidated group view and one company, with a quick way back to the group
An app header must show which organization, workspace or store the whole screen is scoped to
The list of companies or workspaces comes from an API after the page has rendered
The app must confirm or refuse a context change, for example while a form has unsaved edits
Avoid when
The list holds hundreds of entries that people need to search by typing → use Combobox instead
The trigger opens actions such as Rename or Delete rather than a choice of scope → use Menu instead
The choice is a form value submitted with other fields → use Select instead
Agent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["context-switcher"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Value of the active member. Empty, absent or equal to root-value: the root context is active. Setting it does not emit mv-change.
root-label
string
Name of the root context (e.g. the group). Without it there is no root entry and no back button: a plain member switcher.
root-value
string
""
Value that stands for the root context in value and in mv-change.
list-label
string
Heading above the members (overrides strings.list).
loading
boolean
The member list is being fetched: a busy row replaces it (aria-busy) and the trigger shows strings.loading if value is not known yet. Removing it while the menu is open announces the count.
back-button
boolean
Shows an icon button next to the trigger, while a member is active, that returns to the root context.
placement
top | bottom | left | right[-start|-end]
bottom-start
Menu position relative to the trigger (automatic flip/shift).
data-size
md | sm
md
sm: one-line trigger for dense toolbars (the kicker stays for screen readers).
data-variant
ghost | outline
ghost
outline: bordered trigger on a surface, for toolbars and filters.
data-context
root | item
Set by the component on itself: which kind of context is active (for styling the page around it).
Default texts, for translation: kickers ("Group view", "Company"), list heading, root hint ("All companies"), placeholder, loading, empty, loaded announcement ("Companies loaded: {count}") and back button name ("Back to {label}"). Assignable before the element is defined.
current
{ value, label, root }
The active context (read-only).
isOpen
boolean
Whether the menu is open (read-only).
Methods
Name
Description
show({ focus })
Opens the menu; focus = "first" | "last" | "panel".
hide({ focus })
Closes the menu; focus=true returns focus to the trigger.
Events
Name
Description
mv-change
The user picked another context (menu or back button). Cancelable: preventDefault keeps the current one. detail: { value, label, root, item, previous } (item is the member object, null for the root).
mv-open / mv-close
From the inner menu, when it opens and closes.
Content structure
Name
Description
<option>
One member each: value, label (or text), data-description, data-mark (badge text, initials by default), data-hue (badge hue 0-360, derived from the value by default), disabled. Never rendered; added, removed or changed later, they update the list.
Generated menu panel, member rows (role=menuitemradio) and the loading or empty row.
CSS variables
Name
Default
Description
--mv-context-switcher-max-width
18rem
Trigger maximum width; longer names are truncated with an ellipsis.
--mv-context-switcher-menu-width
16rem
Menu minimum width.
--mv-context-switcher-mark-size
1.75rem
Trigger badge size (1.25rem in data-size="sm").
Accessibility
APG Menu Button: the trigger is a native button with aria-haspopup=menu and aria-expanded, named by its visible text (kicker then the active context, e.g. “Company Halden UK”). The panel is a role=menu with two labelled groups (the root, then the members) of menuitemradio rows whose aria-checked follows the active context. Keyboard from the menu: Enter, Space or ↓ open on the first row, ↑ on the last; ↑ ↓ Home End move (wrapping), letters jump by name (accent-insensitive), Enter or Space switch, Escape closes and Tab closes and moves on, focus returns to the trigger, which then reads the new context. Disabled members stay visible with aria-disabled and are skipped. While loading, the list has aria-busy and a polite status announces “Loading companies…” on open and the count once the list arrives; if rows are replaced while focus is in them, focus moves to the checked or first row instead of being lost. The back button is a real button named “Back to Halden Group”; it is removed from the tab order while the root is active and focus moves to the trigger after it is used. Badges are decorative (aria-hidden), colors are never the only cue, forced colors use system colors. Known limits: no search field inside the menu (use combobox for very long lists), and the menu grows with the list up to the viewport height, then scrolls.