Files
Styleguide/components/interactive-elements.html
T

1131 lines
48 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Styleguide Interactive Elements</title>
<link rel="stylesheet" href="../styleguide.css">
</head>
<body>
<h1 class="sg-main-heading">Components Interactive Elements</h1>
<!-- Component: Link Buttons -->
<section id="component-link-buttons">
<p class="sg-preview-label">Component: Link-Buttons</p>
<div class="sg-component-row">
<button class="sg-interaction-element sg-button sg-button--inactive" type="button" data-component="button" data-component-state="inactive" disabled aria-disabled="true">
Link-Button inaktiv
</button>
<button class="sg-interaction-element sg-button sg-button--active" type="button" data-component="button" data-component-state="active">
Link-Button aktiv
</button>
</div>
</section>
<!-- Component: Prozessbutton -->
<section id="component-process-button">
<p class="sg-preview-label">Component: Prozessbutton</p>
<div class="sg-component-row">
<button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="button" data-component="button" data-component-variant="process" data-component-state="inactive" disabled aria-disabled="true">
Prozess Button inaktiv
</button>
<button class="sg-interaction-element sg-button sg-button--process" type="button" data-component="button" data-component-variant="process" data-component-state="active">
Prozess Button
</button>
</div>
</section>
<!-- Component: Button Tab Navigation -->
<section id="component-button-tab-navigation">
<p class="sg-preview-label">Component: Tasten-Navigation-gross</p>
<div class="sg-tab-button-group" role="tablist" aria-label="Tasten Navigation" data-component="tab-navigation" data-component-size="large">
<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Gesamtbewertung</button>
<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="true" data-component-part="tab-button">Marktbewertung</button>
<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Wachstum</button>
<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Profitabilität</button>
<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Stabilität</button>
</div>
</section>
<!-- Component: Compact Button Tab Navigation -->
<section id="component-button-tab-navigation-compact">
<p class="sg-preview-label">Component: Tasten-Navigation-schmal</p>
<div class="sg-tab-button-group" role="tablist" aria-label="Schmale Tasten Navigation" data-component="tab-navigation" data-component-size="compact">
<button class="sg-interaction-element sg-button sg-tab-button sg-tab-button--compact" type="button" role="tab" aria-selected="true" data-component-part="tab-button">PE</button>
<button class="sg-interaction-element sg-button sg-tab-button sg-tab-button--compact" type="button" role="tab" aria-selected="false" data-component-part="tab-button">PE Forward</button>
<button class="sg-interaction-element sg-button sg-tab-button sg-tab-button--compact" type="button" role="tab" aria-selected="false" data-component-part="tab-button">PEG</button>
<button class="sg-interaction-element sg-button sg-tab-button sg-tab-button--compact" type="button" role="tab" aria-selected="false" data-component-part="tab-button">FCF Yield</button>
</div>
</section>
<!-- Component: Pulldown -->
<section id="component-pulldown">
<p class="sg-preview-label">Component: Pulldown</p>
<!--
Pulldown examples are shown on the form-area preview background.
This wrapper is only the evaluation surface; it is not part of the pulldown component.
State meaning:
- form-inactive-selectable: selectable but not currently active.
- form-active / selected: currently selected and opens the option panel.
- form-disabled: technically unavailable and cannot be operated.
-->
<div class="sg-form-preview-area">
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">Komponente normal</p>
<div class="sg-pulldown-demo" data-open="false" data-align="left" data-selection-mode="single" data-component="pulldown" data-component-state="inactive-selectable">
<!--
Inactive selectable pulldowns use the same overlay behavior as active pulldowns.
The option panel opens below the trigger and may be aligned left or right to remain visible inside the viewport.
-->
<button class="sg-interaction-element sg-pulldown sg-pulldown--inactive-selectable sg-pulldown-demo__trigger" type="button" aria-expanded="false" aria-label="Pulldown ohne aktive Auswahl" data-component-part="pulldown-trigger">
Auswahl
</button>
<div class="sg-pulldown-panel" aria-label="Geöffnetes inaktives Pulldown" data-component-part="pulldown-panel">
<ul class="sg-pulldown-option-list" aria-label="Verfügbare Optionen im inaktiven Pulldown">
<li class="sg-pulldown-option" role="checkbox" aria-checked="false" data-pulldown-option>
<span>Menüpunkt 1</span>
</li>
<li class="sg-pulldown-option" role="checkbox" aria-checked="false" data-pulldown-option>
<span>Menüpunkt 2</span>
</li>
<li class="sg-pulldown-option" role="checkbox" aria-checked="false" data-pulldown-option>
<span>Menüpunkt 3</span>
</li>
<li class="sg-pulldown-option" role="checkbox" aria-checked="false" data-pulldown-option>
<span>Menüpunkt 4</span>
</li>
<li class="sg-pulldown-option sg-pulldown-option--disabled">
<span>Menüpunkt 5</span>
</li>
</ul>
</div>
</div>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">Variante aktivierbar</p>
<div class="sg-pulldown-demo" data-open="false" data-align="left" data-selection-mode="multiple" data-component="pulldown" data-component-state="inactive-selectable" data-activatable="true">
<!--
The number in brackets shows the count of currently selected options inside this pulldown.
Initial state: no option is selected, therefore the trigger starts as inactive selectable.
Clicking an option activates the pulldown and shows the selected count in brackets.
-->
<span class="sg-activatable-control">
<button class="sg-interaction-element sg-pulldown sg-pulldown--inactive-selectable sg-pulldown-demo__trigger" type="button" aria-expanded="false" aria-label="Pulldown ohne aktive Auswahl" data-label-base="Auswahl" data-component-part="pulldown-trigger">
Auswahl
</button>
<button class="sg-pulldown-panel__remove" type="button" aria-label="Pulldown-Filter entfernen" data-pulldown-activate-remove hidden>×</button>
</span>
<div class="sg-pulldown-panel" aria-label="Geöffnetes Pulldown" data-component-part="pulldown-panel">
<!--
Interactive styleguide variant:
The panel opens and closes via the pulldown button above.
It closes when clicking outside, when another pulldown opens, or when a sandwich menu opens.
If the panel would overflow the viewport, it aligns from the opposite side of the trigger.
Cross-dependencies between pulldowns are application logic and are not defined in this component.
-->
<!--
Inactive filter rows:
These rows are intentionally dimmed with disabled-opacity although they are still operable.
They are not HTML-disabled.
First click on the row or change in the select activates the row and keeps the preselected value.
The remove button returns the row to the inactive preselection state.
This is a component-specific inactive state, not the same as form-disabled.
-->
<div class="sg-pulldown-panel__row sg-pulldown-panel__row--disabled" data-pulldown-filter-row data-active="false" data-component-part="pulldown-filter-row">
<p class="sg-pulldown-panel__label sg-body">Gesamtscore</p>
<select class="sg-interaction-element sg-pulldown" aria-label="Inaktiver Filter">
<option selected>Min +10%</option>
<option>Min +20%</option>
<option>Min +30%</option>
</select>
<button class="sg-pulldown-panel__remove" type="button" aria-label="Filter entfernen" hidden>
×
</button>
</div>
<div class="sg-pulldown-panel__row sg-pulldown-panel__row--disabled" data-pulldown-filter-row data-active="false" data-component-part="pulldown-filter-row">
<p class="sg-pulldown-panel__label sg-body">Aktienbewertung</p>
<select class="sg-interaction-element sg-pulldown" aria-label="Inaktiver Filter">
<option selected>Min +10%</option>
<option>Min +20%</option>
<option>Min +30%</option>
</select>
<button class="sg-pulldown-panel__remove" type="button" aria-label="Filter entfernen" hidden>
×
</button>
</div>
<div class="sg-checkbox-field-list sg-pulldown-panel__checkbox-list" aria-label="Checkboxen im Pulldown">
<label class="sg-checkbox-field-option sg-body" data-component="checkbox-field" data-component-state="inactive-selectable">
<button class="sg-checkbox-field sg-checkbox-field--on-grey sg-checkbox-field--inactive-selectable" type="button" role="checkbox" aria-checked="false" aria-label="Checkbox 1" data-pulldown-option>
<span class="sg-checkbox-field__mark" aria-hidden="true"></span>
</button>
<span>Checkbox 1</span>
</label>
<label class="sg-checkbox-field-option sg-body" data-component="checkbox-field" data-component-state="inactive-selectable">
<button class="sg-checkbox-field sg-checkbox-field--on-grey sg-checkbox-field--inactive-selectable" type="button" role="checkbox" aria-checked="false" aria-label="Checkbox 2" data-pulldown-option>
<span class="sg-checkbox-field__mark" aria-hidden="true"></span>
</button>
<span>Checkbox 2</span>
</label>
<label class="sg-checkbox-field-option sg-body" data-component="checkbox-field" data-component-state="inactive-selectable">
<button class="sg-checkbox-field sg-checkbox-field--on-grey sg-checkbox-field--inactive-selectable" type="button" role="checkbox" aria-checked="false" aria-label="Checkbox 3" data-pulldown-option>
<span class="sg-checkbox-field__mark" aria-hidden="true"></span>
</button>
<span>Checkbox 3</span>
</label>
</div>
</div>
</div>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-disabled</p>
<select class="sg-interaction-element sg-pulldown sg-pulldown--disabled" aria-label="Deaktivierter Pulldown" disabled data-component="pulldown" data-component-state="disabled">
<option>Nicht verfügbar</option>
</select>
</div>
</div>
</section>
<!-- Component: Search Field -->
<section id="component-input-single-line" class="sg-input-component">
<p class="sg-preview-label">Component: Suchfeld</p>
<!--
Input examples are shown on the form-area preview background.
This wrapper is only the evaluation surface; it is not part of the input component.
State meaning:
- form-inactive-selectable: empty, selectable input with placeholder text.
- form-active / selected: input with current value and visible remove button.
- form-disabled: unavailable input; no value can be entered and no remove button is shown.
-->
<div class="sg-form-preview-area">
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-inactive-selectable</p>
<span class="sg-search-field-row">
<span class="sg-input-single-line-wrap" data-has-value="false" data-component="single-line-input" data-component-state="inactive-selectable">
<input
class="sg-interaction-element sg-input-single-line sg-search-field-input sg-input-single-line--inactive-selectable sg-form-inactive-selectable"
type="text"
placeholder="Suchfeld"
aria-label="Suchfeld ohne Eingabe"
>
<button class="sg-input-clear-button" type="button" aria-label="Eingabe löschen">×</button>
</span>
<span class="sg-search-result-count">0 Treffer</span>
</span>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-active / selected</p>
<span class="sg-search-field-row">
<span class="sg-input-single-line-wrap" data-has-value="true" data-component="single-line-input" data-component-state="active">
<input
class="sg-interaction-element sg-input-single-line sg-search-field-input sg-form-active"
type="text"
value="Produkt"
aria-label="Suchfeld mit Eingabe"
>
<button class="sg-input-clear-button" type="button" aria-label="Eingabe löschen">×</button>
</span>
<span class="sg-search-result-count">12 Treffer</span>
</span>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-disabled</p>
<span class="sg-search-field-row">
<input
class="sg-interaction-element sg-input-single-line sg-search-field-input sg-input-single-line--disabled sg-form-disabled"
type="text"
value="Nicht verfügbar"
aria-label="Deaktiviertes Suchfeld"
disabled
data-component="single-line-input"
data-component-state="disabled"
>
</span>
</div>
</div>
</section>
<!-- Component: Single Line Input -->
<section id="component-input-field-single-line" class="sg-input-component">
<p class="sg-preview-label">Component: Eingabefeld einzeilig</p>
<div class="sg-form-preview-area">
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-inactive-selectable</p>
<label class="sg-labeled-input-row">
<span class="sg-label">Label</span>
<input
class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable"
type="text"
placeholder="Einzeiliges Eingabefeld"
aria-label="Einzeiliges Eingabefeld ohne Eingabe"
>
</label>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-active / selected</p>
<label class="sg-labeled-input-row">
<span class="sg-label">Label</span>
<input
class="sg-interaction-element sg-input-single-line sg-form-active"
type="text"
value="Aktive Eingabe"
aria-label="Einzeiliges Eingabefeld mit aktiver Eingabe"
>
</label>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-disabled</p>
<label class="sg-labeled-input-row">
<span class="sg-label">Label</span>
<input
class="sg-interaction-element sg-input-single-line sg-input-single-line--disabled sg-form-disabled"
type="text"
value="Nicht verfügbar"
aria-label="Deaktiviertes einzeiliges Eingabefeld"
disabled
>
</label>
</div>
</div>
</section>
<!-- Component: Multi Line Input -->
<section id="component-input-field-multi-line" class="sg-input-component">
<p class="sg-preview-label">Component: Eingabefeld mehrzeilig</p>
<div class="sg-form-preview-area">
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-inactive-selectable</p>
<label class="sg-labeled-input-row">
<span class="sg-label">Label</span>
<textarea
class="sg-input-multi-line sg-form-inactive-selectable"
rows="3"
placeholder="Mehrzeiliges Eingabefeld"
aria-label="Mehrzeiliges Eingabefeld ohne Eingabe"
></textarea>
</label>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-active / selected</p>
<label class="sg-labeled-input-row">
<span class="sg-label">Label</span>
<textarea
class="sg-input-multi-line sg-form-active"
rows="3"
aria-label="Mehrzeiliges Eingabefeld mit aktiver Eingabe"
>Aktive Eingabe über mehrere Zeilen</textarea>
</label>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">form-disabled</p>
<label class="sg-labeled-input-row">
<span class="sg-label">Label</span>
<textarea
class="sg-input-multi-line sg-form-disabled"
rows="3"
aria-label="Deaktiviertes mehrzeiliges Eingabefeld"
disabled
>Nicht verfügbar</textarea>
</label>
</div>
</div>
</section>
<!-- Component: Checkbox Field -->
<section id="component-checkbox-field">
<p class="sg-preview-label">Component: Kreuzchenfeld / Multiple Choice</p>
<!--
Checkbox examples are shown on the form-area preview background.
This wrapper is only the evaluation surface; it is not part of the checkbox component.
State meaning:
- form-inactive-selectable: not checked, but selectable.
- form-active / selected: checked and currently selected.
- form-disabled: technically unavailable and cannot be toggled.
-->
<div class="sg-form-preview-area sg-checkbox-field-list">
<label class="sg-checkbox-field-option sg-body" data-component="checkbox-field" data-component-state="default">
<span class="sg-state-example__label sg-table-label">Komponente normal</span>
<button class="sg-checkbox-field" type="button" role="checkbox" aria-checked="false" aria-label="Standard Checkbox">
<span class="sg-checkbox-field__mark" aria-hidden="true"></span>
</button>
<span>Standard Checkbox</span>
</label>
<label class="sg-checkbox-field-option sg-checkbox-field-option--inactive-selectable sg-body" data-component="checkbox-field" data-component-state="inactive-selectable" data-activatable="true">
<span class="sg-state-example__label sg-table-label">Variante aktivierbar</span>
<button class="sg-checkbox-field sg-checkbox-field--inactive-selectable" type="button" role="checkbox" aria-checked="false" aria-label="Option wählen">
<span class="sg-checkbox-field__mark" aria-hidden="true"></span>
</button>
<span>Option wählbar</span>
<button class="sg-pulldown-panel__remove" type="button" aria-label="Checkbox entfernen" data-checkbox-activate-remove hidden>×</button>
</label>
<label class="sg-checkbox-field-option sg-checkbox-field-option--disabled sg-body" data-component="checkbox-field" data-component-state="disabled">
<span class="sg-state-example__label sg-table-label">form-disabled</span>
<button class="sg-checkbox-field sg-checkbox-field--disabled" type="button" role="checkbox" aria-checked="false" aria-label="Option nicht verfügbar" disabled>
<span class="sg-checkbox-field__mark" aria-hidden="true"></span>
</button>
<span>Option nicht verfügbar</span>
</label>
</div>
</section>
<!-- Component: Radio Field -->
<section id="component-radio-field">
<p class="sg-preview-label">Component: Radio Button / Single Choice</p>
<!--
Radio examples are shown on the form-area preview background.
This wrapper is only the evaluation surface; it is not part of the radio component.
State meaning:
- form-inactive-selectable: not selected, but selectable.
- form-active / selected: selected radio option.
- form-disabled: technically unavailable and cannot be selected.
-->
<div class="sg-form-preview-area sg-checkbox-field-list">
<label class="sg-checkbox-field-option sg-body" data-component="radio-field" data-component-state="default">
<span class="sg-state-example__label sg-table-label">Komponente normal</span>
<button class="sg-radio-field" type="button" role="radio" aria-checked="false" aria-label="Standard Radio">
<span class="sg-radio-field__mark" aria-hidden="true"></span>
</button>
<span>Standard Radio</span>
</label>
<label class="sg-checkbox-field-option sg-checkbox-field-option--inactive-selectable sg-body sg-radio-activatable-group" data-component="radio-field" data-component-state="inactive-selectable" data-activatable="true" data-activatable-radio-group="true">
<span class="sg-state-example__label sg-table-label">Variante aktivierbar</span>
<span class="sg-radio-activatable-group__choices">
<span class="sg-radio-activatable-group__choice">
<button class="sg-radio-field sg-radio-field--inactive-selectable" type="button" role="radio" aria-checked="false" aria-label="Option 1 wählen">
<span class="sg-radio-field__mark" aria-hidden="true"></span>
</button>
<span>Option 1</span>
</span>
<span class="sg-radio-activatable-group__choice">
<button class="sg-radio-field sg-radio-field--inactive-selectable" type="button" role="radio" aria-checked="false" aria-label="Option 2 wählen">
<span class="sg-radio-field__mark" aria-hidden="true"></span>
</button>
<span>Option 2</span>
</span>
</span>
<button class="sg-pulldown-panel__remove" type="button" aria-label="Radio-Auswahl entfernen" data-radio-activate-remove hidden>×</button>
</label>
<label class="sg-checkbox-field-option sg-checkbox-field-option--disabled sg-body" data-component="radio-field" data-component-state="disabled">
<span class="sg-state-example__label sg-table-label">form-disabled</span>
<button class="sg-radio-field sg-radio-field--disabled" type="button" role="radio" aria-checked="false" aria-label="Option nicht verfügbar" disabled>
<span class="sg-radio-field__mark" aria-hidden="true"></span>
</button>
<span>Option nicht verfügbar</span>
</label>
</div>
</section>
<!-- Component: Mode Toggle -->
<section id="component-mode-toggle">
<p class="sg-preview-label">Component: Modus Schieber global</p>
<button class="sg-mode-toggle" type="button" data-active="relative" aria-label="Modus Schieber global: relativ aktiv" data-component="mode-toggle" data-mode-label="Modus Schieber global">
<span class="sg-mode-toggle__label">absolut</span>
<span class="sg-mode-toggle__switch" aria-hidden="true" data-component-part="toggle-track">
<span class="sg-mode-toggle__handle" data-component-part="toggle-handle"></span>
</span>
<span class="sg-mode-toggle__label">relativ</span>
</button>
</section>
<!-- Component: Mode Toggle Local -->
<section id="component-mode-toggle-local">
<p class="sg-preview-label">Component: Modus Schieber lokal</p>
<button class="sg-mode-toggle sg-mode-toggle--local" type="button" data-active="relative" aria-label="Modus Schieber lokal: relativ aktiv" data-component="mode-toggle-local" data-mode-label="Modus Schieber lokal">
<span class="sg-mode-toggle__label">absolut</span>
<span class="sg-mode-toggle__switch" aria-hidden="true" data-component-part="toggle-track">
<span class="sg-mode-toggle__handle" data-component-part="toggle-handle"></span>
</span>
<span class="sg-mode-toggle__label">relativ</span>
</button>
</section>
<!-- Component: Slider -->
<section id="component-slider">
<p class="sg-preview-label">Component: Slider</p>
<div class="sg-form-preview-area">
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">Komponente normal</p>
<label class="sg-slider-row" data-component="slider" data-component-state="default">
<span class="sg-label">Wert</span>
<input
class="sg-interaction-element sg-slider"
type="range"
min="0"
max="10"
step="0.1"
value="6.5"
aria-label="Slider"
>
<output class="sg-slider-value sg-body" for="slider">6.5</output>
</label>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">Variante aktivierbar: inaktiv</p>
<label class="sg-slider-row sg-slider-row--inactive-selectable" data-component="slider" data-component-state="inactive-selectable">
<span class="sg-label">Wert</span>
<input
class="sg-interaction-element sg-slider sg-form-inactive-selectable"
type="range"
min="0"
max="10"
step="0.1"
value="2.0"
aria-label="Slider inaktiv auswählbar"
>
<output class="sg-slider-value sg-body" for="slider">2.0</output>
</label>
</div>
<div class="sg-state-example">
<p class="sg-state-example__label sg-table-label">Variante aktivierbar: aktiv</p>
<label class="sg-slider-row" data-component="slider" data-component-state="active">
<span class="sg-label">Wert</span>
<input
class="sg-interaction-element sg-slider sg-form-active"
type="range"
min="0"
max="10"
step="0.1"
value="6.5"
aria-label="Slider aktiv ausgewählt"
>
<output class="sg-slider-value sg-body" for="slider">6.5</output>
<button class="sg-pulldown-panel__remove" type="button" aria-label="Slider-Filter entfernen">×</button>
</label>
</div>
</div>
</section>
<!-- Component: Sandwich Menu Button -->
<section id="component-sandwich-menu-button">
<p class="sg-preview-label">Component: Sandwich-Menü-Button Portal (groß)</p>
<div class="sg-component-row">
<div class="sg-sandwich-menu-wrap" data-open="false" data-component="sandwich-menu" data-component-size="default">
<button class="sg-interaction-element sg-sandwich-button" type="button" aria-expanded="false" aria-label="Portal-Menü öffnen" data-component-part="sandwich-trigger">
<span class="sg-sandwich-button__icon" aria-hidden="true">
<span class="sg-sandwich-button__line"></span>
<span class="sg-sandwich-button__line"></span>
<span class="sg-sandwich-button__line"></span>
</span>
</button>
<div class="sg-sandwich-menu-panel" aria-label="Ausgeklapptes Portal-Menü" data-component-part="sandwich-panel">
<a class="sg-sandwich-menu-link sg-body" href="#">Menüpunkt</a>
<a class="sg-sandwich-menu-link sg-body" href="#">Menüpunkt</a>
<a class="sg-sandwich-menu-link sg-body" href="#">Menüpunkt</a>
</div>
</div>
</div>
<p class="sg-preview-label">Component: Sandwich-Menü-Button Objekt (klein)</p>
<div class="sg-component-row">
<div class="sg-sandwich-menu-wrap" data-open="false" data-component="sandwich-menu" data-component-size="small">
<button class="sg-interaction-element sg-sandwich-button sg-sandwich-button--small" type="button" aria-expanded="false" aria-label="Objekt-Menü öffnen" data-component-part="sandwich-trigger">
<span class="sg-sandwich-button__icon" aria-hidden="true">
<span class="sg-sandwich-button__line"></span>
<span class="sg-sandwich-button__line"></span>
<span class="sg-sandwich-button__line"></span>
</span>
</button>
<div class="sg-sandwich-menu-panel" aria-label="Ausgeklapptes Objekt-Menü" data-component-part="sandwich-panel">
<a class="sg-sandwich-menu-link sg-body" href="#">Menüpunkt</a>
<a class="sg-sandwich-menu-link sg-body" href="#">Menüpunkt</a>
<a class="sg-sandwich-menu-link sg-body" href="#">Menüpunkt</a>
</div>
</div>
</div>
</section>
<!-- Component: Help Icon -->
<section id="component-help-icon">
<p class="sg-preview-label">Component: Fragezeichen-Icon</p>
<span class="sg-help-icon-wrap" data-open="false" data-align="left" data-component="help-icon">
<button class="sg-help-icon" type="button" aria-expanded="false" aria-label="Hilfetext anzeigen" data-component-part="help-trigger">?</button>
<span class="sg-help-icon-panel sg-table-label" role="tooltip" data-component-part="help-panel">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.
</span>
</span>
</section>
<!-- Component: Hyperlink -->
<section id="component-hyperlink">
<p class="sg-preview-label">Component: Hyperlink</p>
<p class="sg-body">
Dies ist ein Text mit einem <a class="sg-hyperlink" href="#" data-component="hyperlink">Hyperlink</a>.
</p>
</section>
<script>
document.querySelectorAll('.sg-mode-toggle').forEach((toggle) => {
toggle.addEventListener('click', () => {
const nextState = toggle.dataset.active === 'relative' ? 'absolute' : 'relative';
const modeLabel = toggle.dataset.modeLabel || 'Modus Schieber';
toggle.dataset.active = nextState;
toggle.setAttribute(
'aria-label',
`${modeLabel}: ${nextState === 'relative' ? 'relativ' : 'absolut'} aktiv`
);
});
});
document.querySelectorAll('.sg-tab-button-group').forEach((group) => {
group.querySelectorAll('.sg-tab-button').forEach((button) => {
button.addEventListener('click', () => {
group.querySelectorAll('.sg-tab-button').forEach((otherButton) => {
otherButton.setAttribute('aria-selected', String(otherButton === button));
});
});
});
});
document.querySelectorAll('.sg-input-single-line-wrap').forEach((wrap) => {
const input = wrap.querySelector('.sg-input-single-line');
const clearButton = wrap.querySelector('.sg-input-clear-button');
const updateState = () => {
wrap.dataset.hasValue = String(input.value.length > 0);
};
input.addEventListener('input', updateState);
clearButton.addEventListener('click', () => {
if (input.disabled || clearButton.disabled) {
return;
}
input.value = '';
updateState();
input.focus();
});
updateState();
});
document.querySelectorAll('.sg-slider-row').forEach((row) => {
const slider = row.querySelector('.sg-slider');
const valueOutput = row.querySelector('.sg-slider-value');
if (!slider || !valueOutput) {
return;
}
const updateSliderState = () => {
const min = Number(slider.min || 0);
const max = Number(slider.max || 100);
const value = Number(slider.value || 0);
const denominator = max - min;
const progress = denominator > 0 ? ((value - min) / denominator) * 100 : 0;
slider.style.setProperty('--sg-slider-progress', `${progress}%`);
valueOutput.textContent = value.toFixed(1);
};
slider.addEventListener('input', updateSliderState);
updateSliderState();
});
document.querySelectorAll('.sg-checkbox-field').forEach((checkbox) => {
checkbox.addEventListener('click', () => {
if (checkbox.disabled) {
return;
}
const activatableOption = checkbox.closest('[data-component="checkbox-field"][data-activatable="true"]');
if (activatableOption) {
checkbox.setAttribute('aria-checked', 'true');
checkbox.classList.add('sg-form-active');
checkbox.classList.remove('sg-checkbox-field--inactive-selectable');
activatableOption.dataset.componentState = 'active';
const removeButton = activatableOption.querySelector('[data-checkbox-activate-remove]');
if (removeButton) {
removeButton.hidden = false;
}
return;
}
const nextState = checkbox.getAttribute('aria-checked') !== 'true';
checkbox.setAttribute('aria-checked', String(nextState));
const pulldownDemo = checkbox.closest('.sg-pulldown-demo');
if (pulldownDemo) {
updatePulldownSelectionState(pulldownDemo);
}
});
});
document.querySelectorAll('.sg-pulldown-option[data-pulldown-option]').forEach((option) => {
option.addEventListener('click', (event) => {
event.stopPropagation();
const pulldownDemo = option.closest('.sg-pulldown-demo');
if (!pulldownDemo) {
return;
}
const selectionMode = pulldownDemo.dataset.selectionMode || 'single';
if (selectionMode === 'multiple') {
const nextState = option.getAttribute('aria-checked') !== 'true';
option.setAttribute('aria-checked', String(nextState));
} else {
pulldownDemo.querySelectorAll('[data-pulldown-option]').forEach((otherOption) => {
otherOption.setAttribute('aria-checked', String(otherOption === option));
});
}
updatePulldownSelectionState(pulldownDemo);
if (selectionMode !== 'multiple') {
pulldownDemo.dataset.open = 'false';
const trigger = pulldownDemo.querySelector('.sg-pulldown-demo__trigger');
if (trigger) {
trigger.setAttribute('aria-expanded', 'false');
}
}
});
});
document.querySelectorAll('.sg-radio-field').forEach((radio) => {
radio.addEventListener('click', () => {
if (radio.disabled) {
return;
}
const activatableGroup = radio.closest('[data-activatable-radio-group="true"]');
if (activatableGroup) {
const radios = activatableGroup.querySelectorAll('.sg-radio-field');
const defaultRadio = radios[0];
const removeButton = activatableGroup.querySelector('[data-radio-activate-remove]');
if (activatableGroup.dataset.componentState === 'inactive-selectable' && defaultRadio) {
radios.forEach((otherRadio) => {
otherRadio.setAttribute('aria-checked', String(otherRadio === defaultRadio));
otherRadio.classList.remove('sg-radio-field--inactive-selectable');
otherRadio.classList.toggle('sg-form-active', otherRadio === defaultRadio);
});
activatableGroup.dataset.componentState = 'active';
if (removeButton) {
removeButton.hidden = false;
}
return;
}
radios.forEach((otherRadio) => {
otherRadio.setAttribute('aria-checked', String(otherRadio === radio));
otherRadio.classList.remove('sg-radio-field--inactive-selectable');
otherRadio.classList.toggle('sg-form-active', otherRadio === radio);
});
activatableGroup.dataset.componentState = 'active';
if (removeButton) {
removeButton.hidden = false;
}
return;
}
const group = radio.closest('.sg-form-preview-area');
if (!group) {
return;
}
group.querySelectorAll('.sg-radio-field').forEach((otherRadio) => {
otherRadio.setAttribute('aria-checked', String(otherRadio === radio));
});
});
});
const updatePulldownSelectionState = (demo) => {
const trigger = demo.querySelector('.sg-pulldown-demo__trigger');
const selectableOptions = demo.querySelectorAll('[data-pulldown-option]');
const activatableRemove = demo.querySelector('[data-pulldown-activate-remove]');
if (!trigger || selectableOptions.length === 0) {
return;
}
const selectedCount = Array.from(selectableOptions).filter((option) => {
return option.getAttribute('aria-checked') === 'true';
}).length;
selectableOptions.forEach((option) => {
const optionRow = option.closest('.sg-pulldown-option');
if (!optionRow) {
return;
}
optionRow.classList.toggle(
'sg-pulldown-option--selected',
option.getAttribute('aria-checked') === 'true'
);
});
const labelBase = trigger.dataset.labelBase || 'Auswahl';
trigger.textContent = selectedCount > 0 ? `${labelBase} (${selectedCount})` : labelBase;
trigger.classList.toggle('sg-pulldown--selected', selectedCount > 0);
trigger.classList.toggle('sg-form-active', selectedCount > 0);
trigger.classList.toggle('sg-pulldown--inactive-selectable', selectedCount === 0);
if (activatableRemove) {
activatableRemove.hidden = selectedCount === 0;
}
trigger.setAttribute(
'aria-label',
selectedCount > 0 ? 'Pulldown mit aktiver Auswahl' : 'Pulldown ohne aktive Auswahl'
);
};
document.querySelectorAll('.sg-pulldown-demo').forEach(updatePulldownSelectionState);
// Pulldown behavior: pulldown demos open their panel below the trigger.
// Opening one closes all sandwich menus and any other open pulldown demo.
// If the panel would overflow the viewport, it aligns from the opposite trigger edge.
document.querySelectorAll('.sg-pulldown-demo').forEach((demo) => {
const trigger = demo.querySelector('.sg-pulldown-demo__trigger');
if (!trigger) {
return;
}
trigger.addEventListener('click', (event) => {
event.stopPropagation();
const nextState = demo.dataset.open !== 'true';
if (demo.dataset.activatable === 'true') {
const activatableRemove = demo.querySelector('[data-pulldown-activate-remove]');
trigger.classList.add('sg-pulldown--selected', 'sg-form-active');
trigger.classList.remove('sg-pulldown--inactive-selectable');
trigger.setAttribute('aria-label', 'Pulldown mit aktiver Auswahl');
if (activatableRemove) {
activatableRemove.hidden = false;
}
}
document.querySelectorAll('.sg-pulldown-demo').forEach((otherDemo) => {
const otherTrigger = otherDemo.querySelector('.sg-pulldown-demo__trigger');
otherDemo.dataset.open = 'false';
if (otherTrigger) {
otherTrigger.setAttribute('aria-expanded', 'false');
}
});
document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-sandwich-button');
wrap.dataset.open = 'false';
if (button) {
button.setAttribute('aria-expanded', 'false');
}
});
demo.dataset.align = 'left';
demo.dataset.open = String(nextState);
trigger.setAttribute('aria-expanded', String(nextState));
if (!nextState) {
return;
}
const panel = demo.querySelector('.sg-pulldown-panel');
if (!panel) {
return;
}
const panelRect = panel.getBoundingClientRect();
if (panelRect.right > window.innerWidth) {
demo.dataset.align = 'right';
}
const alignedPanelRect = panel.getBoundingClientRect();
if (alignedPanelRect.left < 0) {
demo.dataset.align = 'left';
}
});
});
document.querySelectorAll('[data-pulldown-activate-remove]').forEach((removeButton) => {
removeButton.addEventListener('click', (event) => {
event.stopPropagation();
const demo = removeButton.closest('.sg-pulldown-demo');
const trigger = demo ? demo.querySelector('.sg-pulldown-demo__trigger') : null;
if (!demo || !trigger) {
return;
}
demo.querySelectorAll('[data-pulldown-option]').forEach((option) => {
option.setAttribute('aria-checked', 'false');
});
demo.dataset.componentState = 'inactive-selectable';
demo.dataset.open = 'false';
trigger.setAttribute('aria-expanded', 'false');
updatePulldownSelectionState(demo);
});
});
document.querySelectorAll('[data-checkbox-activate-remove]').forEach((removeButton) => {
removeButton.addEventListener('click', (event) => {
event.stopPropagation();
const option = removeButton.closest('[data-component="checkbox-field"][data-activatable="true"]');
const checkbox = option ? option.querySelector('.sg-checkbox-field') : null;
if (!option || !checkbox) {
return;
}
option.dataset.componentState = 'inactive-selectable';
checkbox.setAttribute('aria-checked', 'false');
checkbox.classList.remove('sg-form-active');
checkbox.classList.add('sg-checkbox-field--inactive-selectable');
removeButton.hidden = true;
});
});
document.querySelectorAll('[data-radio-activate-remove]').forEach((removeButton) => {
removeButton.addEventListener('click', (event) => {
event.stopPropagation();
const option = removeButton.closest('[data-activatable-radio-group="true"]');
const radios = option ? option.querySelectorAll('.sg-radio-field') : null;
if (!option || !radios || radios.length === 0) {
return;
}
option.dataset.componentState = 'inactive-selectable';
radios.forEach((radio) => {
radio.setAttribute('aria-checked', 'false');
radio.classList.remove('sg-form-active');
radio.classList.add('sg-radio-field--inactive-selectable');
});
removeButton.hidden = true;
});
});
// Filter rows inside the opened pulldown start as inactive preselected rows.
// They are visually dimmed but remain operable; clicking or changing them activates the row.
document.querySelectorAll('[data-pulldown-filter-row]').forEach((row) => {
const select = row.querySelector('.sg-pulldown');
const removeButton = row.querySelector('.sg-pulldown-panel__remove');
const updateFilterRowState = () => {
const isActive = row.dataset.active === 'true';
row.classList.toggle('sg-pulldown-panel__row--disabled', !isActive);
select.classList.toggle('sg-pulldown--selected', isActive);
select.classList.toggle('sg-pulldown--inactive-selectable', !isActive);
removeButton.hidden = !isActive;
};
row.addEventListener('click', (event) => {
if (event.target === removeButton) {
return;
}
if (row.dataset.active !== 'true') {
row.dataset.active = 'true';
updateFilterRowState();
}
});
select.addEventListener('change', () => {
row.dataset.active = 'true';
updateFilterRowState();
});
removeButton.addEventListener('click', () => {
row.dataset.active = 'false';
updateFilterRowState();
});
updateFilterRowState();
});
document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-sandwich-button');
const panel = wrap.querySelector('.sg-sandwich-menu-panel');
button.addEventListener('click', (event) => {
event.stopPropagation();
const nextState = wrap.dataset.open !== 'true';
document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((otherWrap) => {
const otherButton = otherWrap.querySelector('.sg-sandwich-button');
otherWrap.dataset.open = 'false';
if (otherButton) {
otherButton.setAttribute('aria-expanded', 'false');
}
});
document.querySelectorAll('.sg-pulldown-demo').forEach((demo) => {
const trigger = demo.querySelector('.sg-pulldown-demo__trigger');
demo.dataset.open = 'false';
if (trigger) {
trigger.setAttribute('aria-expanded', 'false');
}
});
wrap.dataset.open = String(nextState);
button.setAttribute('aria-expanded', String(nextState));
if (!nextState || !panel) {
return;
}
wrap.dataset.align = 'right';
const panelRect = panel.getBoundingClientRect();
if (panelRect.left < 0) {
wrap.dataset.align = 'left';
}
});
});
document.querySelectorAll('.sg-help-icon-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-help-icon');
const panel = wrap.querySelector('.sg-help-icon-panel');
if (!button || !panel) {
return;
}
button.addEventListener('click', (event) => {
event.stopPropagation();
const nextState = wrap.dataset.open !== 'true';
document.querySelectorAll('.sg-help-icon-wrap').forEach((otherWrap) => {
const otherButton = otherWrap.querySelector('.sg-help-icon');
otherWrap.dataset.open = 'false';
if (otherButton) {
otherButton.setAttribute('aria-expanded', 'false');
}
});
document.querySelectorAll('.sg-pulldown-demo').forEach((demo) => {
const trigger = demo.querySelector('.sg-pulldown-demo__trigger');
demo.dataset.open = 'false';
if (trigger) {
trigger.setAttribute('aria-expanded', 'false');
}
});
document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((menuWrap) => {
const menuButton = menuWrap.querySelector('.sg-sandwich-button');
menuWrap.dataset.open = 'false';
if (menuButton) {
menuButton.setAttribute('aria-expanded', 'false');
}
});
wrap.dataset.align = 'left';
wrap.dataset.open = String(nextState);
button.setAttribute('aria-expanded', String(nextState));
if (!nextState) {
return;
}
const panelRect = panel.getBoundingClientRect();
if (panelRect.right > window.innerWidth) {
wrap.dataset.align = 'right';
}
const alignedPanelRect = panel.getBoundingClientRect();
if (alignedPanelRect.left < 0) {
wrap.dataset.align = 'left';
}
});
});
// Global close behavior for overlay-style controls.
// A click outside sandwich menus and pulldown demos closes both overlay types.
document.addEventListener('click', (event) => {
if (
event.target.closest('.sg-sandwich-menu-wrap')
|| event.target.closest('.sg-pulldown-demo')
|| event.target.closest('.sg-help-icon-wrap')
) {
return;
}
document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-sandwich-button');
wrap.dataset.open = 'false';
if (button) {
button.setAttribute('aria-expanded', 'false');
}
});
document.querySelectorAll('.sg-pulldown-demo').forEach((demo) => {
const trigger = demo.querySelector('.sg-pulldown-demo__trigger');
demo.dataset.open = 'false';
if (trigger) {
trigger.setAttribute('aria-expanded', 'false');
}
});
document.querySelectorAll('.sg-help-icon-wrap').forEach((wrap) => {
const button = wrap.querySelector('.sg-help-icon');
wrap.dataset.open = 'false';
if (button) {
button.setAttribute('aria-expanded', 'false');
}
});
});
</script>
</body>
</html>