Skip to content

ContentSwitch

Überblick

In Figma wird eine neue Version genannt, jedoch ohne Referenz. Diese Komponente entspricht dem Stand vom 10.08.26 aus Figma; die kompakte Variante wurde am 28.08.26 nach „Content Switch Compact” ergänzt. Zustandsmatrix, Varianten und das responsive Verhalten wurden am 15.09.26 nachgezogen.

mmm-content-switch ist eine Komponente zum Umschalten zwischen Inhalten oder zur Navigation — „wie eine Radio-Group, nur hübscher”.

Die Komponente ist kein Formular-Control. Sie besitzt weder ControlValueAccessor noch eine Signal-Forms-Anbindung und rendert keine versteckten input-Elemente. Je nach Einsatzzweck rendert sie das semantisch passende Element und die dazugehörige ARIA-Struktur:

EinsatzzweckKonfigurationContainer-RolleItem-ElementZustand
Inhalte umschalten (eine Auswahl)Standardtablistbuttonaria-selected
Mehrfachauswahl (Filter, Optionen)mode="multi"groupbuttonaria-pressed
Navigation (Links)navigation + hrefnavigation + listaaria-current

Ein Content-Switch sollte entweder aus Links oder aus Buttons bestehen. Eine Mischung erzeugt eine Liste mit einem verirrten Button — dafür gibt es keine sinnvolle Screenreader-Ansage.

Zwischen Host und Items liegt immer ein Wrapper div.mmm-content-switch__list. Im Navigations-Modus trägt er role="list", sonst role="presentation" — er ist dort also nicht im Accessibility-Tree, die Buttons hängen für Screenreader weiterhin direkt an tablist bzw. group. Gebraucht wird er fürs Layout: Die Gruppe ist ein CSS-Container, und eine Container-Query kann das Element, das sie misst, nicht selbst umschalten (siehe Responsives Verhalten). Eigene Styles sollten Items daher mit mmm-content-switch-item ansprechen, nicht mit mmm-content-switch > *.

Zustände

ZustandDarstellung
DefaultWeißer Hintergrund, Rahmen in der Akzentfarbe, Text in grey2
HoverTransparente Akzent-Schicht über dem Item (--mmm-content-switch-hover-layer)
SelectedFüllung in der Akzentfarbe, Text in on-primary
Selected HoverZusätzliche dunkle Schicht über der Füllung (--mmm-content-switch-selected-hover-layer)
FocusRing innerhalb des Items; auf der Füllung wechselt er auf die Textfarbe
DisabledRahmen und Trennlinien auf 40 %, Text in grey3, keine Pointer-Events

Hover und Selected Hover sind transparente Schichten, keine Hintergrund-Wechsel. Dieselbe Regel funktioniert damit sowohl auf dem Standard-Hintergrund als auch auf der Füllung des ausgewählten Items.

Der Fokusring liegt bewusst innerhalb des Items statt als outline außen herum: Die Items stoßen ohne Abstand aneinander, ein äußerer Ring würde von den Nachbarn überlagert.

mmm-content-switch

Inputs

InputTypDefaultBeschreibung
labelstring (required)Barrierefreier Name der Gruppe bzw. des Navigations-Landmarks (aria-label)
valuestring | string[]''Ausgewählter Key. Two-Way-Binding via [(value)]. Bei mode="multi" ein Array
modesingle | multi'single'Auswahl-Semantik. single erzeugt eine Tab-Liste, multi eine Gruppe von Toggle-Buttons
navigationbooleanfalseRendert die Gruppe als Navigations-Landmark mit Liste. Für Items mit href zu verwenden
disabledbooleanfalseDeaktiviert alle Items
verticalbooleanfalseErzwingt vertikales Layout
horizontalbooleanfalseErzwingt horizontales Layout in jeder Breite — schaltet auch das automatische Stapeln enger Zeilen ab (siehe Responsives Verhalten)
compactbooleanfalseKompakte Darstellung als Segmented Control statt Karten (siehe Kompakt)
namestringgeneriertStabiles ID-Präfix der Gruppe

Outputs

OutputTypBeschreibung
changeCustomEvent<{ value: string | string[] }>Wird nur bei Nutzerinteraktion ausgelöst
valueChangestring | string[]Wird bei jeder Wertänderung ausgelöst, also auch bei programmatischem Setzen

mmm-content-switch-item

Inputs

InputTypDefaultBeschreibung
valuestring (required)Identität des Items innerhalb der Gruppe
disabledbooleanfalseDeaktiviert dieses Item
iconbooleanfalseIcon-Only-Darstellung (kompaktes Padding, größere Schrift)
asdiv | span | section | article'div'Tag des Content-Wrappers
controlsstringID des zugehörigen Panels (aria-controls) — nur im Tab-Modus relevant
hrefstring | (string | number)[]Aktiviert den Link-Modus. Siehe Navigation
queryParamsParamsQuery-Parameter des Links
fragmentstringURL-Fragment des Links
targetstringLink-Target. Umgeht bewusst den Router
relstringberechnetBei target="_blank" automatisch noopener noreferrer
activeMatchexact | prefix | IsActiveMatchOptions'exact'Wie der Link gegen die aktuelle URL gematcht wird
ariaCurrentpage | step | location | true'page'aria-current-Wert des aktiven Links (nur ohne Router relevant)

Outputs

OutputTypBeschreibung
itemClickMouseEventKlick auf ein Link-Item vor der Navigation. preventDefault() übernimmt die Navigation selbst

Öffentliche Properties

PropertyTypBeschreibung
tabIdstringID des Tab-Buttons — für aria-labelledby des zugehörigen role="tabpanel"

Slots

DirektiveLegacy-AlternativeBeschreibung
mmmTitleslot="title"Titelzeile des Items
mmmDescriptionslot="description"Beschreibungstext unter dem Titel
mmmIconslot="icon"Icon rechts neben dem Text

Kompakt

compact schaltet den Switch von der Karten- auf die Segmented-Control-Darstellung um (Figma: „Content Switch Compact”).

<mmm-content-switch compact label="Zeitraum" [(value)]="range">
<mmm-content-switch-item value="month"><span mmmTitle>Monat</span></mmm-content-switch-item>
<mmm-content-switch-item value="quarter"><span mmmTitle>Quartal</span></mmm-content-switch-item>
<mmm-content-switch-item value="year"><span mmmTitle>Jahr</span></mmm-content-switch-item>
</mmm-content-switch>

Unterschiede zur Standard-Darstellung:

AspektStandardKompakt
BreiteItems teilen sich die Zeile gleichmäßigItems richten sich nach ihrem Label, Gruppe schrumpft mit
LabelImmer fett, linksbündigNormal, zentriert — fett nur bei Auswahl
HöheErgibt sich aus dem Inhalt44 px
Icon48 px neben dem Text24 px; Icon-Only ergibt ein 44 px großes Quadrat
Langer TextUmbruchAbschneiden mit
MobilStapelt unterhalb von m (600 px) vertikalBleibt horizontal

compact sitzt bewusst auf der Gruppe, nicht auf dem Item: Die Größe ist eine Eigenschaft des gesamten Switches, und gemischte Größen innerhalb einer Zeile gibt es im Design nicht.

Kompakt ist für reine Labels gedacht — also nur mmmTitle, optional zusammen mit icon am Item für die Icon-Only-Darstellung. Ein zusätzlich projizierter mmmDescription wird nicht ausgeblendet (stillschweigend verworfener Inhalt wäre schlimmer als falsches Aussehen), macht das Item aber höher als im Design vorgesehen.

Icon-Only

icon am Item entfernt das Text-Padding und rendert einen quadratischen Button. Ein barrierefreier Name muss dann vom projizierten Inhalt kommen — ein Icon allein hat keinen.

<mmm-content-switch compact label="Ansicht" [(value)]="view">
<mmm-content-switch-item value="list" icon>
<mmm-icon mmmIcon aria-label="Liste">liste_24</mmm-icon>
</mmm-content-switch-item>
</mmm-content-switch>

Das projizierte Icon wird über --mmm-content-switch-icon-size vermaßt — die Regel setzt sowohl font-size (für Icon-Fonts und em-basierte SVGs) als auch --mmm-icon-size (für mmm-icon) auf diesen Wert.

Responsives Verhalten

Der Switch ist mobile-first aufgebaut: Ohne weitere Angabe stapeln sich die Items vertikal und werden ab dem Standard-Breakpoint m (600 px) nebeneinander gelegt. Die Media Query stammt aus dem viewport-Mixin (src/styling/layouts/_viewport.scss), verwendet also dieselben Breakpoints wie der Rest der Bibliothek.

Konfiguration< 600 px≥ 600 px
Standardvertikalhorizontal
horizontalhorizontalhorizontal
verticalvertikalvertikal
compacthorizontalhorizontal

Gestapelte Items nehmen ihre eigene Inhaltshöhe an; nebeneinander teilen sie sich die Zeile zu gleichen Teilen. Beides steuert die vererbte Property --mmm-content-switch-item-flex, die die Gruppe je Richtung setzt.

Automatisches Stapeln bei zu enger Zeile

Eine Karte braucht Platz für ihr Icon (48 px), den Abstand, das Innen-Padding und ein lesbares Label — zusammen rund 180 px. Wird eine Zeile schmaler als Anzahl Items × 180 px, stapelt der Switch von selbst vertikal, unabhängig vom Viewport. Vier Karten reichen dafür schon in einer bildschirmbreiten Gruppe aus; ohne diese Regel würde jedes Item unter die Breite seines eigenen Inhalts gedrückt und das Icon am rechten Rand vom overflow: hidden der Gruppe abgeschnitten.

Gemessen wird dabei die Breite der Gruppe selbst (CSS-Container-Query), nicht die des Fensters: Ein Switch in einer schmalen Seitenleiste stapelt also auch auf einem breiten Bildschirm.

ItemsStapelt unterhalb von
2360 px
3540 px
4720 px
5900 px
Anzahl × 180 px (bis 8 Items)

Ausgenommen sind:

FallWarum
horizontalAusdrücklicher Opt-out aus jedem responsiven Umbruch — die explizite Angabe schlägt die Heuristik
compactSegmente richten sich nach ihrem Label und werden nicht gequetscht
Icon-OnlyEin [icon]-Item ist ein ~40 px großes Quadrat; ein einziges davon nimmt die ganze Liste aus der Regel

Die Schwellen stecken in den Sass-Variablen $stack-item-min-width (11.25 rem) und $stack-max-items (8) in src/styling/components/_content-switch.scss. Container-Queries können keine Custom Properties lesen, die Werte sind daher nur beim Build über @use ... with (...) anpassbar, nicht zur Laufzeit über CSS.

Unabhängig davon bricht ein zu langes Wort im Titel um, statt das Icon aus der Karte zu schieben: Die Textspalte des Item-Grids ist minmax(0, 1fr). Das Icon kann damit in keiner Breite abgeschnitten werden — auch dann nicht, wenn horizontal gesetzt ist.

Browser ohne Container-Query-Unterstützung ignorieren die Regel und verhalten sich wie bisher (nur Breakpoint m). Es geht dabei nichts kaputt, die Items werden dort lediglich wieder eng.

vertical hat dabei das letzte Wort: Ein Switch mit vertical und horizontal stapelt: Ein Umbruch ist die verträglichere Auslegung einer widersprüchlichen Konfiguration.

Die Trennlinien zwischen den Items folgen der Richtung automatisch — sie werden über die vererbten Properties --mmm-content-switch-divider-block und --mmm-content-switch-divider-inline gesetzt, von denen die Gruppe je Richtung genau eine füllt.

Tastaturbedienung

ModusVerhalten
Tabs (single)Die gesamte Tab-Liste ist ein Tabstopp. / (bzw. / bei vertical) wechseln, Home/End springen ans Ende. Die Auswahl folgt dem Fokus
MehrfachauswahlJeder Button ist ein eigener Tabstopp, Leertaste/Enter schaltet um
NavigationJeder Link ist ein eigener Tabstopp, Enter folgt dem Link

Im Navigations-Modus wird href an RouterLink weitergereicht: interne Links navigieren ohne Full-Page-Reload, und RouterLinkActive markiert automatisch den aktiven Eintrag anhand der echten URL. Es sind keine Provider nötig.

Die Bibliothek benötigt dafür @angular/router als Peer-Dependency. Der Router wird optional injiziert — in Anwendungen ohne Routing rendert die Komponente stattdessen einfache Anker.

Erlaubte Pfade:

WertAuflösung
/reportsAbsolut, ausgehend vom App-Root
detail bzw. ./detailRelativ zur aktuellen Route
../geschwisterRelativ zur Elternroute
['/reports', id()]Command-Array für dynamische Segmente
https://…, mailto:…Extern — umgeht den Router, rendert einen reinen Anker

Query-Parameter und Fragmente dürfen nicht in den Pfad geschrieben werden (/reports?tab=1), da der Router sie in ein Pfadsegment kodiert. Stattdessen [queryParams] und [fragment] verwenden.

activeMatch="exact" markiert nur bei exakter Pfadgleichheit, activeMatch="prefix" auch bei Unterrouten (/reports bleibt auf /reports/42/detail aktiv). Beide Presets ignorieren Query-Parameter und Fragmente, damit ein Eintrag auch auf /reports?page=2 aktiv bleibt.

Nicht abgefangen werden — und damit wie bei jedem normalen Link — Klicks mit Modifier-Taste (Strg/Cmd/Shift/Alt, Mittelklick), externe URLs und Links mit target.

CSS-Variablen

Die Gruppe deklariert den kompletten --mmm-content-switch-*-Satz; Items erben ihn. Überschrieben wird er auf dem Element, einem Wrapper oder :root — die generierten Theme-Dateien setzen genau so die vier Basis-Farben.

Farben

VariableDefaultBeschreibung
--mmm-content-switch-default-background--mmm-greyscale-whiteHintergrund nicht ausgewählter Items
--mmm-content-switch-default-text--mmm-greyscale-grey2Textfarbe nicht ausgewählter Items
--mmm-content-switch-active-background--mmm-main-colors-primaryFüllung des ausgewählten Items, Rahmenfarbe
--mmm-content-switch-active-text--mmm-main-colors-on-primaryTextfarbe des ausgewählten Items
--mmm-content-switch-hover-layer--mmm-state-default-elements-hoverTransparente Hover-Schicht (nicht ausgewählt)
--mmm-content-switch-selected-hover-layer--mmm-state-primary-elements-hoverTransparente Hover-Schicht (ausgewählt)
--mmm-content-switch-disabled-text--mmm-color-fg-disabledTextfarbe deaktivierter Items
--mmm-content-switch-disabled-border-colorRahmenfarbe auf 40 %Rahmen und Trennlinien im deaktivierten Zustand
--mmm-content-switch-focus-ring-colorRahmenfarbe, ausgewählt: TextfarbeFarbe des Fokusrings
--mmm-content-switch-icon-fillcurrentColorFüllung des projizierten Icons

Maße

VariableStandardKompakt
--mmm-content-switch-border-width2px2px
--mmm-content-switch-border-radiusTheme-abhängigTheme-abhängig
--mmm-content-switch-padding-block1rem0.625rem
--mmm-content-switch-padding-inline1rem1rem
--mmm-content-switch-gap0.5rem0.5rem
--mmm-content-switch-icon-size3rem1.5rem
--mmm-content-switch-icon-only-size1.25rem1.5rem
--mmm-content-switch-focus-inset0.625rem0.25rem
--mmm-content-switch-focus-width1px1px

Die sichtbare Innenkante eines Items verteilt sich auf zwei Elemente, damit der Fokusring dazwischen liegen kann: padding − focus-inset − focus-width am Button, dann der Ring, dann focus-inset. Eine Variante muss deshalb nur diese drei Werte neu setzen, um das gesamte Item umzumessen.

Beispiel

Inhalt der Listenansicht

Ausgewählt: open