Theme Switcher
<al-theme-switcher><ALThemeSwitcher> REACTbetaReference generated from the custom elements manifest — 0 properties, 0 slots, 1 events.
CODE — HTML / REACT
Guidance
The control that changes the active brand and mode. It is a popover holding a menu of brand+mode entries, and its real job is to find the nearest <al-theme> ancestor and set brand and mode attributes on it — a scoped change, not a global one, so several themes can coexist in one document.
WHEN TO USE
- A product lets the user choose between light and dark, or between brands, and the choice should apply to a region of the page rather than to the document.
- You are demoing or documenting the system and want a visible way to switch brands without reloading.
- You need to react to the change —
onThemeSwitcherChangereports the resolved theme, the logo, and whether the change was applied scoped or through the deprecated global path.
WHEN NOT TO USE
- You want the theme applied and never changed by the user. Wrap the region in the theme host and set its attributes directly — the switcher is the UI for changing it, not the mechanism for applying it.USE THEME →
- You need a general-purpose menu of options anchored to a button. The switcher is a specific control with a fixed, data-driven brand list.USE POPOVER →
- The choice is any other binary setting. Nothing here generalises beyond brand and mode.USE TOGGLE →
DO
- Put an
<al-theme>ancestor above the switcher. That is the supported path: the switcher walks up to the nearest one and sets its attributes, which scopes the change instead of mutating the document. - Set
scopedOnlywhen you want the deprecated global stylesheet swap to never run, even if the theme host is missing. It turns a silent fallback into a no-op you can notice. - Read
scopedfrom theonThemeSwitcherChangedetail if you need to know which path ran. It istruewhen a theme ancestor was found and updated,falsewhen the legacy swap was used. - Add a brand by adding an entry to the component's brand table rather than by branching in consumer code — the list is data, and the component does not change when it grows.
DON’T
- Don't depend on the global swap. It removes and re-appends a
<style id="al-tokens-sheet">element in the document head, is marked deprecated with a removal target of 3.0.0, and exists only because one in-repo fixture still uses the switcher with no theme wrapper. - Don't put two switchers over the same theme host expecting them to stay in step. Each one sets attributes on the host; neither reads the other's state.
- Don't assume changing the theme re-renders the whole page cheaply when the page is server-rendered. Each themed host serialises its brand and mode blocks into its own declarative shadow DOM template, which is a real cost when many themed islands are rendered on the server.
- Don't expose a brand switch to end users when the brand is a property of the product rather than a preference. Mode is a preference; brand usually is not.
ACCESSIBILITY IN PRACTICE
- The switcher composes a button, a popover and a menu, so it inherits their keyboard behaviour rather than defining its own — the trigger is a real button and the entries are real menu items.
- A theme change is a visual change with no announcement. If the switch also changes meaning — a high-contrast mode, say — say so in text near the control, because nothing here tells a screen-reader user what changed.
- Both light and dark entries have to clear contrast on their own. A switcher is not a substitute for either mode being accessible.
CONTENT GUIDELINES
- Label the entries with what the user gets, not with the token layer behind it: "Dark", "Light", the brand's name.
- Keep the trigger's accessible name about the setting, not about the widget — "Change theme" rather than "Settings", which is what the gear icon alone implies.
WHERE THIS CAME FROM
libs/al-web-components/components/theme-switcher/theme-switcher.tsThe scoped path: the nearest theme ancestor is found and its brand and mode attributes are set, with no global mutation.libs/al-web-components/components/theme-switcher/theme-switcher.tsThe legacy document-level stylesheet swap, why it is still present, and its removal target.libs/al-web-components/components/theme-switcher/theme-switcher.tsThe change event, including the `scoped` flag that reports which of the two paths ran.MIGRATION.mdThe migration note covering the deprecated global swap and what replaces it..altitude/SSR.mdThe measured server-rendering cost of a themed host — every brand and mode block is serialised into each host's declarative shadow DOM template.
Each line above names a file and a literal string that must still appear in it.pnpm gate:guidance re-reads them from this built page, so guidance that has stopped being true fails a check rather than misleading you.
FIGMA PARITY
Deliberately not represented in Figma.
- DECIDED BY
- NEVER SYNCED
- FIGMA SIDE
- NEVER OBSERVED
- LAST CONFIRMED SYNC
- NEVER
- PUBLIC SURFACE
- 0 ATTR · 0 SLOT · 1 EVENT · 0 PART
The two sides have never been confirmed equal, so there is no baseline to compare against.
Owns no pixels — an al-button + al-popover + al-menu composition; a Figma set would duplicate three components (altitude-figma-sync: NOT_COMPONENTS).
ACCESSIBILITY
The CI gate disables color-contrast globally (libs/al-web-components/story-fixture/src/main.ts), so its result never reaches a test run. It is enabled for this measurement, and reported here whichever way it comes out.
NO CONTRAST VIOLATIONS ACROSS THIS COMPONENT’S 1 STORIES.
INSTALL
import '@southleft/al-web-components/components/theme-switcher';
import { ALThemeSwitcher } from '@southleft/al-react';API — 0 PROPS
THIS COMPONENT DECLARES NO PUBLIC PROPERTIES IN THE MANIFEST.
SLOTS
THIS COMPONENT DECLARES NO SLOTS IN THE MANIFEST.
EVENTS
PUBLIC METHODS
SOURCE — components/theme-switcher/theme-switcher.ts · Molecules/Theme Switcher