Comparison Table
Überblick
mmm-comparison-table stellt mehrere gleichartige Angebote untereinander zum Vergleich dar — Darlehensvarianten, Tarife, Preisstufen. Jede Zeile zeigt in der Kopfzeile die Kennzahlen und klappt auf Klick ein Detail-Panel mit weiteren Werten und Fußnoten auf.
Die Komponente besteht aus fünf Teilen: der Tabelle selbst als <ul>, den Zeilen als <li> sowie den inhaltlichen Bausteinen Segment, Detail und Note. Die Zeile ist inhaltsunabhängig — sie kennt nur Chevron, Aufklappverhalten und Trennlinien, nicht die Daten, die projiziert werden.
Einbindung
Die Konstante COMPARISON_TABLE bündelt alle fünf Komponenten und kann in die imports gespreizt werden:
import { COMPARISON_TABLE } from '@mmm/designsystem';
@Component({ imports: [...COMPARISON_TABLE], // …})export class OfferListComponent {}Einzelne Klassen (ComparisonTableComponent, ComparisonTableRowComponent, ComparisonTableSegmentComponent, ComparisonTableDetailComponent, ComparisonTableNoteComponent) lassen sich alternativ gezielt importieren.
Subkomponenten
ul[mmmComparisonTable]: Tabellencontainer, hält die Design-Tokens und den Query-Containerli[mmmComparisonTableRow]: eine aufklappbare Zeile mit Trigger und Panelmmm-comparison-table-segment: beschriftete Zelle in der Kopfzeilemmm-comparison-table-detail: Label-Wert-Zeile im aufgeklappten Panelmmm-comparison-table-note: Fußnote am Fuß des Panels
Die Selektoren der ersten beiden sind Attribute auf echten <ul>- und <li>-Elementen, damit die Struktur ul > li ohne Wrapper-Element erhalten bleibt.
Slots einer Zeile
| Slot | Position | Zweck |
|---|---|---|
[comparisonTableLead] | im Trigger-Button, neben dem Chevron | hervorgehobene Leitzelle, üblicherweise ein Segment mit variant="lead" |
| Standard | zweite Zeile (schmal) bzw. neben dem Lead (breit) | die Segmente der Kopfzeile, beliebig viele |
[comparisonTableTrailing] | außerhalb des Trigger-Buttons, am Zeilenende | interaktive Inhalte wie Checkbox, Link oder Button |
[comparisonTableDetails] | aufklappbares Panel | mmm-comparison-table-detail und mmm-comparison-table-note |
[comparisonTableTrailing] liegt bewusst außerhalb des Buttons: ein Bedienelement innerhalb eines <button> ist ungültiges HTML und per Tastatur nicht erreichbar. Eine Auswahl-Spalte gehört deshalb immer in diesen Slot.
Auf mmm-comparison-table-segment existiert zusätzlich der Slot [comparisonTableSegmentLabel] für Labels, die kein reiner String sind.
Inputs
li[mmmComparisonTableRow]
| Input | Typ | Beschreibung |
|---|---|---|
expanded | boolean | Öffnungszustand der Zeile, zweiweg-bindbar über [(expanded)]; ohne Bindung verwaltet die Zeile ihn selbst |
triggerLabel | string | undefined | Überschreibt den Accessible Name des Triggers; ohne Wert benennt sich der Button über seinen Textinhalt |
mmm-comparison-table-segment
| Input | Typ | Beschreibung |
|---|---|---|
label | string | undefined | Label als reiner Text; entfällt, wenn [comparisonTableSegmentLabel] projiziert wird |
variant | 'default' | 'lead' | lead ist die vergrößerte, akzentfarbene Darstellung der Leitzelle |
align | 'left' | 'center' | 'right' | Textausrichtung der Zelle |
divider | boolean | undefined | Vertikale Haarlinie vor der Zelle; ohne Angabe abgeleitet aus variant (alles außer lead) |
grow | boolean | undefined | Verteilt den verfügbaren Platz gleichmäßig; ohne Angabe abgeleitet aus variant (alles außer lead) |
mmm-comparison-table-detail
| Input | Typ | Beschreibung |
|---|---|---|
label | string | Pflicht. Bezeichnung links in der Detailzeile |
ul[mmmComparisonTable] und mmm-comparison-table-note haben keine Inputs.
Outputs
li[mmmComparisonTableRow]
| Output | Typ | Beschreibung |
|---|---|---|
expandedChange | EventEmitter<boolean> | Meldet den geänderten Öffnungszustand |
Die übrigen Komponenten des Sets haben keine Outputs.
Responsives Verhalten
Die Zeile hat zwei Layouts, und umgeschaltet wird über eine Container-Query auf der Tabelle, nicht über eine Media-Query auf dem Viewport. Ausschlaggebend ist die Breite der Tabelle selbst: in einer Sidebar, einem Modal oder einer Spalte eines Split-Layouts ist sie auch auf großen Bildschirmen schmal.
- Schmal (Standard): zwei Zeilen — Lead und Trailing oben, darunter die Segmente in gleich breiten Spalten, getrennt durch eine Linie über die volle Breite.
- Breit (ab
37.5remInline-Größe der Tabelle): alles auf einer Linie — Lead, Segmente, Trailing am Zeilenende.
Drei Metriken sind dabei responsiv und liegen als -narrow/-wide-Paar vor: gap, rule-height und lead-size. Aufgelöst werden sie auf der Zeile, weil ein Element seinen eigenen Query-Container nicht abfragen kann.
Zum Übersteuern dieser drei immer die Inputs setzen (--mmm-comparison-table-gap-narrow bzw. -wide), nicht die aufgelöste Variable. Ein Wert direkt auf --mmm-comparison-table-gap wirkt nur, solange die Tabelle schmal ist.
Der Umschaltpunkt selbst ist die Sass-Variable $wide-min-width in styling/components/_comparison-table.scss. Container-Queries können keine Custom Properties lesen, der Wert wird also zur Build-Zeit eingesetzt und lässt sich nur per @use … with (…) aus einem Build heraus ändern, der das Partial selbst kompiliert.
SCSS-Variablen
Alle --mmm-comparison-table-*-Variablen werden auf dem <ul> deklariert; Zeilen, Segmente, Details und Notes erben sie. Ein ganzes Tabellen-Theme entsteht daher durch Übersteuern auf dem <ul>, einem Wrapper oder :root.
Farben:
--mmm-comparison-table-accent--mmm-comparison-table-text--mmm-comparison-table-label--mmm-comparison-table-divider--mmm-comparison-table-row-border-color--mmm-comparison-table-focus-color
Abstände und Maße:
--mmm-comparison-table-gap-narrow--mmm-comparison-table-gap-wide--mmm-comparison-table-rule-height-narrow--mmm-comparison-table-rule-height-wide--mmm-comparison-table-inset--mmm-comparison-table-row-padding--mmm-comparison-table-row-spacing--mmm-comparison-table-panel-offset--mmm-comparison-table-lead-gap--mmm-comparison-table-lead-padding--mmm-comparison-table-detail-padding
Typografie:
--mmm-comparison-table-label-size--mmm-comparison-table-label-line-height--mmm-comparison-table-value-size--mmm-comparison-table-value-line-height--mmm-comparison-table-lead-size-narrow--mmm-comparison-table-lead-size-wide--mmm-comparison-table-detail-size--mmm-comparison-table-note-size--mmm-comparison-table-note-line-height
Motion:
--mmm-comparison-table-duration
Hinweise
- Jede Zeile verwaltet ihren Zustand eigenständig, es können also mehrere Panels gleichzeitig offen sein. Für Akkordeon-Verhalten mit nur einem offenen Panel
[(expanded)]von außen steuern. - Trigger und Panel sind über
aria-expandedundaria-controlsverknüpft; das geschlossene Panel istinertund damit nicht fokussierbar. - Das Aufklappen animiert über
grid-template-rows: 0fr → 1fr, also ohne JavaScript-Messung. Beiprefers-reduced-motion: reduceentfallen Animation und Chevron-Rotation. - Ein projiziertes
[comparisonTableSegmentLabel]bringt keine Typografie mit. Für das Label-Aussehen zusätzlichclass="mmm-comparison-table-segment__label"setzen. dividerundgrowals Binding setzen ([divider]="false"), nicht als Attribut (divider="false") — sonst greift die Ableitung ausvariantnicht wie erwartet.- Segmente ohne Inhalt blenden sich aus: fehlen Segmente oder Trailing-Inhalte, entfallen auch die zugehörigen Trennlinien.
- Die Zeile bringt ihr Layout selbst mit und verwendet keine Grid-Klassen aus Bootstrap oder Quasar.
Beispiel
Comparison Table
Zwei Darlehensangebote im Vergleich, mit Auswahl-Checkbox und aufklappbaren Details.
Auswahl Nettodarlehensbetrag 200.000,00 EUR Anfängliche Tilgung 2,00 % p.a. Restschuld bei Zinsbindungsende 152.480,00 EUR Repräsentatives Beispiel: Nettodarlehensbetrag 200.000,00 EUR, gebundener Sollzins 3,34 % p.a., effektiver Jahreszins 3,39 %. Auswahl Nettodarlehensbetrag 200.000,00 EUR Anfängliche Tilgung 2,00 % p.a. Restschuld bei Zinsbindungsende 118.940,00 EUR Längere Zinsbindung, höhere Rate: die Restschuld am Ende der Bindung fällt entsprechend niedriger aus.
<ul mmmComparisonTable>
<li mmmComparisonTableRow [(expanded)]="firstExpanded">
<mmm-comparison-table-segment
comparisonTableLead
variant="lead"
label="Sollzinsbindung"
>
10 Jahre
</mmm-comparison-table-segment>
<mmm-comparison-table-segment label="Fester Sollzins">
3,34 % p.a.
</mmm-comparison-table-segment>
<mmm-comparison-table-segment label="Monatliche Rate">
605,00 EUR
</mmm-comparison-table-segment>
<mmm-comparison-table-segment
comparisonTableTrailing
label="Auswahl"
align="center"
>
<mmm-checkbox
ariaLabel="Angebot mit 10 Jahren Sollzinsbindung auswählen"
[checked]="selected() === '10-jahre'"
(checkedChange)="select('10-jahre', $event)"
></mmm-checkbox>
</mmm-comparison-table-segment>
<ng-container comparisonTableDetails>
<mmm-comparison-table-detail label="Nettodarlehensbetrag">
200.000,00 EUR
</mmm-comparison-table-detail>
<mmm-comparison-table-detail label="Anfängliche Tilgung">
2,00 % p.a.
</mmm-comparison-table-detail>
<mmm-comparison-table-detail label="Restschuld bei Zinsbindungsende">
152.480,00 EUR
</mmm-comparison-table-detail>
<mmm-comparison-table-note>
Repräsentatives Beispiel: Nettodarlehensbetrag 200.000,00 EUR,
gebundener Sollzins 3,34 % p.a., effektiver Jahreszins 3,39 %.
</mmm-comparison-table-note>
</ng-container>
</li>
<li mmmComparisonTableRow>
<mmm-comparison-table-segment
comparisonTableLead
variant="lead"
label="Sollzinsbindung"
>
15 Jahre
</mmm-comparison-table-segment>
<!-- Label als projizierte Markup-Variante statt als [label]-String -->
<mmm-comparison-table-segment>
<span
comparisonTableSegmentLabel
class="mmm-comparison-table-segment__label"
>
Eff. Jahreszins <abbr title="per annum">p.a.</abbr>
</span>
3,71 %
</mmm-comparison-table-segment>
<mmm-comparison-table-segment label="Monatliche Rate">
661,67 EUR
</mmm-comparison-table-segment>
<mmm-comparison-table-segment
comparisonTableTrailing
label="Auswahl"
align="center"
>
<mmm-checkbox
ariaLabel="Angebot mit 15 Jahren Sollzinsbindung auswählen"
[checked]="selected() === '15-jahre'"
(checkedChange)="select('15-jahre', $event)"
></mmm-checkbox>
</mmm-comparison-table-segment>
<ng-container comparisonTableDetails>
<mmm-comparison-table-detail label="Nettodarlehensbetrag">
200.000,00 EUR
</mmm-comparison-table-detail>
<mmm-comparison-table-detail label="Restschuld bei Zinsbindungsende">
118.940,00 EUR
</mmm-comparison-table-detail>
<mmm-comparison-table-note>
Längere Zinsbindung, höhere Rate: die Restschuld am Ende der Bindung
fällt entsprechend niedriger aus.
</mmm-comparison-table-note>
</ng-container>
</li>
</ul>
readonly firstExpanded = signal(true);
readonly selected = signal<string | null>('10-jahre');
protected select(id: string, checked: boolean): void {
this.selected.set(checked ? id : null);
}