🇬🇧 EN

Glassmorphism UI Library

Vollständige CSS-Komponentenbibliothek im Glasmorphism-Stil – entwickelt von der Jungherz GmbH. Reines CSS, keine Abhängigkeiten, Dark & Light Mode.

v1.40.0 CSS only Dark + Light 38 Komponenten

Installation

Eine CSS-Datei einbinden – fertig. Optional ein Theme-Override für andere Markenfarben.

<!-- Basis-Library --> <link rel="stylesheet" href="glasskit.css"> <!-- Optional: Theme-Override --> <link rel="stylesheet" href="theme-override.css"> <!-- Dark Mode (Standard) --> <html data-theme="dark"> <!-- Light Mode --> <html data-theme="light">

Design Tokens

Alle visuellen Werte werden über CSS Custom Properties gesteuert.

Farbpalette

TokenDark-WertVerwendung
--gl-color-primary#f5a623Primärfarbe, Buttons, aktive Elemente
--gl-color-primary-dark#d4692aGradient-Endwert
--gl-color-primary-textvar(--gl-color-primary)Text und Linien-Icons in der Primärfarbe: Links in Prose, der aktive Tab, die primäre Modal-Aktion, Akzent-Listenzeilen, der Wert eines Reglers, das Akkordeon — seit 1.26.0, siehe Primärfarbe als Text
--gl-color-primary-on-glasscolor-mix(in srgb, var(--gl-color-primary-text) 60%, #ffffff)Die Primärfarbe als Text auf dem Glas einer Karte, eines Modals oder eines Sheets: Links, der Range-Wert, Akzentzeilen, die primäre Modal-Aktion (seit 1.35.0); im hellen Modus der Primärtext
--gl-focus-ringcolor-mix(in srgb, var(--gl-color-primary) 15%, #ffffff)Die Markierung der Tastatur, eine deckende Farbe; --gl-border-focus und --gl-shadow-focus nehmen sie. Im hellen Theme var(--gl-color-primary-on-surface) — seit 1.40.0, siehe Fokusmarkierungen
--gl-color-text#ffffffStandard-Textfarbe
--gl-color-text-mutedrgba(255,255,255,0.65)Sekundärer Text
--gl-color-success#34c759Erfolg-Status
--gl-color-error#ff3b30Fehler-Status
--gl-color-warning#ffcc00Warn-Status
--gl-color-{state}-surface, -border, -on-surfacecolor-mix(…)Getönte Fläche, Rand und Schrift eines Status-Badges, abgeleitet von der Statusfarbe — für success, error und seit 1.18.0 warning

Glasflächen

TokenWertVerwendung
--gl-surface-1rgba(…, 0.08)Subtilste Fläche (Status)
--gl-surface-2rgba(…, 0.10)Standard (Inputs, Cards)
--gl-surface-3rgba(…, 0.14)Nav-Pills, Badges
--gl-surface-4rgba(…, 0.16)Hover-States
--gl-surface-5rgba(…, 0.22)Starker Hover
--gl-surface-milk-strongrgba(…, 0.75)Secondary Button

Glanz

TokenWertVerwendung
--gl-gloss-cardlinear-gradient(90deg, …)Licht an der Oberkante von .glass-card--glow
--gl-gloss-modallinear-gradient(90deg, …)Dasselbe an .glass-modal
--gl-gloss-progresslinear-gradient(to bottom, …)Schimmer auf dem Fortschrittsbalken
--gl-range-thumb-border2px solid rgba(255,255,255,0.5)Rand des Range-Knopfs
--gl-gloss-dropletradial-gradient(…), radial-gradient(…)Glanzlicht und Leuchten im Tropfen der --droplet-Zeilen (seit 1.27.0)

Seit 1.24.0, in beiden Themes gleich. data-surface="solid" schaltet sie ab, zusammen mit dem Blur und dem Glanz von --gl-inset-*.

Spacing & Radii

TokenWertTokenWert
--gl-space-2xs4px--gl-radius-xs8px
--gl-space-xs8px--gl-radius-sm12px
--gl-space-sm12px--gl-radius-input14px
--gl-space-md16px--gl-radius-btn16px
--gl-space-lg20px--gl-radius-card24px
--gl-space-xl24px--gl-radius-full9999px
--gl-space-2xl32px--gl-radius-pill50%

Dichte

TokenStandardKompaktVerwendung
--gl-control-height52px40pxInput, Select, Suchfeld
--gl-control-padding-xnicht gesetzt → --gl-space-md--gl-space-smInnenabstand links und rechts von Input, Select, Textarea
--gl-btn-height56px40pxButton
--gl-btn-height-sm44px32pxButton --sm
--gl-btn-height-lg64px48pxButton --lg
--gl-textarea-min-height120px88pxTextarea
--gl-list-item-min-height56px48pxListenzeile
--gl-list-item-padding-ynicht gesetzt → --gl-space-md10pxListenzeile, oben und unten
--gl-toggle-width × -height52 × 30px44 × 26pxToggle-Spur
--gl-toggle-thumb-size22px18pxToggle-Knopf
--gl-check-size24px20pxCheckbox, Radio
--gl-modal-action-height52px44pxModal-Aktionen

Theming (Light / Dark)

Das Theme wird über data-theme am <html>-Element gesteuert.

// Theme per JavaScript wechseln: function toggleTheme() { const html = document.documentElement; const current = html.getAttribute('data-theme'); html.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark' ); }

Primärfarbe als Text

Die Primärfarbe hat zwei Aufgaben. Als Fläche trägt sie weiße Schrift: Buttons, der eingeschaltete Schalter, der aktuelle Schritt, der gewählte Tag. Als Schrift steht sie selbst auf der Seite oder dem Glas: Links in .glass-prose, Icon und Label des aktiven Tabs, die primäre Modal-Aktion, Akzent-Listenzeilen, der Wert eines Reglers, der Akkordeon-Auslöser unter dem Zeiger und der Chevron eines geöffneten Abschnitts. Seit 1.26.0 hat die zweite Aufgabe ein eigenes Token, --gl-color-primary-text. Setzt ein Projekt es nicht, ist es die Primärfarbe, und es ändert sich nichts.

Eine Marke, die ihre Primärfarbe abdunkelt, damit weiße Button-Schrift 4,5:1 erreicht, bekommt einen Ton, der für Schrift auf dunklem Hintergrund zu dunkel ist. Sie hellt die Schrift im dunklen Theme auf, und die Buttons behalten ihre Farbe:

:root, [data-theme="dark"] { --gl-color-primary-text: color-mix(in srgb, var(--gl-color-primary) 18%, #ffffff); } [data-theme="light"] { --gl-color-primary-text: var(--gl-color-primary); }

Die helle Regel steht nach der dunklen: :root trifft auch eine Seite im hellen Theme, und von zwei passenden Regeln gewinnt die spätere. Der richtige Ton hängt vom Hintergrund ab, also dort messen. Auf GlassKits eigenem dunklen Hintergrund mit #c25028 als Primärfarbe hebt diese Mischung einen Link auf einer Glas-Card von 1,37:1 auf 5,05:1.

Seit 1.35.0 nimmt Akzenttext auf dem Glas einer Karte, eines Modals oder eines Sheets --gl-color-primary-on-glass: im dunklen Modus den Primärtext mit 40 % Weiß, weil eine Karte heller ist als die Seite. GlassKits eigenes Orange las als Link auf einer Karte im dunklen Modus 4,18:1, jetzt 5,54:1; auf der Seite bleibt es bei 5,69:1. Im hellen Modus ist der Token der Primärtext. Links und Chips der Seite auf einer Karte nutzen ihn ebenso, color: var(--gl-color-primary-on-glass). Das helle obere Ende einer .glass-card--glow bleibt für Akzenttext außer Reichweite: Setze ihn tiefer in die Karte oder nimm dort die Textfarbe.

Der aktive Tab nimmt für Icon und Label --gl-tab-bar-active-color, wo das gesetzt ist, sonst --gl-color-primary-text. Das Spotlight der Floating-Bar hellt den aktiven Eintrag stärker auf als jede andere Fläche, eine Marke braucht dort also womöglich einen helleren Ton, bis hin zu Weiß. Es wird pro Theme gesetzt wie die Textfarbe; var(--gl-color-primary-text) in der hellen Regel stellt die Vorgabe wieder her. Leuchten und Punkt bleiben in der Primärfarbe, das Spotlight in --gl-tab-bar-spotlight-color.

Seit 1.31.0 erreichen gedämpfter Text, Kartentext und Icons von Haus aus WCAG AA, in beiden Themes. Die Primärfarbe als Text nicht, mit Absicht: GlassKits eigenes Orange liegt als Text auf der hellen Seite bei 2,43:1, eine dunklere Marke besteht aber womöglich schon oder braucht viel weniger, und eine feste Mischung mit Schwarz würde jede Marke gleich stark abdunkeln. Wo das helle Theme beim Orange bleibt, dunkle es dort ab; --gl-color-primary-on-surface, die Tinte des primären Badges, erreicht 5,8:1. Auch Weiß auf dem Primary-Button gehört zum Aussehen, 2,7:1 im hellen und 2,0:1 im dunklen Theme am hellsten Punkt des Verlaufs; --gl-color-on-primary stellt auf eine dunkle Tinte um, die auf jedem Punkt 4,6:1 oder mehr erreicht:

[data-theme="light"] { --gl-color-primary-text: var(--gl-color-primary-on-surface); } /* Dunkle Schrift auf dem Primary-Button, in beiden Themes */ :root, [data-theme="dark"], [data-theme="light"] { --gl-color-on-primary: color-mix(in srgb, var(--gl-color-primary) 17%, #000); }

Seit 1.32.0 haben beide Wege eigene Tokens. Wer weiße Schrift behalten will, dunkelt die Fläche ab und lässt die Markenfarbe in Ruhe, die auch Fokusringe, Rahmen, das Spotlight der Tab-Bar und den Akzent als Schrift zeichnet. --gl-color-primary-fill ist dabei die flache Akzentfläche, die Schrift trägt — der gewählte Tag von Wochenstreifen und Kalender, die Nummer des aktuellen Schritts —, ungesetzt die Primärfarbe; --gl-btn-primary-bg ist der ganze Hintergrund des Primary-Buttons, ungesetzt der bisherige Verlauf. Auf GlassKits eigenem Orange erreicht Weiß 4,5:1 ab 75 % Primärfarbe im hellen Theme und ab 65 % im dunklen, und der Verlauf unten bleibt auf jedem Punkt darüber:

/* Weiße Schrift auf dunklerer Fläche: Button, gewählte Tage, aktueller Schritt */ :root, [data-theme="dark"] { --gl-color-primary-fill: color-mix(in srgb, var(--gl-color-primary) 65%, #000); } [data-theme="light"] { --gl-color-primary-fill: color-mix(in srgb, var(--gl-color-primary) 75%, #000); } :root, [data-theme="dark"], [data-theme="light"] { --gl-btn-primary-bg: linear-gradient(135deg, var(--gl-color-primary-fill), color-mix(in srgb, var(--gl-color-primary-dark) 80%, #000)); }

Auf dem gewählten Tag des Wochenstreifens ist der Wochentag jetzt so kräftig wie die Zahl (seit 1.32.0); verblasst blieb er selbst auf einer Fläche unter 4,5:1, auf der die Zahl es erreicht.

Die dunkle Tinte erreicht jetzt auch das Radio: Sein Punkt folgt --gl-color-on-primary, solange --gl-radio-dot-color nicht gesetzt ist, wie das Häkchen der Checkbox (seit 1.32.0). Wo die Markenfarbe erst zur Laufzeit feststeht, kann der Browser die Tinte wählen: contrast-color() liefert Schwarz oder Weiß, je nachdem, was sich stärker von der Primärfarbe abhebt — für GlassKits Orange in beiden Themes Schwarz, auf jedem Punkt des Buttons 5,5:1 oder mehr. Die 17-%-Tinte bleibt der Rückfall, wo ein Browser es noch nicht kennt:

:root, [data-theme="dark"], [data-theme="light"] { --gl-color-on-primary: color-mix(in srgb, var(--gl-color-primary) 17%, #000); } @supports (color: contrast-color(red)) { :root, [data-theme="dark"], [data-theme="light"] { --gl-color-on-primary: contrast-color(var(--gl-color-primary)); } }

contrast-color() sieht nur die Primärfarbe an. Mit einer eigenen Fläche oder einem Verlauf, der deutlich dunkler endet, auch das andere Ende messen.

Fokusmarkierungen

Seit 1.40.0 zeigt jedes Bedienelement, wo die Tastatur steht, in einer deckenden Farbe: --gl-focus-ring. Drücke hier die Tab-Taste:

Buttons, Pills und der Theme-Schalter zeichnen sie als 2 px breite Outline mit 2 px Abstand, die Aktionen eines Modals innerhalb der Aktion, weil das Modal sie sonst abschneiden würde. Bis 1.39.0 hatten diese Teile gar keine Markierung. Alle anderen Teile nehmen den Ring aus --gl-shadow-focus oder --gl-border-focus, jetzt 2 px breit. Beide waren Tönungen der Primärfarbe und lagen unter den 3:1, die eine Fokusmarkierung gegen ihre Umgebung braucht (WCAG 1.4.11). Beide nehmen jetzt die deckende Farbe, und sie folgt der Marke:

/* Die Vorgaben */ :root, [data-theme="dark"] { --gl-focus-ring: color-mix(in srgb, var(--gl-color-primary) 15%, #ffffff); } [data-theme="light"] { --gl-focus-ring: var(--gl-color-primary-on-surface); }

GlassKits Orange erreicht 3:1 oder mehr auf der Seite und auf Glas. Wie hell Glas wird, entscheidet aber, was dahinter liegt, und eine dunklere Marke kann an der hellsten Stelle darunter bleiben: Glas auf einer Glow-Karte in der Seitenmitte, im Dark Mode. Eine eigene Farbe gehört in --gl-focus-ring: Halte sie deckend und miss sie dort. Ein Projekt, das --gl-border-focus oder --gl-shadow-focus selbst gesetzt hat, behält seinen Ring, bis es sie entfernt, und eine eigene Outline auf :focus-visible ist nicht mehr nötig.

In Forced Colors, dem Hochkontrastmodus von Windows, verwirft der Browser box-shadow und damit jeden Ring. Dort zeigt jedes Teil eine Outline in der Textfarbe, innerhalb der Zeilen von Listen, Akkordeons und Menüs, die abschneiden, was außerhalb liegt.

Rechts nach links

Seit 1.20.0 folgen die Komponenten der Schreibrichtung. dir="rtl" auf <html> oder einem beliebigen Container genügt: Abstände, Positionen und Rahmen sind logische Eigenschaften, also spiegeln sich Labels und Hinweise, das Such-Icon, die Trennlinien der Liste, die Popover-Anker, die Tab-Badges, die Modal-Aktionen, das × des Toasts und die Ausrichtung von Akkordeon, Liste, Tabelle und Prose. Wo zwei Werte gemeinsam wandern müssen — der Chevron des Selects und der Platz für ihn, der Knopf des Schalters und sein Weg, der Verlauf der Fortschrittsfüllung — steht die Regel unter :dir(rtl): Chrome und Edge ab 120, Safari ab 16.4, Firefox ab 49. Ein älterer Browser lässt diese Teile links nach rechts, statt sie auseinanderzureißen.

Drucken

Druckdialoge lassen Hintergrundgrafiken standardmäßig weg, und manche Zustände sind nur mit einem Hintergrund gezeichnet. Seit 1.21.1 behalten diese Teile auf Papier ihre Farben — das angehakte Kästchen und sein Haken, das gewählte Radio, die Spur des Toggles, die Füllung des Fortschrittsbalkens, der aktuelle Schritt, der gewählte Tag von Datumsstreifen und Kalender, der Knopf des Schiebereglers und die Tonpunkte —, der Rest der Seite druckt sparsam. Einzustellen ist nichts. Eine dunkle Seite druckt ihre helle Schrift und ihre Flächen auf weißes Papier; eine Seite, die gedruckt werden soll, wechselt dafür ins helle Theme:

let printTheme = null; addEventListener('beforeprint', () => { printTheme = document.documentElement.getAttribute('data-theme'); document.documentElement.setAttribute('data-theme', 'light'); }); addEventListener('afterprint', () => { if (printTheme) document.documentElement.setAttribute('data-theme', printTheme); else document.documentElement.removeAttribute('data-theme'); });

Dichte (Kompakt)

Admin-Oberflächen, Desktop-Layouts und schmale Formulare brauchen dichtere Bedienelemente. Seit 1.21.0 sind ihre Größen Dichte-Tokens, und data-density="compact" am <html>-Element setzt einen kleineren Satz: Felder und Buttons 40px hoch statt 52 und 56, kleinere Toggles, Checkboxen und Radios, dichtere Listenzeilen und Modal-Aktionen. Farben, Schrift und Abstände bleiben, wie sie sind. Ohne das Attribut behält jedes Element die Größe, die es immer hatte.

Standard
Kompakt

Die Vorschau setzt das Attribut auf ihre rechte Spalte. Auf einer Seite gehört es an <html>, neben data-theme: Ein Element weiter unten mit data-theme="dark" setzt die Standardgrößen wieder.

<html data-theme="dark" data-density="compact"> // Zur Laufzeit umschalten: document.documentElement.setAttribute('data-density', 'compact'); document.documentElement.removeAttribute('data-density'); /* Ein Token für beide Dichten – oder nur für kompakt: */ :root { --gl-btn-height: 48px; } [data-density='compact'] { --gl-btn-height: 36px; }

Einzug und Weg des Toggle-Knopfs — in beiden Schreibrichtungen — und die Ausrichtung von Toggle, Checkbox und Radio an der ersten Zeile leiten sich aus den Tokens ab. Jede Größe, die ein Projekt setzt, bleibt so ausgerichtet. Die Regeln lesen jede Größe mit dem bisherigen Wert als Fallback, wie --gl-modal-max-width: Eine Seite, deren Token-Sheet älter ist, behält die alten Größen.

Oberflächen (Solid, Tint)

Nicht jede Oberfläche soll Glas sein: Admin-Oberflächen, Formulare auf schlichtem Hintergrund, Designs ohne Glas. Seit 1.24.0 macht data-surface="solid" am <html>-Element jede Fläche deckend und schaltet Blur und Glanz ab. Jede Fläche nimmt die Farbe an, die das Glas über dem mittleren Hintergrund des Themes zeigt, --gl-color-bg-mid: Cards, Felder und Buttons behalten ihre Abstufung, und ein umgefärbter Hintergrund wirkt mit. Der innere Glanz (--gl-inset-*), das Licht an der Oberkante der Glow-Card und des Modals, der Schimmer auf dem Fortschrittsbalken und der Rand des Range-Knopfs sind aus. Ränder, Schlagschatten, die Tönungen von Badges und Chips und die abdunkelnde Überlagerung bleiben, wie sie sind. Ohne das Attribut ändert sich nichts.

Eine Seite mit eigenem Hintergrund — einer Markenfarbe, einem Foto, einem Muster — nimmt stattdessen data-surface="tint" (seit 1.25.0). Eine über --gl-color-bg-mid gemischte Fläche stünde dort als Fremdkörper, deshalb werden die Flächen im Fluss der Seite zu durchscheinenden Tönungen der Textfarbe, ohne Blur: Ein Feld auf einer Card bleibt so durchscheinend wie die Card, und sein Kontrast folgt dem Text. Was über anderem Inhalt liegt, behält die Solid-Werte — Modal, Sheet, Popover, Toast und die Tab-Bars samt der Felder und Zeilen darin —, ebenso der Secondary-Button, eine helle Fläche für dunkle Schrift. Blur und Glanz sind wie bei Solid aus; ein Blur hinter Dialogen bleibt, wo ein Projekt --gl-blur-overlay setzt.

Glas
Solid
Tint

Die Vorschau setzt das Attribut auf ihre zweite und dritte Card. Auf einer Seite gehört es an <html>, neben data-theme und data-density; ein Theme-Bereich weiter unten — ein Element mit eigenem data-theme — bleibt solid. Alles läuft über Tokens, ein Projekt passt das Preset also an wie ein Theme. GlassKit Elements trägt das Attribut in seine Shadow Roots, wie data-theme.

<html data-theme="dark" data-surface="solid"> // Zur Laufzeit umschalten: document.documentElement.setAttribute('data-surface', 'solid'); document.documentElement.removeAttribute('data-surface'); /* Anpassen wie ein Theme – hier eine dunklere Card-Fläche im Dark Mode: */ [data-surface='solid'] { --gl-surface-2: #17313d; } /* Beide Presets mischen ihre deckenden Flächen über --gl-color-bg-mid; die Paneele des Tint-Presets nehmen sie als --gl-solid-surface-1 … -5 und --gl-solid-card-glow-*. Eine Marke setzt die Mittelfarbe auf ihre eigene: */ [data-surface='tint'] { --gl-color-bg-mid: #5c1310; }

Auch der Glanz besteht jetzt aus Tokens: --gl-gloss-card, --gl-gloss-modal, --gl-gloss-progress und --gl-range-thumb-border (siehe Design Tokens). Vor 1.24.0 waren es feste Werte, die ein eigener Solid-Skin eines Projekts nicht erreichte.

Hintergrund

Der Glaseffekt braucht einen farbigen Hintergrund.

KlasseBeschreibung
.glass-bgVollständiger Hintergrund mit Aurora-Lichteffekten
.glass-bg--has-tab-barModifier: Padding unten für Tab-Bar
.glass-bg--has-tab-bar-floatingModifier: Padding unten für Floating-Tab-Bar + Accessory
<div class="glass-bg glass-bg--has-tab-bar"> <!-- Inhalte --> </div>

Pill-Buttons

Runde Icon-Buttons für Navigation und Aktionen.

KlasseBeschreibung
.glass-pillStandard Glas-Kreis (46×46px)
.glass-theme-togglePill mit Mond/Sonne-Icon-Switch

Tab-Bar

Fixierte Bottom-Navigation mit Glas-Hintergrund und optionalem Badge. Die Items sitzen in der Mitte der Leiste; auf einem iPhone mit viewport-fit=cover bleiben sie oben, über dem Home-Indikator (seit 1.28.0; vorher saßen sie überall oben, auf Android und am Desktop mit 26 px Leere darunter).

<nav class="glass-tab-bar"> <button class="glass-tab-bar__item is-active"> <span class="glass-tab-bar__icon"><!-- SVG --></span> <span class="glass-tab-bar__label">Start</span> </button> <button class="glass-tab-bar__item"> <span class="glass-tab-bar__icon"> <!-- SVG --> <span class="glass-tab-bar__badge">3</span> </span> <span class="glass-tab-bar__label">Verträge</span> </button> </nav>

Icons und Tokens

Seit 1.28.0 färbt jeder Zustand die Icon-Box, und ein SVG zieht seine Linien in currentColor. Das Icon kann deshalb alles sein, was eine Farbe annimmt — ein Icon-Font wie Material Icons, ein als Maske gezeichnetes <img>, ein eigenes Element —, und es folgt aktivem Zustand und Hover, der Lupe des Tropfens und dem Ziehen. Ein Font-Icon übernimmt die 24 px der Box, solange seine eigene Klasse keine Größe setzt. Gib ihm aria-hidden="true": Sein Text würde sonst zum Namen des Tabs. Sechs Tokens, alle ungesetzt, also ändert sich nichts, bis ein Projekt sie setzt:

TokenUngesetztWirkung
--gl-tab-bar-color--gl-icon-mutedIcon und Label der inaktiven Tabs
--gl-tab-bar-hover-color--gl-icon-defaultIcon und Label eines Tabs unter dem Zeiger; mitsetzen, wenn die Farbe darüber heller ist
--gl-tab-bar-label-size10pxGröße der Labels
--gl-tab-bar-icon-size24pxGröße der Icon-Box und eines SVG darin (seit 1.33.0); eine kompakte Leiste nimmt 18 px
--gl-tab-bar-active-weight--gl-font-weight-mediumSchriftstärke des Labels am aktiven Tab (seit 1.33.0); ohne ihn ist jedes Label medium
--gl-tab-bar-z-index900Stapelung der festen Leiste und des schwebenden Docks
<button class="glass-tab-bar__item"> <span class="glass-tab-bar__icon"> <span class="material-icons" aria-hidden="true">home</span> </span> <span class="glass-tab-bar__label">Start</span> </button> :root { --gl-tab-bar-color: var(--gl-icon-strong); --gl-tab-bar-hover-color: var(--gl-color-text); --gl-tab-bar-label-size: 12px; }

Seit 1.33.0 bewegen die Einträge nur, was ein Zustand ändert: Farbe, Fläche, Schatten, Deckkraft und das Drücken (transition-property: color, background-color, box-shadow, opacity, transform). Mit all glitten bei einer Leiste, die ihr Layout wechselte — aufrecht, schwebend, eine Rail —, Padding, Breite und Höhe 250 ms lang, und ein Eintrag, der in dieser Zeit gemessen wurde, hatte noch nicht seine Größe.

Ein Punkt ohne Zahl

Seit 1.38.0 sagt .glass-tab-bar__badge--dot „hier braucht es dich“ ohne Zahl — eine offene Zahlung am Konto-Tab: ein Punkt von 10 px an der Ecke des Icons im einfachen Fehlerrot, mit einem 2 px breiten Ring in der Farbe hinter der Leiste, der ihn vom Icon absetzt. In der Seitenleiste steht er am Ende der Zeile, wie die Zahl. Einem Screenreader sagt der Punkt nichts: Schreib den Grund in den Namen des Eintrags. Tokens setzen Zahl und Punkt gleichermaßen; die Seitenleiste behält ihren Platz am Ende der Zeile und nimmt Größe und Ring.

TokenOhne WertWirkung
--gl-tab-bar-badge-size16pxHöhe und Mindestbreite der Zahl; der Punkt ist 10 px, solange nichts gesetzt ist
--gl-tab-bar-badge-top-4pxAbstand zur Oberkante des Icons; der Punkt -2 px
--gl-tab-bar-badge-end-8pxAbstand zum Ende des Icons; der Punkt -3 px
--gl-tab-bar-badge-ring-width0pxEin Ring darum; der Punkt 2 px
--gl-tab-bar-badge-ring-color--gl-color-bg-midDie Farbe des Rings, der mittlere Seitenhintergrund
<button class="glass-tab-bar__item" aria-label="Konto, eine Zahlung ist offen"> <span class="glass-tab-bar__icon"> <!-- SVG --> <span class="glass-tab-bar__badge glass-tab-bar__badge--dot"></span> <!-- kein Text --> </span> <span class="glass-tab-bar__label">Konto</span> </button>

Seitenleiste, Rail und obere Leiste

Seit 1.32.0 geht dieselbe Navigation dorthin, wo das Layout sie braucht, nicht nur an die untere Kante. Die Einträge bleiben, wie sie sind — Icon, Label, Badge, der aktive Zustand, der Tropfen —, nur das Layout der Leiste ändert sich. --vertical macht sie zur Spalte für eine Seitenleiste: platziert vom Layout der Seite, etwa einer Grid-Spalte, und so hoch wie ihr Container, ein Eintrag pro Zeile mit dem Icon vor dem Label in Fließtextgröße und dem Badge am Zeilenende. Die aktive Zeile steht auf einer eigenen Fläche, --gl-tab-bar-active-bg, wo das gesetzt ist, sonst --gl-surface-3. Mit --rail dazu zeigt die Spalte nur die Icons, --gl-tab-bar-rail-width (72 px) breit: Das Label bleibt der Name des Eintrags für assistive Technik, nur unsichtbar, also zeig es in einem Tooltip.

<div class="app"> <!-- display: grid; grid-template-columns: 220px 1fr; height: 100dvh --> <nav class="glass-tab-bar glass-tab-bar--vertical" aria-label="Bereiche"> <a class="glass-tab-bar__item is-active" href="/"> <span class="glass-tab-bar__icon"><!-- SVG --></span> <span class="glass-tab-bar__label">Start</span> </a> <a class="glass-tab-bar__item" href="/rechnungen"> <span class="glass-tab-bar__icon"><!-- SVG --><span class="glass-tab-bar__badge">3</span></span> <span class="glass-tab-bar__label">Rechnungen</span> </a> </nav> <main>…</main> </div> <!-- Nur Icons --> <nav class="glass-tab-bar glass-tab-bar--vertical glass-tab-bar--rail" aria-label="Bereiche">…</nav>

Mit --start oder --end ist die Spalte stattdessen an diese Kante des Viewports geheftet, über die volle Höhe und frei vom sicheren Bereich dieser Kante; Start und Ende folgen der Schreibrichtung. Das ist die Seite, die die Daumen auf einem großen Telefon im Querformat oder einem aufgeklappten Foldable erreichen, wo auch das System seine eigenen Bedienelemente an den Rand rückt. Die Seite lässt diesen Rand frei, so wie sie unten Platz für die Leiste dort lässt. Die Einträge bleiben, wenn das Layout wechselt; die Seite schaltet also nur die Klassen um — per Media Query oder nach Wahl der Nutzer:

<nav class="glass-tab-bar" id="tabs" aria-label="Bereiche">…</nav> <script> const tabs = document.getElementById('tabs'); const side = matchMedia('(orientation: landscape) and (min-width: 700px)'); const layout = () => { tabs.classList.toggle('glass-tab-bar--vertical', side.matches); tabs.classList.toggle('glass-tab-bar--start', side.matches); // die Seite lässt den Rand der Leiste frei document.body.style.paddingInlineStart = side.matches ? `${tabs.offsetWidth}px` : ''; }; side.addEventListener('change', layout); layout(); </script>

--top legt eine waagerechte Leiste für den oberen Rand einer Seite an: im Fluss — in einen Sticky-Header setzen —, mit der Linie unten, ohne Platz für den Home-Indikator und ab einem Viewport von 640 px mit dem Icon neben dem Label.

<header style="position: sticky; top: 0; z-index: 10"> <nav class="glass-tab-bar glass-tab-bar--top" aria-label="Bereiche">…</nav> </header>

Jede Leiste zeigt jetzt einen Ring für die Tastatur, --gl-shadow-focus am fokussierten Eintrag; bis 1.32.0 hatten die Einträge keinen. Ein Eintrag darf auch ein Link sein, <a class="glass-tab-bar__item" href="…">, sodass sich eine Ansicht auch in einem neuen Tab öffnen lässt; er sieht aus wie der Button. In GlassKit Elements sind die Layouts Attribute: <glk-tab-bar orientation="vertical"> mit rail (oder rail="labels") und dock="start" oder "end", und placement="top"; slot="leading" und "trailing" füllen die Plätze vor und nach den Einträgen.

KlasseBeschreibung
.glass-tab-bar--verticalSpalte für eine Seitenleiste, platziert vom Layout der Seite und so hoch wie ihr Container; scrollt selbst, wo die Einträge nicht passen
.glass-tab-bar--railMit --vertical: nur Icons, --gl-tab-bar-rail-width (72 px) breit, das Label unsichtbar, das Badge an der Ecke des Icons
.glass-tab-bar--start, --endMit --vertical: an diese Kante des Viewports geheftet, volle Höhe, frei vom sicheren Bereich; von rechts nach links gespiegelt
.glass-tab-bar--topWaagerechte Leiste für den oberen Rand einer Seite, im Fluss, die Linie unten; ab 640 px das Icon neben dem Label
a.glass-tab-bar__itemEin Eintrag als Link, ohne Unterstreichung
.glass-tab-bar--labelsMit --rail: das Label unter dem Icon; die Einträge füllen die Breite der Rail und sind mindestens --gl-tab-bar-rail-item-height hoch, gerundet von --gl-tab-bar-item-radius (seit 1.33.0, siehe Eine Rail mit Labels)
--floating mit --vertical und --railEine Rail aus eigenem Glas: so hoch wie ihr Container, mit den Ecken einer Karte; mit --start oder --end dazu an dieser Kante des Viewports, volle Höhe abzüglich --gl-tab-bar-floating-inset an beiden Enden, so weit von ihrer Kante entfernt, frei von den sicheren Bereichen (seit 1.33.0)
.glass-tab-bar__leading, __trailingPlätze vor und nach den Einträgen: in einer Spalte darüber und darunter, der hintere am fernen Ende; in einer Zeile davor und danach (seit 1.33.0)
.glass-tab-bar__accessory-itemDas Accessory über seinem Namen, .glass-tab-bar__accessory-label (seit 1.33.0)

In der Spalte wird ein langes Label mit Auslassungspunkten gekürzt und endet vor dem Badge: Solange ein Badge zu sehen ist, hält die Zeile an ihrem Ende --gl-tab-bar-badge-room frei.

TokenUngesetztWirkung
--gl-tab-bar-rail-width72pxBreite der Rail
--gl-tab-bar-badge-room32pxPlatz, den eine Zeile der Seitenleiste an ihrem Ende freihält, solange sie ein Badge zeigt, damit das Label davor endet
--gl-tab-bar-active-bg--gl-surface-3 in einer SpalteDie Fläche der aktiven Zeile; an der unteren Leiste heißt ungesetzt keine
--gl-tab-bar-rail-item-height62pxMindesthöhe eines Eintrags in einer Rail mit Labels (seit 1.33.0)
--gl-tab-bar-item-radiuseine Kapsel; 16px in einer Rail mit LabelsEcken der schwebenden Einträge, der Einträge einer Rail mit Labels und des Tropfens (seit 1.33.0)
--gl-tab-bar-floating-inset16pxAbstand einer schwebenden Rail an einer Kante von dieser Kante und von den Enden des Viewports (seit 1.33.0)
--gl-tab-bar-rail-radius--gl-radius-cardEcken einer schwebenden Rail, angedockt oder nicht (seit 1.33.0)

Eine kompakte obere Leiste

Drei Tokens formen die Einträge seit 1.33.0, ohne dass sich GlassKits globale Tokens ändern: --gl-tab-bar-icon-size setzt die Größe der Icons, --gl-tab-bar-item-radius rundet den Tropfen und die schwebenden Einträge — ungesetzt eine Kapsel —, und --gl-tab-bar-active-weight setzt die Schriftstärke des aktiven Labels. Eine obere Leiste, die als kompakte Spur gezeichnet ist wie ein Segmented Control, nimmt dessen 18 px große Icons und 12 px Ecken:

<nav class="glass-tab-bar glass-tab-bar--top glass-tab-bar--droplet" aria-label="Bereiche" style="--gl-tab-bar-icon-size: 18px; --gl-tab-bar-item-radius: 12px; --gl-tab-bar-active-weight: var(--gl-font-weight-semibold)">…</nav>

Eine Rail mit Labels

Seit 1.33.0 behält --labels neben --rail das Label unter dem Icon, wie auf der Floating-Bar: eine Rail für den Desktop, die ihre Einträge benennt. Die Einträge füllen die Breite der Rail, --gl-tab-bar-rail-width, und sind mindestens --gl-tab-bar-rail-item-height (62 px) hoch; --gl-tab-bar-item-radius rundet sie und den Tropfen, ungesetzt 16 px, die Ecken eines Buttons. Ein langes Label endet mit Auslassungspunkten, und das Badge bleibt an der Ecke des Icons.

__leading und __trailing sind Plätze in der Leiste vor und nach den Einträgen: in einer Spalte darüber — ein Logo, das Accessory — und an ihrem fernen Ende — ein Fuß wie das Konto; in einer Zeile davor und danach. .glass-tab-bar__accessory-item stellt das Accessory über seinen Namen, __accessory-label. Der Name ist auch das aria-label des Buttons, deshalb ist der sichtbare aria-hidden, und ein Klick darauf sollte den Button drücken.

Mit --floating ist die Rail eine Fläche aus eigenem Glas, angedockt oder nicht: so hoch wie ihr Container, mit den Ecken einer Karte, --gl-tab-bar-rail-radius, und Raum um ihre Einträge — im Fluss eine Rasterspalte der Seite, wie in der Vorschau. Mit --start oder --end dazu steht sie stattdessen an dieser Kante des Viewports: so hoch wie der Viewport abzüglich --gl-tab-bar-floating-inset (16 px) an beiden Enden, so weit von ihrer Kante entfernt, frei von den sicheren Bereichen und von rechts nach links gespiegelt. Die kompakte aufrechte Pille des senkrechten Docks, ohne --rail, bleibt eine Kapsel.

<nav class="glass-tab-bar glass-tab-bar--floating glass-tab-bar--vertical glass-tab-bar--start glass-tab-bar--rail glass-tab-bar--labels glass-tab-bar--droplet" aria-label="Bereiche" style="--gl-tab-bar-rail-width: 96px"> <div class="glass-tab-bar__leading"> <img src="logo.svg" alt="" width="40" height="40"> <div class="glass-tab-bar__accessory-item"> <button class="glass-tab-bar__accessory glass-tab-bar__accessory--accent" aria-label="Assistent"><!-- SVG --></button> <span class="glass-tab-bar__accessory-label" aria-hidden="true">Assistent</span> </div> </div> <a class="glass-tab-bar__item is-active" href="/"> <span class="glass-tab-bar__icon"><!-- SVG --></span> <span class="glass-tab-bar__label">Start</span> </a> <!-- weitere Einträge --> <div class="glass-tab-bar__trailing"><!-- das Konto --></div> </nav> /* Die Seite hält die Seite der Rail frei: ihre Breite und der Abstand zu beiden Seiten */ main { padding-inline-start: calc(96px + 2 * 16px); }

In GlassKit Elements ist die Rail <glk-tab-bar floating orientation="vertical" dock="start" rail="labels">, mit dem Logo und einem <glk-tab-accessory show-label> in slot="leading" und dem Fuß in slot="trailing".

Tab-Bar – Floating

Pillenförmige, zentrierte, schwebende Variante im iOS-26-Liquid-Glass-Stil. Optional mit einer eigenständigen .glass-tab-bar__accessory-Kapsel daneben (z. B. Suche, Erstellen) – mit eigenem Glasmaterial. Der aktive Tab erhält einen weichen radialen Spotlight-Halo statt des Punkt-Underlines, in --gl-tab-bar-spotlight-color (der Primärfarbe). Icon und Label nehmen --gl-tab-bar-active-color, wo das gesetzt ist, sonst --gl-color-primary-text (seit 1.26.0, siehe Primärfarbe als Text).

<div class="glass-tab-bar-dock"> <nav class="glass-tab-bar glass-tab-bar--floating"> <button class="glass-tab-bar__item is-active"> <span class="glass-tab-bar__icon"><!-- SVG --></span> <span class="glass-tab-bar__label">Start</span> </button> <!-- weitere Items --> </nav> <button class="glass-tab-bar__accessory" aria-label="Suche"> <!-- SVG --> </button> </div>

Akzent-Varianten für das Accessory

Mit --accent, --success oder --error wird das Accessory farbig untermalt (gefüllter Hintergrund, weißes Icon). Seit 1.28.0 sitzt die Icon-Farbe am Button: Ein Font-Icon im Accessory folgt Hover und Varianten wie ein SVG, und jede Variante behält ihre Icon-Farbe auch unter dem Zeiger.

KlasseBeschreibung
.glass-tab-bar-dockWrapper: fest unten zentriert, hält Tab-Bar + Accessory
.glass-tab-bar-dock--accessory-leftModifier: Accessory links statt rechts
.glass-tab-bar--floatingModifier auf .glass-tab-bar: Pillenform, max-content-Breite
.glass-tab-bar__accessoryEigenständige Glas-Kapsel neben der Tab-Bar
.glass-tab-bar__accessory--accentGefüllt mit Primärfarbe
.glass-tab-bar__accessory--successGefüllt mit Success-Farbe
.glass-tab-bar__accessory--errorGefüllt mit Error-Farbe
.glass-bg--has-tab-bar-floatingBackground-Padding-Helper für die Floating-Variante
.glass-tab-bar--dropletModifier: Der aktive Eintrag sitzt auf einem Glastropfen, der zum nächsten gleitet (seit 1.27.0, siehe Droplet)
.glass-tab-bar-dock--verticalModifier: das Dock aufrecht am Endrand, senkrecht zentriert; die Bar darin bekommt --vertical (seit 1.32.0)
.glass-tab-bar-dock--startMit --vertical: stattdessen am Startrand

Aufrecht an der Seite

Seit 1.32.0 steht die Floating-Bar auch aufrecht an einer Seite, dort, wo ein Daumen auf einem quer gehaltenen Telefon liegt. .glass-tab-bar-dock--vertical am Dock und --vertical an der Bar stapeln die Einträge so, wie sie an der unteren Kante stehen, Icon über Label, mit dem Accessory darunter (darüber mit --accessory-left), senkrecht zentriert am Endrand; .glass-tab-bar-dock--start rückt es an den anderen. Beide halten den sicheren Bereich frei und folgen der Schreibrichtung. Mit --droplet läuft der Tropfen auf und ab:

<div class="glass-tab-bar-dock glass-tab-bar-dock--vertical"> <nav class="glass-tab-bar glass-tab-bar--floating glass-tab-bar--vertical"> <!-- die Einträge --> </nav> <button class="glass-tab-bar__accessory" aria-label="Suche"><!-- SVG --></button> </div>

Fläche des aktiven Eintrags

Seit 1.27.0 kann der aktive Eintrag auf einer eigenen Fläche stehen, wie der gewählte Tab in iOS 26. Vier Tokens, alle ungesetzt, es ändert sich also nichts, bis ein Projekt sie setzt: --gl-tab-bar-active-bg und --gl-tab-bar-active-shadow legen eine Kapsel hinter den aktiven Eintrag, --gl-tab-bar-spotlight-opacity: 0 schaltet das Spotlight der Floating-Bar ab und --gl-tab-bar-active-glow: none das Leuchten am Icon, das auf einer hellen Kapsel wie eine Unschärfe wirkt.

:root { --gl-tab-bar-active-bg: color-mix(in srgb, var(--gl-color-text) 14%, transparent); --gl-tab-bar-active-shadow: 0 2px 8px rgba(0, 0, 0, 0.12), var(--gl-inset-subtle); --gl-tab-bar-spotlight-opacity: 0; --gl-tab-bar-active-glow: none; }

Eine fast deckende weiße Kapsel, rgba(255, 255, 255, 0.9), lässt eine dunkle Markenfarbe auch im dunklen Theme die Farbe des aktiven Tabs bleiben, wo sie auf dem Spotlight unlesbar wäre. Die Kapsel ist der Hintergrund des Eintrags und deshalb gerundet wie er: eine Pille auf der Floating-Bar, 12px auf der festen. Seit 1.33.0 rundet --gl-tab-bar-item-radius die schwebenden Einträge und den Tropfen gemeinsam, ungesetzt eine Kapsel: abgerundete Quadrate in einer Rail, die 12 px eines Segmented Controls in einer kompakten oberen Leiste.

Droplet

Seit 1.27.0 macht .glass-tab-bar--droplet die Auswahl flüssig. Der aktive Eintrag sitzt auf einem Glastropfen, der ein wenig über der Bar schwebt. Ein Tipp auf einen anderen Tab schickt den Tropfen dorthin wie Wasser über eine Scheibe: Die Vorderkante läuft voraus, die Hinterkante folgt, unterwegs wird der Tropfen flacher, am Ziel wippt er kurz nach, und die Icons, über die er läuft, wachsen wie unter einer Lupe. Ein Finger kann ihn auch über die Bar ziehen; beim Loslassen wird der Tab darunter gewählt. Für eine App mit Floating-Bar ist das die deutlichste Aufwertung, die sie übernehmen kann, und sie ändert sonst nichts: Das Markup bleibt, und wo das Skript nicht läuft, behält die Bar ihr Spotlight. Probier es:

Den Tropfen zeichnet das Stylesheet. Die Bewegung kommt aus glasskit-droplet.js, einem optionalen Modul im selben Paket ohne Abhängigkeiten, etwa 4 KB minifiziert und unter 2 KB komprimiert. Es folgt .is-active, das die Seite wie gewohnt selbst setzt, und klickt den Tab an, auf dem ein Ziehen endet; die Wahl trifft also der Klick-Handler der Seite:

<nav class="glass-tab-bar glass-tab-bar--floating glass-tab-bar--droplet"> <!-- die Einträge, der aktive mit .is-active --> </nav> <script type="module"> import { attachDroplets } from 'https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.40/glasskit-droplet.js'; attachDroplets(); // jede --droplet-Zeile der Seite; attachDroplet(row) für eine </script>

Der Tropfen nimmt die Fläche des aktiven Eintrags, wo ein Projekt eine gesetzt hat (--gl-tab-bar-active-bg, -shadow), sonst ein eigenes helles Glas; sein Glanz ist --gl-gloss-droplet. data-surface="solid" und "tint" machen ihn deckend wie jede andere Fläche, und mit prefers-reduced-motion springt er, statt zu gleiten. Eine Transformation an einem Vorfahren — eine App, die beim Start hineinzoomt, ein Popover, das aus scale(0.96) aufgeht — lässt ihn auf seinem Eintrag, in Ruhe und während sie läuft (seit 1.28.0). Segmentwahlen bekommen denselben Tropfen. In GlassKit Elements ist es ein Attribut: <glk-tab-bar floating droplet>.

In einer Spalte — einer Seitenleiste, der aufrechten Floating-Bar — läuft der Tropfen entlang y (seit 1.32.0). Das Skript liest die flex-direction der Reihe oder ihr aria-orientation="vertical", setzt data-droplet-axis="y" und die Richtungen "down" und "up", und ein Finger zieht den Tropfen auf und ab, während ein Wischen zur Seite beim Browser bleibt. Eine waagerechte Leiste oben auf einer Seite, statisch oder schwebend, lässt ihn laufen wie die untere. Probier die Seitenleiste:

Timing des Tropfens

Seit 1.32.0 ist die Bewegung Tokens, die das Stylesheet und glasskit-droplet.js gleichermaßen lesen. Sie werden vererbt und erreichen so auch <glk-tab-bar droplet> und <glk-segmented droplet> in GlassKit Elements. Ungesetzt lassen sie den Tropfen, wie er war: Er kommt etwa 830 ms nach einem Tipp zur Ruhe.

TokenUngesetztWirkung
--gl-droplet-duration0.3sDie Vorderkante und die Kanten quer zum Weg
--gl-droplet-easingcubic-bezier(0.3, 1.2, 0.5, 1)Ihre Kurve
--gl-droplet-trail-duration0.5sDie Hinterkante, die ihr nachgezogen wird
--gl-droplet-trail-delay40msWie lange die Hinterkante wartet
--gl-droplet-trail-easingcubic-bezier(0.5, 1.5, 0.45, 0.92)Ihre Kurve, mit dem Überschwingen
--gl-droplet-lift0.86Wie flach der Tropfen unterwegs läuft; 1 staucht nichts
--gl-droplet-lift-duration280msWie lange er flach läuft
--gl-droplet-settle-duration0.55sDas Zurückfedern in die Form
--gl-droplet-settle-easingcubic-bezier(0.34, 1.8, 0.5, 1)Seine Kurve, das Wippen am Ende
--gl-droplet-lens-duration0.4sDer Eintrag unter dem Tropfen, der wie unter einer Lupe wächst
--gl-droplet-lens-easingcubic-bezier(0.34, 1.6, 0.5, 1)Seine Kurve

Ein Produkt, das seine Bewegungen bei 240 ms ease-out hält, lässt den Tropfen in 240 ms ankommen, ohne Nachlauf, in der Tab-Bar wie in der Segmentwahl. Mit der Dauer des Anhebens bei 0ms vertieft der Tropfen unterwegs auch seinen Schatten nicht, der sich sonst erst nach etwa 600 ms wieder legt:

:root { --gl-droplet-duration: 240ms; --gl-droplet-easing: ease-out; --gl-droplet-trail-duration: 240ms; --gl-droplet-trail-delay: 0ms; --gl-droplet-trail-easing: ease-out; --gl-droplet-lift: 1; --gl-droplet-lift-duration: 0ms; --gl-droplet-settle-duration: 0ms; --gl-droplet-lens-duration: 240ms; --gl-droplet-lens-easing: ease-out; }

Ein Finger, der den Tropfen zieht, behält sein eigenes kurzes Timing, und mit prefers-reduced-motion springt der Tropfen weiterhin, statt zu gleiten.

Titel

Verträge aufnehmen
& hochladen

<h1 class="glass-title">Seitentitel</h1>

Card

Vertrag fotografieren, Kanten prüfen und gesammelt hochladen.

<div class="glass-card glass-card--glow"> <div class="glass-card__icon"><!-- SVG --></div> <p class="glass-card__text">Text</p> </div>
KlasseBeschreibung
.glass-cardBasis-Glasfläche
.glass-card--glowHell → milchig Verlauf + Lichtstreifen
.glass-card__iconZentrierter Icon-Wrapper
.glass-card__textBeschreibungstext

Buttons

KlasseBeschreibung
.glass-btnBasis (56px, volle Breite)
--primaryFarbiger Gradient
--secondaryMilchig-weiß
--tertiarySubtiles Glas
--sm44px Höhe
--lg64px Höhe
--autoAuto-Breite
:disabled, [aria-disabled="true"]Gedimmt auf 0,45, Cursor not-allowed, kein Hover (seit 1.21.2)
svg.glass-icon--fillBewusst gefülltes Icon (z. B. Brand-Logos)

Auf dem Glas eines Modals oder eines Sheets ist ein tertiärer Button im dunklen Modus eine Stufe dunkler, --gl-dialog-control, und nimmt keinen eigenen Blur (seit 1.35.0): Seine gedämpfte Beschriftung las dort 3 bis 4:1.

Ein deaktivierter Button — disabled, oder aria-disabled="true" an einem a.glass-btn, der sich nicht deaktivieren lässt — ist gedimmt, zeigt den Cursor not-allowed und hebt sich beim Hover nicht mehr an (seit 1.21.2; vorher sah er genau aus wie ein aktiver). Der Link selbst navigiert weiter, wenn die Seite ihn nicht abfängt. Dasselbe gilt für Pill, Tab-Bar-Accessory, Modal-Aktionen, interaktive Listenzeilen und die Monatspfeile des Kalenders; Toggle, Checkbox und Radio dimmen Bedienelement und Label, Select, Textarea und Range dimmen wie das Input.

Buttons haben seit 1.31.0 eigene Haken. --gl-btn-radius rundet sie, ungesetzt --gl-radius-btn, das sich Listen, Statuskarte, Toasts, Akkordeon und Segmentwahl teilen: --gl-btn-radius: 9999px auf :root ergibt Buttons in Pillenform und lässt die anderen, wie sie sind. --gl-btn-padding-x ist der Raum neben der Beschriftung, ungesetzt 24 px, der Innenabstand, den bisher nur --auto hatte; jeder andere Button hatte die 6 px des Browsers, sodass ein kleiner, der seine Breite aus dem Text nahm, in einer Reihe von Aktionen den Text mit seinen Kanten berührte. --sm liest zuerst --gl-btn-radius-sm und --gl-btn-padding-x-sm, dann die Haken darüber, und behält seinen Radius von 12 px, solange keiner gesetzt ist; so formt ein Wert jede Größe.

Seit 1.32.0 ersetzt --gl-btn-primary-bg den ganzen Hintergrund von --primary, so wie Schalter und Radio ihre eigenen haben; ungesetzt ist es der Verlauf von --gl-color-primary über -mid zu -dark. Ein Produkt, das bei weißer Schrift bleibt, lässt den Verlauf so dunkler beginnen und die Markenfarbe in Ruhe; das Rezept steht unter Primärfarbe als Text.

Badge

Standard Aktiv Fertig Offen Fehler
<span class="glass-badge glass-badge--primary">Aktiv</span>

Varianten: --primary, --success, --warning (seit 1.18.0) und --error. Jede nimmt Fläche, Rand und Schrift aus den Token -surface, -border und -on-surface ihrer Statusfarbe — wer die Statusfarbe umfärbt, färbt das ganze Badge um.

Chips

Ein Badge dient auch als Filter-Chip. --interactive macht ihn anfassbar — Zeiger, Hover-Tönung, Fokusring — und --selected zeigt, welcher Chip aktiv ist. Nimm einen echten <button> und leg den Zustand in aria-pressed, damit die Zeile per Tastatur bedienbar ist und als Umschalter angesagt wird.

--selected vertieft die Farbe, die der Badge schon hat, statt sie zu übergehen — ein Status-Chip bleibt beim Auswählen also in seiner eigenen Farbe.

Im dunklen Modus sagt ein gewählter Badge seine Beschriftung in der Textfarbe (seit 1.35.0): Der für dunkle Flächen aufgehellte Akzent las auf einem Chip über Glas 2,2 bis 3:1. Rand und Tönung zeigen die Auswahl; --gl-badge-selected-color setzt die Farbe der Beschriftung. Auf dem Glas eines Modals oder eines Sheets nimmt ein Badge keinen eigenen Blur: Der Dialog weichzeichnet schon, und Chromium zählte seinen Schimmer unter einem verschachtelten Backdrop-Filter doppelt, sodass ein Chip für Weiß auf 2,9:1 kam.

<button class="glass-badge glass-badge--interactive glass-badge--selected" aria-pressed="true">aktiv</button> <button class="glass-badge glass-badge--interactive" aria-pressed="false">beworben</button>
KlasseBeschreibung
--interactiveAnklickbar: Zeiger, Hover-Tönung, Fokusring, Druck-Rückmeldung
--selectedDer aktive Chip — vertieft die eigene Farbe des Badges
--gl-badge-accentDie Farbe, die beide Zustände vertiefen. Pro Badge setzbar, wenn ein Chip eine eigene Farbe bekommen soll.

Auswahl-Chips

Seit 1.33.0 macht --md einen Chip zum Auswählen, wie in der Zeile der Branchen eines Anmeldeformulars: --gl-badge-md-height (36 px) hoch, in der kleinen Schrift eines Formulars. Mit --interactive nimmt er Berührungen auf 44 px an, 4 px über und unter seinen Kanten, die Zielgröße, die die anderen Bedienelemente einhalten. In einer umbrechenden Zeile mindestens 8 px zwischen den Zeilen lassen, damit sich die Ziele treffen und nicht überlappen.

<button class="glass-badge glass-badge--md glass-badge--interactive glass-badge--selected" aria-pressed="true">Café &amp; Bäckerei</button>
Klasse / TokenBeschreibung
--mdEin Chip zum Auswählen: 36 px hoch, die kleine Schrift eines Formulars; interaktiv ein Berührungsziel von 44 px
--gl-badge-md-heightSeine Höhe, ungesetzt 36px; das Berührungsziel wächst um ihn herum auf 44 px

In GlassKit Elements: <glk-badge interactive size="md">.

Avatar

S
M
L
<div class="glass-avatar glass-avatar--lg">L</div>

Seit 1.37.0 setzt --gl-avatar-size jede Größe, am Avatar oder an einem Container drumherum, und --gl-avatar-font-size dazu die Größe der Initialen. In der Leading-Box einer Listen-Zeile nimmt ein Avatar die Größe der Box an, 28 px, und 40 px in --lg, den Avatar einer Kontaktliste: 44 px schnitt die Box ab, 32 px wirkten darin verloren. Der Token am Avatar selbst entscheidet weiter.

<span class="glass-list__leading glass-list__leading--lg"><span class="glass-avatar">LH</span></span> <!-- 40px --> .contacts { --gl-avatar-size: 36px; --gl-avatar-font-size: 14px; }

Divider


<hr class="glass-divider">

Status-Hinweis

Noch keine Verträge vorhanden.

--gl-status-color setzt die Farbe der Nachricht (seit 1.35.0), ohne Wert gedämpfter Text. Ein Wort darin in <b>, etwa ein Paketname, bekommt die Textfarbe über eine Regel der Seite. Seit 1.36.0 hat die Nachricht keinen eigenen Außenabstand. Das 1em des Browsers darüber und darunter machte die Karte 82 px statt 54 px hoch, wo kein Reset der Seite das <p> erreichte: auf einer Seite ohne Reset, im Shadow Root von <glk-status>.

<div class="glass-status"> <svg viewBox="0 0 24 24"><!-- Icon --></svg> <p>Noch keine Verträge vorhanden.</p> </div>

Liste

Gruppierter Settings-Container für gestapelte Zeilen. Items unterstützen ein führendes Icon, Titel + optionalen Untertitel sowie ein Trailing-Element (Chevron, Wert, Button). Trennlinien zwischen Items werden automatisch gezeichnet — kein zusätzliches Markup nötig.

Settings-Stil (Icon + Titel + Untertitel + Trailing)

  • iOS 26.4 Update
    2,1 GB · Jetzt verfügbar
  • Apple One
    Familie — verlängert sich am 28. Apr.
    22,95 €
  • Mitteilungen
  • Alle Abos anzeigen
<ul class="glass-list"> <li class="glass-list__item glass-list__item--interactive"> <span class="glass-list__leading"><svg>...</svg></span> <div class="glass-list__content"> <div class="glass-list__title">iOS 26.4 Update</div> <div class="glass-list__subtitle">2,1 GB · Jetzt verfügbar</div> </div> <div class="glass-list__trailing"><svg>...</svg></div> </li> <!-- Weitere Items… Trennlinien erscheinen automatisch --> <li class="glass-list__item glass-list__item--center">Alle anzeigen</li> </ul>

Kompaktes Menü (zentrierter Text, ohne Icons)

  • Vertrag teilen
  • Duplizieren
  • Löschen
<ul class="glass-list"> <li class="glass-list__item glass-list__item--center glass-list__item--interactive">Vertrag teilen</li> <li class="glass-list__item glass-list__item--center glass-list__item--interactive">Duplizieren</li> <li class="glass-list__item glass-list__item--center glass-list__item--interactive glass-list__item--danger">Löschen</li> </ul>

Gruppierte Sektionen mit großen Icons

Empfehlungen
  • Geladene Medien prüfen
    Spare bis zu 1,38 GB ein. Prüfe alle auf deinem Gerät geladenen Videos und Audiodateien und entferne sie bei Bedarf.
  • AusweisApp Bund
    Version 2.5.1
    24 MB
Hörbücher
  • Der Geist aus der Maschine
    Teilweise angesehen
    237,1 MB
  • Alle Downloads anzeigen
<div class="glass-list__section-header">Empfehlungen</div> <ul class="glass-list"> <li class="glass-list__item glass-list__item--interactive"> <span class="glass-list__leading glass-list__leading--lg"><svg>...</svg></span> <div class="glass-list__content"> <div class="glass-list__title">Geladene Medien prüfen</div> <div class="glass-list__subtitle glass-list__subtitle--wrap">Mehrzeilige Beschreibung...</div> </div> <div class="glass-list__trailing"> <span class="glass-list__value">24 MB</span> <svg>...</svg> </div> </li> <li class="glass-list__item glass-list__item--accent glass-list__item--center">Alle anzeigen</li> </ul>

Klassen-Referenz

KlasseBeschreibung
.glass-listGruppierter Surface-Container
.glass-list--flushEdge-to-Edge-Variante (kein seitlicher Rand / Radius): reicht bis an beide Kanten ihres Containers, hinausgezogen um --gl-list-flush-inset — in Card und Modal-Inhalt deren Innenabstand, sonst 20 px (seit 1.29.1)
.glass-list--bareEntfernt Hintergrund, Border & Shadow — für die Einbettung in .glass-popover oder .glass-card.
.glass-list__itemListenzeile (als <li>, <a> oder <button>)
.glass-list__section-headerSektionslabel oberhalb einer .glass-list — Uppercase, klein, gedämpft
.glass-list__item--interactiveFügt Hover- / Focus- / Active-States hinzu; für die Tastatur ein 2 px breiter Ring in --gl-border-focus innerhalb der Zeile (seit 1.32.0; vorher zeigte nur eine Tönung den Fokus)
.glass-list__item--centerZentrierte Single-Text-Variante mit Ellipsis-Truncation
.glass-list__item--dangerRoter Text für destruktive Aktionen
.glass-list__item--accentPrimärfarbener Text für Akzent-Aktionen
.glass-list__leadingLeading-Slot (28×28, enthält ein Icon-SVG); --gl-list-leading-width verbreitert ihn (seit 1.36.0, unten)
.glass-list__leading--lgGroßer Leading-Slot (40×40, abgerundetes Quadrat, unterstützt <img>); derselbe Token verbreitert ihn
.glass-list__contentFlexibler mittlerer Slot — nimmt verbleibende Breite, ermöglicht Truncation
.glass-list__titlePrimärer Text (medium weight, eine Zeile mit Ellipsis, außer --gl-list-title-white-space lässt ihn umbrechen)
.glass-list__subtitleSekundärer Text (klein, gedämpft, eine Zeile mit Ellipsis, außer --gl-list-subtitle-white-space lässt ihn umbrechen)
.glass-list__title-rowHält den Titel und __title-trailing — einen Badge, eine Marke —, das unter den Titel rückt, wo die Zeile zu schmal ist (seit 1.35.0)
.glass-list__extraUnter dem Untertitel: ein Link, eine Reihe Chips (seit 1.35.0)
.glass-list__subtitle--wrapMehrzeiliger Subtitle (bis 3 Zeilen, mit Ellipsis-Clamp; --gl-list-subtitle-lines setzt die Grenze)
.glass-list__trailingTrailing-Slot — Chevron, Wert, Button, Badge
.glass-list__valueGedämpfter Text in .glass-list__trailing (z.B. Dateigröße)
.glass-list__item.is-flashLeuchtet einmal auf, 1,2 s in --gl-flash-color, für ein Element, das sich anderswo geändert hat (seit 1.32.0)
.glass-list__item.is-selectedDie gewählte Zeile einer Liste neben ihrem Detail: eine Pille aus erhabenem Glas in der Zeile, --gl-list-item-selected-bg (ohne Wert --gl-surface-4), der Titel halbfett, das Icon im Akzent; mit aria-current="true" (seit 1.34.0, unten)

Trennlinien zwischen Zeilen werden automatisch via ::after gerendert. Items mit Leading-Icon erhalten einen icon-aligned Inset; Items ohne — kein __leading, oder eines mit dem Attribut hidden (seit 1.20.0) — bekommen das normale Padding-Inset. Ein verstecktes __leading oder __trailing nimmt keinen Platz ein. Das letzte Item hat nie eine Trennlinie. SVG-Icon-Konvention: 24px für Leading, 18px für Trailing, stroke: currentColor, stroke-width: 2.

Seit 1.32.0 kann ein Element, das sich anderswo geändert hat — auf einem anderen Gerät, durch ein Rückgängig —, einmal aufleuchten, damit das Auge es zwischen den anderen findet: .is-flash tönt es 1,2 s lang in --gl-flash-color, ungesetzt 30 % der Primärfarbe, und unter prefers-reduced-motion leuchtet nichts. Die Klasse bei animationend wieder entfernen, damit die nächste Änderung erneut aufleuchtet.

<li class="glass-list__item is-flash">…</li> <!-- anderswo geändert, leuchtet einmal auf -->

Bündig

--flush führt die Liste von Kante zu Kante ihres Containers, ohne seitlichen Rand und Radius. Die Liste wächst auf beiden Seiten um den Innenabstand des Containers, --gl-list-flush-inset: .glass-card und .glass-modal__body setzen ihn auf ihren eigenen Innenabstand, anderswo sind es 20 px (--gl-space-lg), solange der Container ihn nicht setzt. Seit 1.29.1 — vorher behielt die Liste ihre Breite und rückte nur zum Anfang.

Kunde

  • Zahlungen
  • Rechnungen
  • Tickets
<div class="glass-card"> <ul class="glass-list glass-list--flush">…</ul> </div> /* In einem eigenen Container mit 16px Innenabstand */ .my-panel { padding: 16px; --gl-list-flush-inset: 16px; }

Umbrechender Text

Titel und Subtitle bleiben einzeilig und werden mit Ellipsis gekürzt, --wrap begrenzt den Subtitle auf drei Zeilen. Seit 1.23.0 entscheidet ein Projekt das einmal anders, auf :root, oder für eine Liste oder eine Zeile: --gl-list-title-white-space und --gl-list-subtitle-white-space nehmen nowrap (Standard) oder normal, und --gl-list-subtitle-lines setzt die Grenze von --wrap — 3, wenn nicht gesetzt, none zeigt alle Zeilen. Die Tokens werden vererbt und erreichen so auch <glk-list-item> aus GlassKit Elements. Ein Wort, das breiter ist als die Spalte, etwa eine E-Mail-Adresse, bricht dort, wo es muss, statt aus der Zeile zu laufen; Text bricht weiter an Leerzeichen. Für Trennung nach Silben hyphens auf der Liste oder der Seite setzen: Es wird ebenfalls vererbt und folgt dem lang der Seite.

:root { --gl-list-title-white-space: normal; --gl-list-subtitle-white-space: normal; --gl-list-subtitle-lines: none; } /* Trennt nach Silben; kurze Wörter bleiben ganz */ .glass-list, glk-list { hyphens: auto; -webkit-hyphens: auto; hyphenate-limit-chars: 10 4 4; }

Platz neben dem Titel

Eine Zeile darf mehr sagen als einen Titel (seit 1.35.0). .glass-list__title-row hält den Titel und daneben __title-trailing — einen Badge wie „KI“, eine Marke —, das unter den Titel rückt, wo die Zeile zu schmal ist, während der Titel seine Auslassungspunkte behält. __extra unter dem Untertitel nimmt einen Link („Rückgängig“) oder eine Reihe Chips auf. --gl-list-title-size, --gl-list-title-weight und --gl-list-subtitle-size setzen die Schrift einer Zeile, an der Liste oder an einer Zeile.

  • Öffnungszeiten
    KI
    Heute geändert, 09:41
  • Neuigkeiten
    Gestern geändert
<div class="glass-list__content"> <div class="glass-list__title-row"> <div class="glass-list__title">Öffnungszeiten</div> <span class="glass-list__title-trailing"><span class="glass-badge glass-badge--primary">KI</span></span> </div> <div class="glass-list__subtitle">Heute geändert, 09:41</div> <div class="glass-list__extra"><a href="…">Rückgängig</a></div> </div>

Seit 1.36.0 verbreitert --gl-list-leading-width den Platz __leading für mehr als ein Icon — eine Uhrzeit und einen Chip vor dem Text einer Verlaufszeile. Die Box war 28 px breit, mit --lg 40 px, das abschneidet, was nicht passt. Die Trennlinie folgt dem Token. Er nimmt eine Länge; für eine anders aufgebaute Box sagt --gl-list-item-divider-inset, wo die Linie beginnt.

  • 09:41
    Öffnungszeiten: Samstag 7:00 bis 14:00
    Öffnungszeiten · Lena Hoffmann
  • 08:15
    Neuigkeiten: Sommerfest
    Neuigkeiten · Jonas Weber
<ul class="glass-list" style="--gl-list-leading-width: 84px"> <li class="glass-list__item"> <span class="glass-list__leading glass-list__leading--lg"><!-- eine Uhrzeit und ein Chip --></span> <div class="glass-list__content">…</div> </li> </ul>

Die gewählte Zeile

Eine kompakte Liste neben einem Editor markiert den Eintrag, der offen ist (seit 1.34.0): .is-selected an der Zeile legt eine Pille aus erhabenem Glas hinein, wie sie die gewählte Baumzeile und die aktive Zeile einer Seitenleiste haben, mit halbfettem Titel und dem Icon im Akzent. aria-current="true" sagt Hilfstechnik, welcher Eintrag es ist — aria-selected stattdessen, wo die Liste eine Listbox ist. Hover und Druck lassen die gewählte Zeile, wie sie ist, und der Tastaturring bleibt um sie herum. --gl-list-item-selected-bg setzt die Fläche der Pille, ohne Wert --gl-surface-4.

Seit 1.35.0 formen Tokens die gewählte Zeile anders: --gl-list-item-selected-inset (3 px --gl-space-xs), -radius (--gl-radius-sm), -shadow (Kantenlicht und der Schatten eines Buttons), -color (die Überschriftenfarbe) und -weight (halbfett). 0, 0 und none für Abstand, Radius und Schatten ergeben eine flache Tönung über die ganze Zeile. Seit 1.36.0 nimmt der Titel die Farbe der Zeile an, die Überschriftenfarbe oder -color; vorher behielt er seine eigene Textfarbe, und -color änderte an ihm nichts.

  • Start
  • Öffnungszeiten
    Im Editor geöffnet
  • Neuigkeiten
<li class="glass-list__item glass-list__item--interactive is-selected" aria-current="true">…</li>

Die Trennlinie beginnt unter dem Text: nach dem führenden Icon, ohne eines am Innenabstand der Zeile. --gl-list-item-divider-inset (seit 1.34.0) setzt ihren Anfang für Zeilen, die anders aufgebaut sind. Eine Zeile mit einem Griff vor ihrem Icon lässt die Linie an ihrem Innenabstand beginnen, weil die Breite des Griffs Sache der Seite ist, bis das Token sagt, wo der Text beginnt.

  • Neuigkeiten
    Baustein
  • Öffnungszeiten
    Baustein
  • Team
    Baustein
<ul class="glass-list" style="--gl-list-item-divider-inset: 90px"> <li class="glass-list__item"> <span class="glass-sort__handle">…</span> <!-- der Griff zuerst --> <span class="glass-list__leading">…</span> <div class="glass-list__content">…</div> </li> </ul>

Baum

Ebenen, die auf- und zugehen (seit 1.32.0): die Struktur einer App, Menüs mit Untermenüs, Ordner. Eine Zeile pro Eintrag — ein Pfeil, der sich dreht, wenn der Eintrag aufgeht, ein Icon, das Label, etwas am Ende wie ein Badge —, eingerückt um --gl-tree-indent (20 px) pro Ebene; die Ebene ist --gl-tree-level an der Zeile. Die Kinder des Eintrags folgen in einer __group. Der Baum hat keine eigene Fläche: in eine Card oder eine Seitenleiste setzen.

  • Meine App
    • Hauptmenü 3
      • Start
      • Leistungen
    • Archiv
<ul class="glass-tree" role="tree" aria-label="App-Struktur"> <li role="treeitem" aria-level="1" aria-expanded="true" aria-selected="false"> <div class="glass-tree__row is-expanded" style="--gl-tree-level: 1"> <span class="glass-tree__twisty" aria-hidden="true"><!-- Chevron-SVG --></span> <span class="glass-tree__icon" aria-hidden="true"><!-- SVG --></span> <span class="glass-tree__label">Hauptmenü</span> <span class="glass-tree__trailing"><span class="glass-badge">3</span></span> </div> <ul class="glass-tree__group" role="group"> <li role="treeitem" aria-level="2" aria-selected="true"> <div class="glass-tree__row is-leaf is-selected" style="--gl-tree-level: 2"> <span class="glass-tree__twisty" aria-hidden="true"><!-- Chevron-SVG --></span> <span class="glass-tree__label">Leistungen</span> </div> </li> </ul> </li> </ul>

Die Rollen sind die des WAI-ARIA-Tree-Views: role="tree" mit einem Namen an der Liste, "treeitem" an jedem Eintrag mit aria-level, aria-expanded an den Einträgen mit Kindern und aria-selected, "group" an jeder __group; hidden an einer geschlossenen. Die Zustände sind Klassen an der Zeile. Der Baum ist ein einziger Tab-Stopp: Die Pfeiltasten wandern zwischen den Einträgen — runter und hoch, rechts öffnet einen Eintrag oder geht zu seinem ersten Kind, links schließt ihn oder geht zum übergeordneten Eintrag —, und die Zeile des Eintrags mit dem Fokus bekommt .is-focus-visible, oder :focus-visible, wo die Zeile selbst den Fokus hat. Diese Tastatur bringt <glk-tree> aus GlassKit Elements mit, von rechts nach links gespiegelt wie hier der Pfeil, mit Events, bevor ein Eintrag aufgeht, zugeht oder gewählt wird, und mit Kindern, die geladen werden, wenn ein Eintrag zum ersten Mal aufgeht.

Zwei Zustände kommen von außerhalb des Baums. .is-error markiert einen Eintrag, dessen Kinder nicht laden konnten: Der Pfeil wird zu einem „!“ in der Fehlerfarbe, und erneutes Öffnen versucht es noch einmal; GlassKit Elements setzt ihn mit load-error. .is-flash lässt eine Zeile 1,2 s lang aufleuchten, wenn sie sich anderswo geändert hat — auf einem anderen Gerät, durch ein Rückgängig —, damit das Auge sie zwischen den anderen findet; --gl-flash-color tönt sie, ungesetzt 30 % der Primärfarbe, und unter prefers-reduced-motion leuchtet nichts. Die Klasse bei animationend wieder entfernen, damit die nächste Änderung erneut aufleuchtet:

<div class="glass-tree__row is-error" style="--gl-tree-level: 2">…</div> <!-- die Kinder konnten nicht laden --> <div class="glass-tree__row is-flash" style="--gl-tree-level: 2">…</div> <!-- anderswo geändert, leuchtet einmal auf --> row.classList.add('is-flash'); row.addEventListener('animationend', () => row.classList.remove('is-flash'), { once: true });
KlasseBeschreibung
.glass-treeDie Liste; ohne eigene Fläche
__rowDie Zeile eines Eintrags, --gl-tree-row-height (36 px) hoch, eingerückt um --gl-tree-indent (20 px) für jede Ebene von --gl-tree-level nach der ersten
__twistyDer Pfeil; dreht sich mit .is-expanded, verschwindet mit .is-leaf und behält seinen Platz, von rechts nach links gespiegelt
__iconDas Icon vor dem Label, --gl-tree-icon-size (18 px) im Quadrat, sein SVG ebenso (seit 1.34.0) — 28px für einen getönten Chip, wie ihn Listenzeilen zeigen; ein leeres nimmt keinen Platz ein
__labelDas Label, einzeilig mit Auslassungspunkten
__trailingAm Zeilenende: ein Badge, ein Status, eine Aktion
__groupDie Kinder des Eintrags unter seiner Zeile; hidden, solange er zu ist
.is-expanded, .is-leafAn der Zeile: offen oder ohne Kinder
.is-selectedDie gewählte Zeile: angehobenes Glas wie die aktive Zeile einer Seitenleiste, halbfett, ihr Icon in der Akzentfarbe
.is-loadingKinder unterwegs: Der Pfeil wird zu einem kleinen Spinner (mit aria-busy="true" am Eintrag)
.is-errorDie Kinder konnten nicht laden: Der Pfeil wird zu einem „!“ in der Fehlerfarbe; erneutes Öffnen versucht es noch einmal
.is-flashEin Aufleuchten von 1,2 s in --gl-flash-color (ungesetzt 30 % der Primärfarbe) an einer Zeile, die sich anderswo geändert hat; keines unter prefers-reduced-motion
.is-disabledGedimmt, Cursor not-allowed
.is-focus-visibleDer Tastaturring innerhalb der Zeile, für ein Skript, das den Fokus bewegt

Sortieren

Die Zustände eines Ziehens, das eine Liste oder einen Baum neu ordnet (seit 1.32.0). Das gezogene Element bleibt verblasst an seinem Platz (.is-sort-source), während eine angehobene Kopie, .glass-sort__ghost, dem Zeiger im Top Layer folgt. Eine Linie zeigt, wo das Element landet, vor oder hinter einem anderen (.is-sort-before, .is-sort-after an diesem), im Baum eingerückt auf die Ebene, auf der es landet; ein Eintrag, der es aufnimmt, leuchtet auf (.is-sort-inside), und einer, der es ablehnt, zeigt die Fehlerfarbe (.is-sort-refused).

  • Start
  • Leistungen
  • Über uns
  • Kontakt

Im Baum ist die Linie auf die Ebene eingerückt, auf der das Element landet. Hier zeigen sich alle drei Zustände auf einmal; ein Ziehen zeigt immer nur einen.

  • Hauptmenü
    • Start
    • Impressum
<li class="glass-list__item is-sort-source">…</li> <!-- das gezogene Element --> <li class="glass-list__item is-sort-after">…</li> <!-- es landet hinter diesem --> <div class="glass-tree__row is-sort-inside">…</div> <!-- oder in diesem --> <div class="glass-sort__ghost" popover="manual"> <!-- folgt dem Zeiger --> <span class="glass-sort__handle"><!-- SVG --></span>Leistungen </div>

Elemente nebeneinander — Kacheln in einem Raster — bekommen die Linie aufrecht (seit 1.34.0): .is-sort-inline mit .is-sort-before setzt sie an die Anfangskante des Elements, mit .is-sort-after an seine Endkante. Eine Kachel, die das Element aufnimmt, leuchtet auf wie eine Zeile.

Neuigkeiten
Team
Öffnungszeiten
Untermenü
Galerie
<div class="tile is-sort-inline is-sort-before">…</div> <!-- landet vor dieser Kachel --> <div class="tile is-sort-inside">…</div> <!-- oder in dieser -->

Mit einem .glass-sort__handle im Element startet nur der Griff ein Ziehen, ein Tipp auf das Element wählt es also weiterhin, und ein Finger auf dem Griff zieht sofort, ohne langes Drücken. Das Ziehen selbst bringt GlassKit Elements mit: <glk-list sortable> und <glk-tree sortable> ordnen mit der Maus, per Touch nach langem Drücken und mit der Tastatur — die Leertaste hebt ein Element auf, die Pfeiltasten bewegen es, die Leertaste legt es ab, Escape bricht ab —, sagen jeden Schritt an, nehmen Breadcrumbs als Ablageziele und lassen die Elemente über --gl-sort-duration (180 ms) mit --gl-sort-easing (ease-out) an ihren Platz gleiten. Unter prefers-reduced-motion gleitet nichts.

KlasseBeschreibung
.is-sort-sourceDas gezogene Element an seinem alten Platz, auf 40 % verblasst
.is-sort-before, .is-sort-afterEine 2 px breite Linie in der Primärfarbe über oder unter dem Element, eingerückt auf --gl-tree-level
.is-sort-inlineMit .is-sort-before oder .is-sort-after: Die Linie steht aufrecht an der Anfangs- oder Endkante des Elements, für Elemente nebeneinander — Kacheln in einem Raster (seit 1.34.0)
.is-sort-insideDas Element nimmt das gezogene auf: getönt, mit Ring — seit 1.34.0 auch über dem Hover einer interaktiven Zeile und über dem gewählten Zustand
.is-sort-refusedDas Element nimmt es nicht: Fehlerfläche und -ring; eine Linie wird rot
.glass-sort__ghostDie angehobene Kopie, die dem Zeiger folgt; [popover] im Top Layer
.glass-sort__handleDer Griff: Nur er startet ein Ziehen, und ein Finger darauf zieht sofort

Toast

Sichtbar via .is-visible. Varianten: --success, --error, --warning.

Mit Aktion (seit 1.19.0): __action ist eine kleine Pille im Ton des Toasts — primär beim schlichten Toast, in der Statusfarbe bei einer Variante — und __close das × am Ende; es braucht ein aria-label. Beide haben eine Trefferfläche von 44 px. Ein Toast mit Aktion darf bis 480 px breit werden, der Text bricht um, die Knöpfe schrumpfen nie; er sollte stehen bleiben, bis einer davon benutzt wird. Der Toast sitzt --gl-toast-top vom oberen Rand (56 px, wenn nicht gesetzt) — setz ihn unter deine Kopfzeile und nimm bei viewport-fit=cover env(safe-area-inset-top) dazu. Er bleibt auch mit Aktion oben: ein Ort für jeden Toast.

Dokument erfolgreich hochgeladen!
Neue Version verfügbar
<div class="glass-toast glass-toast--success"> <svg class="glass-toast__icon">...</svg> <span class="glass-toast__text">Erfolgreich!</span> </div> <!-- with an action --> <div class="glass-toast is-visible" role="status"> <svg class="glass-toast__icon">...</svg> <span class="glass-toast__text">Neue Version verfügbar</span> <button class="glass-toast__action">Neu laden</button> <button class="glass-toast__close" aria-label="Schließen"><!-- × SVG --></button> </div>
KlasseBeschreibung
.glass-toast, .is-visibleOben fixiert, sichtbar eingeblendet
--success, --error, --warningFärben das Symbol; tönen die Aktion in der Statusfarbe
__icon, __text22-px-Symbol, Nachricht (bricht um, gibt nach)
__actionEine Aktion: Pille im Ton des Toasts, 44 px Trefferfläche, schrumpft nie (seit 1.19.0)
__closeDas ×, 28 px mit 44 px Trefferfläche (seit 1.19.0)
--gl-toast-topAbstand vom oberen Rand, Vorgabe var(--gl-space-4xl) (seit 1.19.0)

In der obersten Ebene (seit 1.39.0): Ein Toast liegt unter einem Modal oder Sheet, das der Browser in die oberste Ebene legt, ganz gleich welcher z-index — unter dem verschwommenen Overlay, unlesbar. Gib ihm popover="manual" und rufe showPopover() auf — bei jedem Zeigen neu, nach hidePopover(), damit er über einem seither geöffneten Dialog liegt. .glass-toast[popover] nimmt die eigene Box des Popovers zurück; Ort und Aussehen bleiben die des Toasts. Halte ihn offen, während er versteckt ist: Er bleibt unsichtbar und lässt den Zeiger durch, und als Live-Region wird er weiter vorgelesen. Neben einem Modal ist er zu sehen, aber wie alles außerhalb des Dialogs nicht bedienbar und nicht angesagt. <glk-toast> in GlassKit Elements tut das.

<div class="glass-toast glass-toast--error" role="status" popover="manual">…</div> // vor jedem Zeigen: neu hinein, oben auf if (toast.matches(':popover-open')) toast.hidePopover(); toast.showPopover(); toast.classList.add('is-visible');

Popover

Verankertes Dropdown- / Menü-Container. Trigger und .glass-popover in einem .glass-popover-anchor verschachteln. Sichtbarkeit wird über die .is-open-Klasse gesteuert — das Umschalten benötigt ein bisschen JavaScript.

  • Teilen
  • Duplizieren
  • Löschen
<div class="glass-popover-anchor"> <button class="glass-btn glass-btn--secondary" onclick="togglePop(this)">Menü öffnen</button> <div class="glass-popover"> <ul class="glass-list glass-list--bare"> <li class="glass-list__item glass-list__item--interactive">Teilen</li> <li class="glass-list__item glass-list__item--interactive">Löschen</li> </ul> </div> </div> <script> // Hinweis: NICHT `togglePopover` benennen — kollidiert mit der nativen // HTMLElement.togglePopover()-API-Methode. function togglePop(btn) { btn.nextElementSibling.classList.toggle('is-open'); } </script>

Im Top Layer

Seit 1.32.0 kann ein Popover im Top Layer aufgehen. Gib ihm das Attribut popover und öffne ihn mit showPopover(): Er liegt über jedem Stacking Context, eine Box mit overflow: hidden drumherum schneidet ihn nicht mehr ab, und geschlossen nimmt er keinen Platz ein, wo die Klassen-Variante die Seite noch um ihre Box verbreiterte. Neben den Trigger setzt der Browser ihn allerdings nicht. Im Top Layer ist der Popover position: fixed: Ein Skript misst den Trigger, schreibt left und top in Viewport-Pixeln, klappt den Popover auf die andere Seite, wo der Viewport keinen Platz lässt, und hält ihn dort, während die Seite scrollt; data-side="top" lässt ihn von seiner Unterkante aus wachsen, wenn er über dem Trigger aufgeht. <glk-popover> aus GlassKit Elements erledigt all das, und Tooltip und Menü gehen genauso auf. Probier es: Die Vorschau schneidet ab, was ihre Box verlässt, den Popover im Top Layer aber nicht; ein Klick daneben oder Escape schließt ihn.

Über jedem Stacking Context: overflow: hidden an der Vorschau schneidet ihn nicht ab.

<button class="glass-btn glass-btn--secondary glass-btn--auto" id="filter-button">Filter</button> <div class="glass-popover" id="filters" popover="manual">…</div> <script> const button = document.getElementById('filter-button'); const pop = document.getElementById('filters'); button.addEventListener('click', () => { if (pop.matches(':popover-open')) return pop.hidePopover(); pop.showPopover(); const r = button.getBoundingClientRect(); pop.style.left = `${r.left}px`; pop.style.top = `${r.bottom + 8}px`; }); </script>

Klassen-Referenz

KlasseBeschreibung
.glass-popover-anchorPositionierungs-Kontext für den Popover (umschließt Trigger + Popover)
.glass-popoverFloating Glass-Surface, ausgeblendet bis .is-open
.glass-popover.is-openSichtbarer Zustand — Fade- + Scale-Animation
.glass-popover--topÖffnet nach oben (über dem Trigger)
.glass-popover--startLinke Kante am Trigger ausrichten
.glass-popover--endRechte Kante am Trigger ausrichten
.glass-popover[popover]Im Top Layer, position: fixed: mit showPopover() geöffnet, per Skript an Viewport-Koordinaten platziert, geschlossen ohne Platzbedarf (seit 1.32.0)
[data-side="top"]Mit [popover]: wächst von der Unterkante aus, für einen Popover über seinem Trigger

Hinweis: die Toggle-Funktion darf nicht togglePopover heißen — dieser Name kollidiert mit der nativen HTMLElement.togglePopover()-Methode der HTML Popover API, und Inline-onclick-Handler werfen einen NotSupportedError.

Tooltip

Ein kurzer Name oder Hinweis für ein Bedienelement, beim Überfahren und beim Tastaturfokus (seit 1.32.0): ein Button nur mit Icon, die Einträge einer Rail, eine Zeilenaktion. Kleine Schrift auf dem Glas des Popovers mit seinem Kantenlicht, so weit getönt, dass sie über allem lesbar bleibt (6,3:1 oder mehr, auch Weiß unter einem dunklen Tooltip), höchstens --gl-tooltip-max-width (240 px) breit; längerer Text bricht um. Gib ihm role="tooltip"; das Bedienelement verweist mit aria-describedby darauf, oder mit aria-labelledby, wo der Tooltip sein einziger Name ist.

<button class="glass-pill" aria-labelledby="tip-archive"><!-- SVG --></button> <div class="glass-tooltip" id="tip-archive" role="tooltip" popover="hint">Archivieren</div> <button class="glass-btn glass-btn--secondary glass-btn--sm glass-btn--auto" aria-describedby="tip-share">Link teilen</button> <div class="glass-tooltip" id="tip-share" role="tooltip" popover="hint">Jeder mit dem Link …</div>

Er geht im Top Layer auf wie der Popover, mit popover="hint" (ein Browser ohne hint nimmt es als manual), per Skript neben sein Bedienelement gesetzt und umgeklappt oder verschoben, damit er im Viewport bleibt. Ohne das Attribut ist er ein schlichter Block wie in der Vorschau. WCAG 1.4.13 verlangt dreierlei von einem Tooltip: Der Zeiger kann auf ihn wechseln, ohne dass er zugeht, Escape schließt ihn, ohne den Fokus zu bewegen, und er bleibt, solange das Bedienelement überfahren oder fokussiert ist. <glk-tooltip> aus GlassKit Elements bringt dieses Verhalten mit: Er erscheint nach 400 ms beim Überfahren, sofort beim Tastaturfokus und bei langem Drücken per Touch, während ein Tipp ihn nie zeigt.

TokenUngesetztWirkung
--gl-tooltip-max-width240pxDie größte Breite, bevor der Text umbricht
--gl-tooltip-bg--gl-color-bg-dark zu 94 %Seine Fläche: der dunkelste Hintergrund des Themes, im hellen Theme hell
--gl-tooltip-color--gl-color-textSeine Schrift

Eine zweite Zeile (seit 1.39.0): .glass-tooltip__hint nach dem Namen sagt, was eine Wahl tut, in normaler Stärke und der gedämpften Farbe (--gl-tooltip-hint-color).

Statt der VorschauEin breiterer Editor, die Felder bleiben in der Mitte
<div class="glass-tooltip" popover="hint">Statt der Vorschau<span class="glass-tooltip__hint">Ein breiterer Editor, die Felder bleiben in der Mitte</span></div>

Input

Bitte gültige E-Mail angeben
EUR

Datums- und Zeitfelder — type="date", time, datetime-local, month — nehmen dieselbe Klasse. Sie verzichten auf das native Aussehen (seit 1.17.0): iOS zeichnete sie breiter als ihre Spalte, ein leeres schmaler als ein gefülltes, den Wert mittig. Jetzt sitzen sie wie jedes andere Feld in ihrer Spalte, der Wert am Anfang; der native Picker öffnet weiter.

<div class="glass-input-group"> <label class="glass-label">Label</label> <input class="glass-input" type="text"> <span class="glass-hint">Hilfetext</span> </div>

Präfix und Suffix

Ein Icon, eine Währung, eine Einheit oder ein Button im Kasten des Feldes (seit 1.22.0): das Input in .glass-input-wrap legen und __prefix und __suffix daneben setzen. Sie liegen auf dem Feld, mittig, an seinem Anfang und Ende — von rechts nach links tauschen sie die Seiten. Das Padding des Feldes wächst um ihre Breite, --gl-input-prefix-size und --gl-input-suffix-size: 18px, ein Icon, wenn nicht gesetzt — für eine breitere Einheit setzen. Es folgt dem Modifier --prefix / --suffix, oder einem sichtbaren Affix, wo :has() unterstützt wird. Ein Klick auf Text, ein Icon oder ein deaktiviertes Bedienelement erreicht das Feld; aktive Bedienelemente, Links und Elemente mit tabindex nehmen ihren eigenen, formularassoziierte Custom Elements wie <glk-button> eingeschlossen (seit 1.22.2). Das tun sie auch in einem Container, der mit pointer-events: none gesperrt ist: ihn mit inert sperren oder sie deaktivieren. <glk-input> aus GlassKit Elements hat darauf die Slots prefix und suffix und misst die Breite selbst.

<div class="glass-input-wrap glass-input-wrap--suffix" style="--gl-input-suffix-size: 30px"> <input class="glass-input" inputmode="decimal"> <span class="glass-input-wrap__suffix">EUR</span> </div>

Ein Wert oder Platzhalter, der zu lang für das Feld ist, endet mit einer Ellipsis, solange das Feld nicht bearbeitet wird, und ein .glass-label ohne Text nimmt keinen Platz ein (beides seit 1.25.0). Datums- und Zeitfelder behalten den Picker-Button des Browsers; --gl-input-picker-icon: none blendet ihn in Chromium aus — etwa wenn das Präfix des Feldes der Picker ist; dann dort showPicker() aufrufen. Es blendet auch den Pfeil eines Feldes mit <datalist> aus.

Am Ende der Label-Zeile

Seit 1.33.0 hat die Label-Zeile eines Feldes einen Platz an ihrem Ende, für einen Link wie „Passwort vergessen?“ auf der Zeile von „Passwort“. .glass-input-group__label-end im Markup hinter das Feld setzen und --label-end an die Gruppe: Die Gruppe zeichnet ihn auf der Zeile des Labels, an ihrem Ende, in der kleinen Schrift des Labels, und die Tastatur erreicht ihn trotzdem nach dem Feld — E-Mail, Passwort, „Passwort vergessen?“, Anmelden, die Reihenfolge, die sich in einem Anmeldeformular am besten liest. Um eine Textarea oder ein Select funktioniert es genauso; in GlassKit Elements haben <glk-input>, <glk-textarea> und <glk-select> dafür einen Slot label-end.

<div class="glass-input-group glass-input-group--label-end"> <label class="glass-label" for="passwort">Passwort</label> <input class="glass-input" id="passwort" type="password"> <a class="glass-input-group__label-end" href="/zuruecksetzen">Passwort vergessen?</a> <!-- hinter dem Feld --> </div>

Seit 1.37.0 kann der Platz einen Zeichenzähler halten, .glass-input-group__counter: „48/145“ in der gedämpften Farbe, und mit .is-over jenseits der Grenze in der Fehlerfarbe — ein per Skript gesetzter Wert kann länger sein, maxlength hält nur das Tippen auf. Aktualisiere ihn bei input und verbinde ihn über aria-describedby mit dem Feld, damit ein Screenreader ihn mit dem Feld vorliest. Nach einem Link am selben Platz hält er Abstand.

50/145
<div class="glass-input-group glass-input-group--label-end"> <label class="glass-label" for="meta">Meta-Beschreibung</label> <textarea class="glass-textarea" id="meta" maxlength="145" aria-describedby="meta-count"></textarea> <span class="glass-input-group__label-end glass-input-group__counter" id="meta-count">0/145</span> <!-- bei input aktuell gehalten --> </div>

Seit 1.38.0 hält --gl-label-hidden: 1 an einer Gruppe oder einem Container um Felder jedes .glass-label als Namen seines Feldes und nimmt es aus dem Blick — gesetzt aus einer Container- oder Media-Query, der ein Attribut nicht folgen kann: Felder in einer Tabelle unter einem gemeinsamen Kopf nur, solange die Tabelle breit genug ist, mit ihren Labels darüber, wenn sie zur Liste wird. Es braucht Style Queries auf Custom Properties: Chrome 111, Safari 18 und Firefox 151. Ohne sie bleiben die Labels über ihren Feldern, jedes Feld 24 px höher — für ältere Browser setze die Regeln eines unsichtbaren Labels auf .glass-label, oder auf ::part(label) in GlassKit Elements, in dieselbe Query, wie unten. In GlassKit Elements reicht es in <glk-input>, <glk-select> und <glk-textarea>, deren label-hidden dasselbe für ein Feld tut.

.week { container: week / inline-size; } @container week (min-width: 560px) { .day { --gl-label-hidden: 1; } /* Labels unsichtbar, weiter die Namen */ /* ältere Browser: dieselben Regeln am Label */ .day .glass-label { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } }

Textarea

<textarea class="glass-textarea"></textarea>

Eine Textarea, die sich nicht bearbeiten lässt, hat keinen Anfasser zum Vergrößern (seit 1.25.0). --gl-textarea-field-sizing: content lässt Textareas mit ihrem Text wachsen, in Browsern mit field-sizing; standardmäßig ist es aus, weil es rows ignoriert.

:root { --gl-textarea-field-sizing: content; }

Select

<select class="glass-select"> <option>Bitte wählen…</option> </select>

Der Chevron ist --gl-select-chevron, ein SVG in der gedämpften Textfarbe des jeweiligen Themes — seit 1.20.0; vorher blieb er auch im hellen Theme weiß und war kaum zu sehen. Mit einem eigenen url(…) im Token lässt er sich ersetzen. Von rechts nach links wandern der Chevron und der Platz für ihn nach links.

Ein Platzhalter ist das native Muster: eine erste Option mit leerem Wert, disabled und hidden, gewählt, solange nichts gewählt ist. Sie lässt sich nicht auswählen, und mit required sendet das Formular nicht, solange sie steht. Seit 1.37.0 zeigt das Feld sie in der Platzhalterfarbe, wie ein Eingabefeld; eine erste echte Option, die für „nichts gewählt“ stand, ließ sich nicht wählen, weil die schon gezeigte Option zu wählen nichts ändert.

<select class="glass-select"> <option value="" disabled hidden selected>Art wählen…</option> <option>Facebook</option> </select>

Toggle Switch

<label class="glass-toggle"> <input class="glass-toggle__input" type="checkbox"> <span class="glass-toggle__track"> <span class="glass-toggle__thumb"></span> </span> <span class="glass-toggle__label">Label</span> </label>

Das Input ist unsichtbar, liegt aber über der Spur und nimmt den Klick selbst an (seit 1.20.0): Ein Testwerkzeug, das den Schalter über seine Rolle anklickt, erreicht es — vorher zeichnete die Spur darüber und nahm den Klick. Es deckt nur die Spur ab; der Labeltext, der auch einen Link enthalten kann, bleibt beim Label. Ein leeres __label nimmt keinen Platz ein, ein Schalter ohne Text hält also keine 12 px Lücke dafür frei. Beides gilt genauso für Checkbox und Radio Button. Von rechts nach links beginnt der Knopf rechts.

Die Spur ist --gl-toggle-track-bg, wenn aus, und --gl-toggle-track-bg-checked, wenn an (seit 1.25.0; ungesetzt --gl-surface-2 und der Primärverlauf): eine kräftigere Spur für eine Seite ohne Glas, auf der die Fläche schwer zu sehen sein kann.

Wo der Schalter steht (seit 1.39.0): .glass-toggle--label-start setzt den Text davor, und .glass-toggle--full spannt das Bedienelement über seine Zeile, sodass mit beiden der Text am Anfang der Zeile steht und der Schalter an ihrem Ende, wie in den Einstellungen von iOS — von rechts nach links gespiegelt. Die ganze Zeile ist das Label, ein Klick irgendwo darauf schaltet. .glass-checkbox--label-start, --full und dasselbe für .glass-radio tun es für Checkbox und Radio. In einer Listen-Zeile kommt der Schalter stattdessen in __trailing, mit dem Titel der Zeile als Namen.

<label class="glass-toggle glass-toggle--label-start glass-toggle--full"> <input class="glass-toggle__input" type="checkbox"> <span class="glass-toggle__track"><span class="glass-toggle__thumb"></span></span> <span class="glass-toggle__label">Benachrichtigungen</span> </label>

Checkbox

Wie beim Toggle liegt das Input über der Box, und ein leeres __label nimmt keinen Platz ein (seit 1.20.0).

Radio Button

Wie beim Toggle liegt das Input über dem Kreis, und ein leeres __label nimmt keinen Platz ein (seit 1.20.0).

Fläche, Rand und Punkt sind Tokens (seit 1.25.0): --gl-radio-bg und --gl-radio-bg-checked, --gl-radio-border und --gl-radio-border-checked (ganze Border-Werte, also beiden dieselbe Breite geben), --gl-radio-dot-size (10px) und --gl-radio-dot-color (ungesetzt die Tinte auf der Primärfarbe, --gl-color-on-primary, weiß, solange ein Projekt sie nicht umstellt — seit 1.32.0; vorher immer weiß). Ein Ring in der Textfarbe, der beim Auswählen die Akzentfarbe annimmt:

:root { --gl-radio-bg: transparent; --gl-radio-bg-checked: transparent; --gl-radio-border: 2px solid color-mix(in srgb, var(--gl-color-text) 60%, transparent); --gl-radio-border-checked: 2px solid var(--gl-color-primary); --gl-radio-dot-size: 50%; --gl-radio-dot-color: var(--gl-color-primary); }

Range Slider

75%

Progress Bar

Standard68%
Erfolg100%
KlasseBeschreibung
.glass-progressBasis-Container
--sm / --lgTrack-Höhe: 4px / 12px
--success / --errorFarb-Varianten

Accordion

Die ausführliche Antwort auf diese Frage.
Die Antwort auf Frage zwei.

Ein offener Eintrag nimmt die Höhe seines Inhalts an, gleich wie lang, und gleitet dorthin: __content ist ein Grid mit einer Zeile, die von 0fr auf 1fr geht (seit 1.30.0). Bis dahin endete er bei 500 px und schnitt den Rest ab, und das Gleiten lief im Tempo dieser Grenze statt in dem des Inhalts. Inhalt, der während des offenen Zustands wächst, wächst mit. Unter prefers-reduced-motion öffnet er ohne Gleiten.

Seit 1.39.0 zeigt der Trigger einen Ring für die Tastatur, inset 0 0 0 2px var(--gl-border-focus) darin, mit dem Item gerundet, wie Listen- und Baumzeilen ihn haben; vorher hatte er keinen. .glass-accordion__summary nach dem Titel hält eine Zusammenfassung des Items, die Werte eines geschlossenen Eintrags: im Raum bis zum Chevron, auf einer Zeile mit Auslassungspunkten, klein und gedämpft, im Button, sodass ein Klick darauf, Enter und Leertaste das Item öffnen wie auf dem Titel. Sie nimmt nur den Raum, den der Titel lässt. Blende sie aus, solange das Item offen ist, wo die Felder die Werte selbst zeigen.

Die Felder des Eintrags.
<button class="glass-accordion__trigger"> <span>Details der Neuigkeit</span> <span class="glass-accordion__summary">Sommerfest · 31.10.2026 · Lena Hoffmann</span> <span class="glass-accordion__trigger-icon">…</span> </button>

Skeleton

Ladeplatzhalter: schimmernde Zeilen stehen für Text, der noch nicht da ist. Die Breite jeder Zeile setzt der Aufrufer; der Schimmer folgt dem Theme und steht unter prefers-reduced-motion still. Den Bereich mit aria-busy="true" markieren, damit Hilfstechnik das Laden erkennt.

<div class="glass-skeleton" aria-busy="true"> <div class="glass-skeleton__line glass-skeleton__line--title" style="width:55%"></div> <div class="glass-skeleton__line"></div> <div class="glass-skeleton__line" style="width:80%"></div> </div>
KlasseBeschreibung
.glass-skeletonStapel aus Zeilen, 10px Abstand
.glass-skeleton__lineEine Schimmerzeile (12px); Breite per Inline-Style
.glass-skeleton__line--titleHöhere Zeile (18px) für eine Überschrift

Tabelle

Eine gewöhnliche <table> auf Glas: Haarlinien zwischen den Zeilen, gedämpfter Kopf, Zahlen rechtsbündig in Tabellenziffern. Der Wrapper .glass-table-wrap scrollt seitlich, wo die Spalten nicht passen. Die Klasse gehört auf die Tabelle selbst — aus einem Shadow Root erreicht ::slotted() die Zellen nicht, deshalb ist das bewusst Dokument-CSS.

DatumPostenBetrag
2026-09-18Rechnung 10421.240,00
2026-09-19Erstattung−80,00
2026-09-20Offen–
<div class="glass-table-wrap"> <table class="glass-table"> <thead><tr><th>Datum</th><th class="glass-table__num">Betrag</th></tr></thead> <tbody><tr><td>2026-09-18</td><td class="glass-table__num">1.240,00</td></tr></tbody> </table> </div>
KlasseBeschreibung
.glass-table-wrapOptionaler Container mit seitlichem Scrollen
.glass-tableDie Tabelle: Haarlinien, Hover-Hervorhebung
.glass-table__numRechtsbündig, Tabellenziffern — auf th und td
.glass-table__mutedSekundäre Textfarbe für eine Zelle

Sortieren, fester Kopf, Zeilenaktionen

Seit 1.32.0 bringt die Tabelle mit, was eine Liste von Rechnungen oder Nutzern braucht. Eine Kopfzelle, die sortiert, enthält einen __sort-Button mit dem Pfeil und trägt aria-sort: "ascending", "descending" oder "none". Die Seite sortiert die Zeilen, oft auf dem Server; die Klassen zeigen nur den Zustand. --sticky an der Tabelle und .glass-table-wrap--sticky an ihrem Wrap halten den Kopf sichtbar: Der Wrap scrollt in beide Richtungen, höchstens --gl-table-max-height (60vh) hoch, und die Kopfzellen haften an seinem oberen Rand, auf --gl-table-header-bg. Zellen mit __actions tragen die Aktionen der Zeile — ein Menü, Icon-Buttons mit Tooltips — und bleiben am Zeilenende, während die Tabelle seitlich scrollt; sobald sie das tut, gibt --scrolls an der Tabelle ihnen eine eigene Fläche, und ein .glass-table-frame um den Wrap schattiert jeden Rand, hinter dem weitere Spalten warten (data-more-start, data-more-end). Sortiere die Spalten, scrolle die Tabelle, mach das Fenster schmal:

Status Tarif
2026-1048Bäckerei Nordwind2026-10-271.240,00OffenBusiness jährlich
2026-1047Zahnarztpraxis am Hafen2026-10-2689,00BezahltStarter monatlich
2026-1046Blumen Glockenblume2026-10-252.310,50BezahltBusiness monatlich
2026-1045Studio Turmfalke2026-10-24412,80ÜberfälligStarter jährlich
2026-1044Yoga Morgenlicht2026-10-2375,00BezahltStarter monatlich
2026-1043Radwerk Nord2026-10-22640,00OffenBusiness jährlich
2026-1042Kanzlei Weber & Roth2026-10-213.480,00BezahltBusiness jährlich
<div class="glass-table-frame"> <div class="glass-table-wrap glass-table-wrap--sticky"> <table class="glass-table glass-table--sticky"> <thead><tr> <th aria-sort="descending"> <button class="glass-table__sort" type="button">Rechnung <svg class="glass-table__sort-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 19V5M6 11l6-6 6 6"/></svg> </button> </th> <th class="glass-table__num" aria-sort="none"><!-- Button wie oben --></th> <th class="glass-table__actions" aria-label="Aktionen"></th> </tr></thead> <tbody> <tr> <td>2026-1048</td> <td class="glass-table__num">1.240,00</td> <td class="glass-table__actions"><!-- ein Menü-Button --></td> </tr> </tbody> </table> </div> </div> /* Kompakte Icon-Buttons in den Aktionszellen */ .glass-table__actions { --gl-btn-padding-x-sm: 8px; --gl-btn-height-sm: 32px; }

Ein paar Zeilen Skript setzen --scrolls und die Attribute des Rahmens aus der Scrollposition des Wraps, wie in der Vorschau. <glk-table> aus GlassKit Elements erledigt all das um eine native Tabelle in seinem Light DOM, die ihre Semantik behält, Kopieren und Einfügen, die Suche auf der Seite und vom Server gerendertes Markup: data-sort an einer Kopfzelle fügt den Button und aria-sort hinzu, ein Klick sendet ein abbrechbares glk-table-sort, und sticky-header und list-below schalten die Layouts.

Als Karten

--list macht aus jeder Zeile eine Karte für schmale Stellen, mit dem Namen der Spalte vor jedem Wert aus data-label an den Zellen. Die Aktionen wandern an das obere Ende der Karte, Zahlen behalten ihre Tabellenziffern. Der Kopf bleibt für assistive Technik da, nur unsichtbar, und die Labels sind generierter Inhalt ohne eigenen Text, sodass ein Screenreader die Spalte nicht doppelt hört. Eine Tabelle, die nicht mehr als Tabelle gelayoutet wird, kann ihre Semantik verlieren; setz ihre Rollen deshalb ausdrücklich. <glk-table list-below="640px"> schaltet nach der eigenen Breite um, nicht nach der des Viewports, und schreibt Labels und Rollen selbst.

RechnungKundeBetragStatus
2026-1048Bäckerei Nordwind1.240,00Offen
2026-1047Zahnarztpraxis am Hafen89,00Bezahlt
<table class="glass-table glass-table--list" role="table"> <thead role="rowgroup">…</thead> <!-- th role="columnheader" --> <tbody role="rowgroup"> <tr role="row"> <td role="cell" data-label="Rechnung">2026-1048</td> <td class="glass-table__num" role="cell" data-label="Betrag">1.240,00</td> <td class="glass-table__actions" role="cell">…</td> </tr> </tbody> </table>

Leer

Solange keine Zeile da ist, trägt __empty unter der Tabelle einen Leerzustand, und der Kopf bleibt; hidden nimmt ihn wieder weg.

RechnungKundeBetragStatus

Keine Rechnungen

Nichts passt zu diesem Filter.

<div class="glass-table-wrap"> <table class="glass-table"><thead>…</thead><tbody></tbody></table> <div class="glass-table__empty"> <div class="glass-empty">…</div> </div> </div>
KlasseBeschreibung
__sort, __sort-iconButton, der eine Kopfzelle füllt und nach ihrer Spalte sortiert, und sein Pfeil: nach oben für ascending, nach unten für descending, blass, solange unsortiert
th[aria-sort]Der Zustand: ascending, descending, none
--sticky + .glass-table-wrap--stickyDer Kopf haftet oben an einem Wrap, der in beide Richtungen scrollt, höchstens --gl-table-max-height hoch
__actionsZelle für Zeilenaktionen, am Zeilenende, dort gehalten, während die Tabelle seitlich scrollt
--scrollsGesetzt, solange die Tabelle seitlich scrollt: die Aktionen auf eigener Fläche, mit Schatten
.glass-table-frameUm den Wrap; schattiert den Start- oder Endrand mit data-more-start, data-more-end
__emptyUnter der Tabelle in ihrem Wrap, der Platz für einen Leerzustand
--listJede Zeile eine Karte, die Labels aus data-label, die Aktionen am oberen Ende
TokenUngesetztWirkung
--gl-table-cell-padding9px 10pxDer Raum in den Zellen des Körpers
--gl-table-header-padding8px 10pxDer Raum in den Kopfzellen
--gl-table-max-height60vhWie hoch ein haftender Wrap wird, bevor er scrollt
--gl-table-header-bg--gl-color-bg-mid zu 94 %Die Fläche des haftenden Kopfs und die der Aktionen, solange die Tabelle seitlich scrollt

Fließtext

Fließtext, den die App nicht von Hand geschrieben hat — gerendertes Markdown, Hilfe- und Infoseiten. Eine Klasse auf dem Container stylt Überschriften, Absätze, Listen, Links, Code, Zitate, Bilder und Tabellen. Bewusst Dokument-CSS: der Inhalt ist Light DOM, und ::slotted() endet auf der ersten Ebene.

Erste Schritte

glasskit.css einbinden, data-theme auf <html> setzen, fertig. Alles Weitere ist optional — siehe die Design-Tokens.

  • Dark Mode ist der Standard
  • Light Mode über data-theme="light"
Der Glaseffekt braucht einen farbigen Hintergrund, um sichtbar zu sein.
<article class="glass-prose"> <!-- gerendertes Markdown: h1–h3, p, ul/ol, a, code, pre, blockquote, img, hr, table --> </article>

Segmentwahl

Eine kleine, exklusive Auswahl als ein Bedienelement — Ampel, Vormittag / Nachmittag, Modus. Knöpfe in einer role="group"; der gewählte trägt aria-pressed="true", und genau an diesem Attribut hängt das Styling, damit Zustand und Semantik nicht auseinanderlaufen. Die Ton-Modifier setzen einen Punkt in einer Zustandsfarbe vor die Beschriftung. Probier es: Klick oder Tab auf einen Knopf und Leertaste. Mehr Optionen als Platz? Ohne Modifier bleibt die Zeile einzeilig und läuft über den Rand; --scroll lässt sie seitwärts scrollen, --wrap bricht sie um. Beide lassen sich mit --full kombinieren.

<div class="glass-segmented glass-segmented--full" role="group" aria-label="Status"> <button class="glass-segmented__item glass-segmented__item--success" aria-pressed="true"> <span class="glass-segmented__dot"></span>Grün </button> <button class="glass-segmented__item" aria-pressed="false">Hilfeseite</button> </div>
KlasseBeschreibung
.glass-segmentedDie Gruppe: eingelassene Glas-Schiene, Knöpfe darin
--fullKnöpfe teilen sich die Breite
--scrollEine Zeile, die seitwärts scrollt, wenn die Optionen nicht passen; Scrollbar versteckt (seit 1.17.0)
--wrapBricht die Zeile um, wenn die Optionen nicht passen (seit 1.17.0)
__itemEine Option; aria-pressed="true" markiert die gewählte, disabled dämpft
__item--success / --warning / --errorTon: setzt --gl-segmented-tone für den Punkt
__dotDer Punkt vor der Beschriftung, in der Tonfarbe
__iconEin Icon vor der Beschriftung, in der Farbe der Option (seit 1.28.0)
__badgeEine Zahl nach der Beschriftung, in der Tonfarbe, wo ein Ton gesetzt ist; in __icon an der Ecke des Icons (seit 1.29.0)
__badge--primary, --success, --warning, --errorDer Zähler in eigener Farbe, gleich welcher Ton, ohne Punkt (seit 1.31.0)
--iconsNur Icons, quadratische Optionen; die Namen stehen in aria-label (seit 1.28.0)
__icon--colorEin farbiges Logo behält seine eigenen Füllungen und Konturen; ohne zieht ein SVG Linien wie ein Linien-Icon (seit 1.35.0)
--dropletDie gewählte Option sitzt auf einem Glastropfen, der zur nächsten gleitet (seit 1.27.0)

Icons

Seit 1.28.0 kann eine Option ein Icon in .glass-segmented__icon zeigen, vor ihrer Beschriftung: Ein SVG zieht seine Linien in der Farbe der Option, jedes andere Element — ein Font-Icon, eine <img>-Maske — übernimmt diese Farbe, ein Font-Icon auch die 18 px der Box. Unter dem Tropfen wächst das Icon wie der Ton-Punkt. --icons macht eine Zeile nur aus Icons, etwa einen Schalter Hell / Dunkel / Automatisch: Die Optionen werden quadratisch, und jede trägt ihren Namen als aria-label, als title auch für den Tooltip. Die Icon-Box bekommt aria-hidden="true", damit der Text eines Font-Icons nicht in den Namen gerät.

Ein farbiges Logo als Icon — das Zeichen einer Bildquelle — nimmt .glass-segmented__icon--color (seit 1.35.0) und behält seine eigenen Füllungen und Konturen; vorher erbten seine Formen die Kontur des Linien-Icons und standen in der Textfarbe umrandet da. --gl-segmented-bg setzt die Spur (seit 1.35.0); auf dem Glas eines Modals oder eines Sheets im dunklen Modus ist sie eine Mulde, --gl-dialog-well, mit der gewählten Option auf --gl-dialog-raised.

<div class="glass-segmented glass-segmented--icons glass-segmented--droplet" role="group" aria-label="Darstellung"> <button class="glass-segmented__item" aria-pressed="true" aria-label="Hell" title="Hell"> <span class="glass-segmented__icon" aria-hidden="true"><!-- SVG oder Font-Icon --></span> </button> <!-- Dunkel, Automatisch --> </div>

Fläche der gewählten Option

Seit 1.28.0 sind Fläche, Schatten und Textfarbe der gewählten Option Tokens, wie beim aktiven Eintrag der Tab-Bar: Auf einem dunklen Marken-Hintergrund ließ die helle Fläche ein weißes Label unter 4,5:1. Alle drei sind ungesetzt; der Tropfen übernimmt dieselbe Fläche und denselben Schatten, ein Wertepaar dient also beiden Darstellungen. Der Schatten steht in Schattenlisten: Abschalten mit einem Nullschatten, nicht mit none.

TokenUngesetztWirkung
--gl-segmented-active-bg--gl-surface-5 (der Tropfen: ein heller Glasverlauf)Fläche der gewählten Option und des Tropfens
--gl-segmented-active-shadow--gl-shadow-btn (der Tropfen: sein eigener)Ihr Schatten
--gl-segmented-active-color--gl-color-text-headingLabel und Icon der gewählten Option und der Option unter einem gezogenen Tropfen
:root { --gl-segmented-active-bg: var(--gl-color-primary); --gl-segmented-active-color: var(--gl-color-on-primary); }

Zähler

Seit 1.29.0 kann eine Option sagen, wie viel hinter ihr wartet — offene Rechnungen, neue Tickets —, mit .glass-segmented__badge nach ihrer Beschriftung: eine kleine Pille in Tabellenziffern, im Fluss, damit eine scrollende Zeile und der Tropfen die Option mit ihr messen. Der Ton-Punkt sagt, dass etwas da ist, das Badge, wie viel. Ohne Ton ist es neutral, eine Tönung der Textfarbe über dem State-Scrim; mit einem nimmt es die Statusfarbe der Badges. In .glass-segmented__icon gesetzt, sitzt es an der Ecke des Icons, wie bei der Tab-Bar — der Platz in einer --icons-Zeile, wo es das Icon verdeckt und deckend ist. Seine Zahl ist kein Name: Die Option bekommt ein aria-label, das sie nennt, „Tickets, 1 offen“.

<button class="glass-segmented__item glass-segmented__item--warning" aria-pressed="false" aria-label="Tickets, 1 offen"> <span class="glass-segmented__dot"></span>Tickets<span class="glass-segmented__badge">1</span> </button> <!-- In einer --icons-Zeile: an der Ecke des Icons --> <span class="glass-segmented__icon" aria-hidden="true"><!-- SVG --><span class="glass-segmented__badge">3</span></span>

Die neutrale Pille hat zwei Tokens, beide Farben und ungesetzt: --gl-segmented-badge-bg, die Tönung über dem Scrim (ungesetzt: 12 % der Textfarbe), und --gl-segmented-badge-color (ungesetzt: die Textfarbe). Gemessen in Chromium, WebKit und Firefox erreichen die Ziffern im dunklen Theme mindestens 5,6:1, im hellen mindestens 4,7:1, auf der gewählten Option und daneben, neutral und in jedem Ton.

Seit 1.31.0 kann ein Zähler eine eigene Farbe tragen, gleich welchen Ton die Option hat: .glass-segmented__badge--primary, --success, --warning und --error geben ihm das Aussehen der Badge-Varianten — die Zustandstönung über doppeltem Scrim, eine Haarlinie, die lesbare Textfarbe — ohne den Punkt, den ein Ton vor die Beschriftung setzt. Hat die Option zusätzlich einen Ton, gewinnt die eigene Farbe des Zählers. Auch seine Größe ist ein Token: --gl-segmented-badge-height (ungesetzt 16 px, zugleich seine Mindestbreite) und --gl-segmented-badge-font-size (ungesetzt 11 px). --gl-segmented-badge-border zieht eine Haarlinie in dieser Farbe um den neutralen Zähler, nach innen, sodass er seine Größe behält. Gemessen in Chromium, WebKit und Firefox erreichen die vier Farben 4,7:1 oder mehr in beiden Themes, inline und an der Ecke des Icons, auf der gewählten Option und daneben.

<button class="glass-segmented__item" aria-pressed="true" aria-label="Offen, 4 neu"> Offen<span class="glass-segmented__badge glass-segmented__badge--error">4</span> </button>

Droplet

Seit 1.27.0 gibt .glass-segmented--droplet der gewählten Option den Tropfen der Tab-Bar: Er läuft zur neuen Option, dehnt sich unterwegs und kommt zur Ruhe, die Schrift, über die er läuft, leuchtet auf, und ein Finger kann ihn ziehen. Deaktivierte Optionen werden übersprungen. In einer scrollenden Zeile (--scroll) scrollt ein Wischen weiterhin; gezogen wird nur der Tropfen selbst, und an den Rändern scrollt die Zeile mit. Der Tropfen folgt aria-pressed; attachDroplets() aus glasskit-droplet.js startet ihn, wie oben.

<div class="glass-segmented glass-segmented--scroll glass-segmented--droplet" role="group" aria-label="Bereich"> <button class="glass-segmented__item" aria-pressed="true">Allgemein</button> <!-- weitere Optionen --> </div>

Schrittanzeige

Fortschritt durch einen kurzen Ablauf: nummerierte Kreise mit Haarlinien dazwischen, erledigte auf der Erfolgsfläche (mit SVG-Haken), der aktuelle in der Primärfarbe (--gl-color-primary-fill, wo ein Projekt sie setzt, seit 1.32.0) und mit aria-current="step".

  1. Tag
  2. Fenster
  3. 3Hund
  4. 4Bestätigen

Die Liste ist ein Größen-Container: unter 360 px behält nur der aktuelle Schritt seine Beschriftung, die anderen zeigen ihre Nummer; davor kürzen Beschriftungen mit Ellipse. Im schmalen Layout behalten die Kreise ihre Größe, und die Linien geben zuerst nach, bis auf 2 px, bevor die aktuelle Beschriftung kürzt (seit 1.28.0): Sieben Schritte behalten eine aktuelle Beschriftung von 88 px ab einer 310 px breiten Spalte ganz, und die Zeile läuft nie über ihren Rand. Maßgeblich ist die Breite des Blocks, nicht des Viewports — hier in einem 300-px-Rahmen:

  1. Tag
  2. 2Zeitfenster wählen
  3. 3Hund
  4. 4Bestätigen
<ol class="glass-steps"> <li class="glass-steps__item glass-steps__item--done"> <span class="glass-steps__num"><!-- check SVG --></span><span class="glass-steps__label">Tag</span> </li> <li class="glass-steps__line" aria-hidden="true"></li> <li class="glass-steps__item glass-steps__item--current" aria-current="step"> <span class="glass-steps__num">2</span><span class="glass-steps__label">Fenster</span> </li> </ol>
KlasseBeschreibung
.glass-stepsDie Liste; ein Größen-Container, in einer Flex-Zeile also mit Breite versehen
__item, __item--done, __item--currentEin Schritt und seine Zustände
__numDer Kreis: Nummer, oder ein SVG-Haken wenn erledigt
__labelDie Beschriftung; unter 360 px ausgeblendet, außer am aktuellen Schritt
__lineHaarlinie zwischen den Schritten

Bottom-Sheet

Das Bottom-Sheet ist das mobile Geschwister des Modals: ein Overlay, das einblendet, und ein Panel, das von der Unterkante hochfährt, nur oben gerundet, mit Griff und Safe-Area-Abstand. Eigener Block statt Modal-Modifier, weil Layout, Einfahrbewegung und Geste verschieden sind. --inline bettet das Panel ohne Overlay in den Fluss ein; die Overlay-Form braucht ein wenig JavaScript, unten gezeigt und in GlassKit Elements als <glk-sheet> umgesetzt. Seit 1.30.0 darf das Overlay ein natives <dialog> sein, wie beim Modal: Mit showModal() geöffnet liegt es im Top Layer, über jedem fixierten oder transformierten Element der Seite, gleich welcher z-index, und die Seite dahinter ist inert. dialog.glass-sheet-overlay nimmt dem Dialog seine eigene Box und seinen Backdrop und schneidet ab, ohne ein Scroll-Container zu sein (seit 1.39.0: overflow: clip — mit hidden scrollte ein Fokus im Panel, während es hereinglitt, das Overlay, und das Panel stand sofort an seinem Platz oder schwang darüber hinaus und zurück); einblenden mit showModal() und dann .is-active, ausblenden durch Entfernen von .is-active und close() nach transitionend. <glk-sheet> macht es so.

Umbuchen auf …

Inhalt des Sheets: Liste, Text, Formular. Dieses hier ist eingebettet.
<div class="glass-sheet-overlay" hidden> <div class="glass-sheet" role="dialog" aria-modal="true"> <div class="glass-sheet__grip"></div> <h2 class="glass-sheet__title">Umbuchen auf …</h2> <div class="glass-sheet__body">…</div> <div class="glass-sheet__actions">…</div> </div> </div> // öffnen: hidden entfernen, Reflow erzwingen, im nächsten Frame .is-active setzen // schließen: .is-active entfernen, hidden nach transitionend (Sicherheitsnetz 400 ms)
KlasseBeschreibung
.glass-sheet-overlayFixiertes Overlay: Scrim, Blur, Panel an der Unterkante; startet bei Opacity 0
.is-activeOverlay sichtbar, Panel hochgefahren — per Skript geschaltet
.glass-sheetDas Panel: Milchglas, oben gerundet, unten Safe-Area-Abstand
--inlineIm Fluss, ohne Overlay, rundum gerundet
__grip, __title, __body, __actionsGriff, Überschrift, Inhalt, Aktionsspalte
__head, __headingDer Kopf: nimmt Platz und Abstand des Titels ein; __heading hält __eyebrow, den Titel und __subtitle (seit 1.35.0). Mit hidden nimmt er keinen Platz ein (seit 1.36.0)
__eyebrowEine kurze Zeile über dem Titel, kleine Versalien in der gedämpften Farbe
__subtitleUnter dem Titel: ein Badge, eine Zeile Text, klein in der gedämpften Farbe (seit 1.36.0)
__leading, __trailingVor und nach dem Titel: eine Marke oder ein Icon-Chip, ein Badge
__closeEin runder Schließen-Button aus Glas, 32 px mit 44 px Trefferfläche, am Ende des Kopfs; --gl-sheet-close-size, --gl-sheet-close-icon-size (16 px) und --gl-sheet-close-icon-stroke (2) ändern ihn und sein Zeichen (seit 1.36.0)
.glass-sheet-overlay--start, --endDas Panel an dieser Seite, volle Höhe, von der Seite, ohne Griff, Aktionen nebeneinander (seit 1.35.0)
.glass-sheet-overlay--modelessKein Scrim und kein Blur; nur das Panel nimmt den Zeiger — für ein mit show() geöffnetes Sheet

Der Kopf

Seit 1.35.0 hält der Kopf mehr als den Titel: __eyebrow darüber („Wiederherstellen“), __leading davor (ein Icon-Chip von 56 px), __trailing danach (ein Badge) und __close an seinem Ende, einen runden Button aus Glas. __head nimmt Platz und Abstand des Titels ein; ein Titel allein darin steht wie ohne ihn. Seit 1.36.0 steht __subtitle unter dem Titel, in __heading: Ein Badge dort lässt dem Titel seine Breite, wo einer in __trailing ihn in einem schmalen Sheet zusammendrückte. Drei Tokens ändern den Schließen-Button und sein Zeichen — --gl-sheet-close-size (32 px), --gl-sheet-close-icon-size (16 px) und --gl-sheet-close-icon-stroke (2) —, und ein leerer Kopf mit hidden nimmt keinen Platz ein; vorher behielt er seine 8 px.

Wiederherstellen

Version von 09:41

1× in deiner App
Wiederherstellen ersetzt die aktuelle Version.
<div class="glass-sheet__head"> <span class="glass-sheet__leading"><!-- ein Icon-Chip --></span> <div class="glass-sheet__heading"> <span class="glass-sheet__eyebrow">Wiederherstellen</span> <h2 class="glass-sheet__title">Version von 09:41</h2> </div> <span class="glass-sheet__trailing"><span class="glass-badge">1× in deiner App</span></span> <button class="glass-sheet__close" aria-label="Schließen"><!-- × --></button> </div>

Bildergalerie

1× in deiner App
Das Badge steht unter dem Titel, der seine Breite behält. Der Schließen-Button ist schlicht, 44 px mit einem Zeichen von 20 px.
<div class="glass-sheet__heading"> <h2 class="glass-sheet__title">Bildergalerie</h2> <span class="glass-sheet__subtitle"><span class="glass-badge glass-badge--success">1× in deiner App</span></span> </div> .my-sheet { --gl-sheet-close-size: 44px; --gl-sheet-close-icon-size: 20px; --gl-sheet-close-icon-stroke: 1.8; } .my-sheet .glass-sheet__close:not(:focus-visible) { background: transparent; border-color: transparent; box-shadow: none; }

An einer Seite, nicht-modal

.glass-sheet-overlay--end oder --start (seit 1.35.0) setzt das Panel an diese Seite des Fensters: so hoch wie das Fenster weniger --gl-sheet-side-inset (16 px) an beiden Enden und so weit vom Rand entfernt, --gl-sheet-side-width (440 px) breit, mit den Ecken einer Karte. Es gleitet von dieser Seite herein, von rechts nach links gespiegelt, hat keinen Griff, und seine Aktionen stehen nebeneinander an seinem Fuß — ein Seitenpanel auf dem Desktop. --modeless lässt die Seite bedienbar: kein Scrim und kein Blur, nur das Panel nimmt den Zeiger. Öffne diesen Dialog mit show() statt showModal(): Die Seite ist nicht inert, ihre gewählte Zeile bleibt sichtbar, und ein später geöffnetes Modal liegt über dem Sheet. --gl-sheet-z-index (1000) stapelt es. Seit 1.36.0 setzen --gl-sheet-side-inset-block-start und --gl-sheet-side-inset-block-end oben und unten für sich — etwa unter einer festen Kopfleiste — und fallen auf --gl-sheet-side-inset zurück, das für die Seite und den Weg herein bleibt.

<dialog class="glass-sheet-overlay glass-sheet-overlay--end"> <!-- showModal(), dann .is-active --> <div class="glass-sheet">…</div> </dialog> <dialog class="glass-sheet-overlay glass-sheet-overlay--end glass-sheet-overlay--modeless"> <!-- show() --> <div class="glass-sheet">…</div> </dialog> /* Unter einer festen Leiste von 68px, 16px vom oberen Rand */ .my-side-sheet { --gl-sheet-side-inset: 24px; --gl-sheet-side-inset-block-start: 100px; }

Dunkles Glas

Das Glas eines Modals oder eines Sheets ist heller als die Seite, oben am meisten. Im dunklen Modus beginnt seit 1.35.0 der Schimmer beider beim mittleren Schimmer der Karte, und die Bedienelemente darauf nehmen eigene Werte, wo die Seite keine gesetzt hat: Ein Segmented Control liegt in einer Mulde mit der gewählten Option eine Stufe dunkler, ein tertiärer Button ist eine Stufe dunkler, Badges und tertiäre Buttons nehmen keinen eigenen Blur, und Akzenttext nimmt --gl-color-primary-on-glass. Oben auf einem hohen Sheet im dunklen Modus las weiße Schrift auf Badges und Segmenten in 1.34.0 2,9 bis 4,7:1 und liest jetzt 4,7 bis 7,7:1, in Chromium, WebKit und Firefox. Der helle Modus behält Schimmer und Bedienelemente. --gl-sheet-bg und --gl-modal-bg ersetzen die ganze Fläche.

TokenDunkler WertVerwendung
--gl-dialog-glow-topvar(--gl-card-glow-mid)Wo der Schimmer eines Modals und eines Sheets beginnt
--gl-dialog-wellrgba(0, 0, 0, 0.10)Die Spur eines Segmented Controls auf Dialogglas
--gl-dialog-raisedvar(--gl-surface-3)Seine gewählte Option
--gl-dialog-controlvar(--gl-surface-1)Ein tertiärer Button auf Dialogglas
--gl-badge-selected-colorvar(--gl-color-text)Die Beschriftung eines gewählten Badges, überall

Alle fünf sind im hellen Modus ohne Wert. Ein Segmented Control oder ein Badge, dessen Tokens die Seite setzt, behält sie auch auf Dialogglas.

Leerzustand

Für leere Listen und Ergebnisseiten: eine zentrierte Spalte mit runder Icon-Fläche, Titel, kurzem gedämpftem Text und Platz für eine oder mehrere Aktionen nebeneinander.

Noch keine Buchungen

Such dir einen Tag und einen Hund aus — das Team freut sich.

<div class="glass-empty"> <span class="glass-empty__icon"><!-- SVG --></span> <p class="glass-empty__title">Noch keine Buchungen</p> <p class="glass-empty__text">…</p> <div class="glass-empty__action">…</div> </div>
KlasseBeschreibung
.glass-emptyZentrierte Spalte
__icon48-px-Kreisfläche, darin ein 24-px-Strich-Icon (::slotted(svg)-Zwilling für Elements)
__title, __textÜberschrift und gedämpfter Text (max. 38 Zeichen breit)
__actionPlatz für eine oder mehrere Aktionen: nebeneinander mit 8 px Abstand, umbrechend und zentriert, wenn es eng wird (seit 1.17.0)

Wochenstreifen

Eine Reihe von Tages-Chips, die seitwärts scrollt — ein Buchungshorizont, die Tage um heute. Jeder Chip ist ein <button> mit Wochentag, Tageszahl und einem Punkt, den ein Ton-Modifier einfärbt; ohne Ton bleibt der Punkt transparent, damit Chips mit und ohne Markierung gleich hoch sind. Der gewählte Chip trägt aria-pressed="true" wie in der Segmentwahl; :disabled oder aria-disabled="true" dimmt einen Tag, der nicht wählbar ist. Die Scrollbar ist versteckt. Gib jedem Knopf ein aria-label mit dem vollen Datum — das sichtbare „Di 22“ ist kein Name. <glk-date-strip> in GlassKit Elements baut das aus Daten, mit Pfeiltasten und zentriertem gewähltem Chip.

<div class="glass-date-strip" role="group" aria-label="Pick a day"> <button class="glass-date-strip__day glass-date-strip__day--today" aria-pressed="false" aria-label="Tuesday, 22 September 2026"> <span class="glass-date-strip__wd">Tu</span><span class="glass-date-strip__num">22</span> <span class="glass-date-strip__mark glass-date-strip__mark--success"></span> </button> <button class="glass-date-strip__day" aria-pressed="true" aria-label="…">…</button> <button class="glass-date-strip__day" aria-pressed="false" disabled>…</button> </div>
KlasseBeschreibung
.glass-date-stripScrollende Reihe; versteckte Scrollbar; 4 px Innenabstand, damit der eigene Überlauf den Fokusring nicht beschneidet
__dayChip-Knopf, mindestens 44 px; [aria-pressed="true"] gewählt, auf --gl-color-primary-fill, wo gesetzt, sonst der Primärfarbe (seit 1.32.0), :disabled / [aria-disabled="true"] gedimmt
__day--todayUnterstreicht die Zahl
__wd, __numWochentag in Versalien, Tageszahl
__mark6-px-Punkt; ohne Ton transparent
__mark--primary, --success, --warning, --errorTon des Punkts über --gl-date-strip-tone; auf dem gewählten Chip wird jeder Ton zur Schriftfarbe auf Primär (--gl-date-strip-ink)

Monatskalender

Ein Monat: Titel zwischen zwei runden Navigationsknöpfen, Wochentagszeile und 42 quadratische Tages-Knöpfe mit bis zu drei Punkten unter der Zahl. Der gewählte Tag trägt aria-pressed="true", --today zeichnet einen warmen Rahmen, --other dimmt einen Tag des Nachbarmonats, :disabled oder aria-disabled="true" dimmt einen Tag außerhalb des erlaubten Bereichs — die zweite Form bleibt für Pfeiltasten fokussierbar. Der Block ist eine role="group" aus Knöpfen mit dem vollen Datum als Namen, kein role="grid". <glk-calendar> in GlassKit Elements baut ihn, mit Tastaturnavigation und Namen aus Intl.

September 2026
<div class="glass-calendar"> <div class="glass-calendar__head"> <button class="glass-calendar__nav" aria-label="Previous month"><!-- SVG chevron --></button> <div class="glass-calendar__title">September 2026</div> <button class="glass-calendar__nav" aria-label="Next month">…</button> </div> <div class="glass-calendar__grid" role="group" aria-label="Pick a day"> <span class="glass-calendar__wd" aria-hidden="true">Mo</span> <!-- × 7 --> <button class="glass-calendar__day glass-calendar__day--other" aria-pressed="false" aria-label="Monday, 31 August 2026"> <span>31</span><span class="glass-calendar__marks"></span> </button> <button class="glass-calendar__day glass-calendar__day--today" aria-pressed="true" aria-label="…"> <span>22</span><span class="glass-calendar__marks"><span class="glass-calendar__mark glass-calendar__mark--warning"></span></span> </button> <!-- 42 cells --> </div> </div>
KlasseBeschreibung
.glass-calendarBlock; __head mit __title (Ellipse, wenn es eng wird) und zwei __nav-Knöpfen, 32 px rund, mit SVG-Chevron
__gridSieben Spalten, 4 px Abstand
__wdWochentags-Kopfzelle, gedämpfte Versalien
__dayQuadratischer Tages-Knopf; [aria-pressed="true"] gewählt, auf --gl-color-primary-fill, wo gesetzt, sonst der Primärfarbe (seit 1.32.0), --today warmer Rahmen, --other gedimmter Nachbarmonat, :disabled / [aria-disabled="true"] gedimmt und nicht wählbar
__marks, __mark5-px-Punkte unter der Zahl, bis zu drei; __mark--primary / --success / --warning / --error über --gl-calendar-tone, die Schriftfarbe auf dem gewählten Tag über --gl-calendar-ink

Bildauswahl

Ein Bild wählen, mit Vorschau: eine 72-px-Fläche — das Bild mit object-fit: cover oder ein Platzhalter-Icon — und eine Spalte mit Beschriftung, Hinweis und Aktionen, die gewöhnliche .glass-btn-Knöpfe sind; der Block bringt keine eigene Knopfoptik mit. --round macht die Fläche rund für Avatare; eine lange Beschriftung bricht um und drückt die Fläche nie zusammen. Jedes Bild, nicht nur Fotos. <glk-image-picker> in GlassKit Elements ergänzt Dateidialog, EXIF-Drehung und Verkleinerung.

Foto des Hundes JPG oder PNG · wird vor dem Hochladen auf 1024 px verkleinert
Profilfoto
<div class="glass-image-picker" role="group" aria-labelledby="photo-label"> <div class="glass-image-picker__preview glass-image-picker__preview--round"><img src="…" alt=""></div> <div class="glass-image-picker__meta"> <span class="glass-image-picker__label" id="photo-label">Profile photo</span> <span class="glass-image-picker__hint">JPG or PNG</span> <div class="glass-image-picker__actions"> <button class="glass-btn glass-btn--secondary glass-btn--sm glass-btn--auto">Change</button> <button class="glass-btn glass-btn--tertiary glass-btn--sm glass-btn--auto">Remove</button> </div> </div> </div>
KlasseBeschreibung
.glass-image-pickerZeile: Fläche links, Spalte rechts, 14 px Abstand
__preview72-px-Fläche; img füllt sie, svg ist der 28-px-Platzhalter in der gedämpften Icon-Farbe
__preview--roundKreis, für Avatare
__metaSpalte, die nachgibt (min-width: 0)
__label, __hintBeschriftung im Look von .glass-label, bricht überall um; kleiner gedämpfter Hinweis
__actionsUmbrechende Reihe aus .glass-btn-Knöpfen

Utilities

Stack (vertikal)

.gl-stack .gl-stack--xs (8px) .gl-stack--sm (12px) .gl-stack--md (16px) .gl-stack--lg (20px) .gl-stack--xl (24px)

Row (horizontal)

.gl-row .gl-row--xs (8px) .gl-row--sm (12px) .gl-row--md (16px)

Margin Top

.gl-mt-sm (12px) .gl-mt-md (16px) .gl-mt-lg (20px) .gl-mt-xl (24px)

Margin Bottom

.gl-mb-sm (12px) .gl-mb-md (16px) .gl-mb-lg (20px) .gl-mb-xl (24px)

Text

.gl-text-center .gl-text-muted .gl-text-sm

Layout

.gl-px (padding horizontal) .gl-w-full (width: 100%) .gl-flex-1 (flex: 1)

📋 Alle Klassen auf einen Blick

KomponenteKlassenModifier / States
Hintergrund.glass-bg--has-tab-bar
Navigation.glass-nav–
Pill-Button.glass-pill:disabled
Theme-Toggle.glass-theme-toggle–
Titel.glass-title–
Card.glass-card--glow
Button.glass-btn--primary, --secondary, --tertiary, --sm, --lg, --auto, :disabled
Badge.glass-badge--primary, --success, --warning, --error, --interactive, --selected, --md
Avatar.glass-avatar--sm, --lg; --gl-avatar-size (seit 1.37.0)
Divider.glass-divider–
Status.glass-status–
Liste.glass-list--flush, --bare, __item--interactive, __item--center, __item--danger, __item--accent, __leading--lg, __subtitle--wrap, __value, __section-header, .is-flash
Baum.glass-tree__row, __twisty, __icon, __label, __trailing, __group, .is-expanded, .is-leaf, .is-selected, .is-loading, .is-error, .is-flash, .is-disabled, .is-focus-visible, --gl-tree-level
Sortieren.glass-sort__ghost, .glass-sort__handle.is-sort-source, .is-sort-before, .is-sort-after, .is-sort-inside, .is-sort-refused
Skeleton.glass-skeleton__line, __line--title
Tabelle.glass-table-wrap, -wrap--sticky, -frame, __num, __muted, __sort, __sort-icon, [aria-sort], __actions, __empty, --sticky, --scrolls, --list
Fließtext.glass-prose–
Segmentwahl.glass-segmented--full, --scroll, --wrap, --icons, __item, __item--success/--warning/--error, __dot, __icon, __badge, [aria-pressed], --droplet
Schrittanzeige.glass-steps__item, __item--done, __item--current, __num, __label, __line
Bottom-Sheet.glass-sheet-overlay + .glass-sheet.is-active, --inline, __grip, __title, __body, __actions
Leerzustand.glass-empty__icon, __title, __text, __action
Wochenstreifen.glass-date-strip__day, __day--today, __wd, __num, __mark, __mark--primary/--success/--warning/--error, [aria-pressed], :disabled
Monatskalender.glass-calendar__head, __title, __nav, __grid, __wd, __day, __day--today, __day--other, __marks, __mark--primary/--success/--warning/--error, [aria-pressed], [aria-disabled]
Bildauswahl.glass-image-picker__preview, __preview--round, __meta, __label, __hint, __actions
Popover.glass-popover--top, --start, --end, .is-open, [popover], [data-side="top"]
Tooltip.glass-tooltip[popover]; __hint, eine zweite Zeile (seit 1.39.0)
Menü.glass-menu__item, __item--danger, __check, __icon, __label, __trailing, __separator, __group-label, [aria-checked], [aria-disabled], .is-checked, .is-focused, .is-focus-visible, [popover]
Input.glass-input--error, :disabled; .glass-input-wrap mit __prefix / __suffix; .glass-input-group--label-end mit __label-end und __counter (seit 1.37.0)
Textarea.glass-textarea:disabled
Select.glass-select:disabled; eine Platzhalter-Option in der Platzhalterfarbe (seit 1.37.0)
Suchfeld.glass-search–
Toggle.glass-toggle:checked, :disabled; --label-start, --full (seit 1.39.0)
Checkbox.glass-checkbox:checked, :disabled
Radio.glass-radio:checked, :disabled
Range.glass-range:disabled
Progress.glass-progress--sm, --lg, --success, --error
Modal.glass-modal-overlay.is-active
Toast.glass-toast--success, --error, --warning, .is-visible, __action, __close; [popover] in der obersten Ebene (seit 1.39.0)
Tab-Bar.glass-tab-bar__item mit .is-active, __icon, __label, __badge (--dot seit 1.38.0), __leading, __trailing, --droplet, --vertical, --rail, --labels, --start, --end, --top
Tab-Bar Floating.glass-tab-bar-dock + .glass-tab-bar--floating__accessory, __accessory--accent/--success/--error, __accessory-item, __accessory-label, --accessory-left, -dock--vertical, -dock--start
Breadcrumbs.glass-breadcrumbs__list, __item, __crumb, __current, __overflow, __item--overflow, .is-drop-target, .is-drop-refused
Accordion.glass-accordion.is-open auf Items; __summary neben dem Titel (seit 1.39.0)

🤖 KI / LLM-Referenz

GlassKit liefert eine SKILL.md mit – eine strukturierte, maschinenlesbare Komponenten-Referenz für KI-Assistenten, LLMs und Code-Generierungs-Tools.

FeatureBeschreibung
Copy-Paste HTMLAlle 38 Komponenten mit exakter Verschachtelung und BEM-Hierarchie
Design-Token-TabellenFarben, Surfaces, Blur, Radii, Spacing, Shadows, Typography
State-Klassen-Mappingis-active, is-open, is-visible, :checked – wo was hingehört
Composition Patterns6 fertige Seiten-Layouts: Login, Dashboard, Formular, Modal, Einstellungen, Fortschritt
Häufige FehlerTypische Fehler und deren Korrekturen

Anhängen oder referenzieren Sie SKILL.md, wenn Sie eine KI bitten, GlassKit-HTML zu generieren – für produktionsreife Ergebnisse.

# Dateipfad glasskit/SKILL.md # Auch im npm-Paket enthalten npm install @jungherz-de/glasskit node_modules/@jungherz-de/glasskit/SKILL.md

🧩 Die GlassKit-Familie

GlassKit ist das CSS-Fundament einer dreischichtigen Familie – drei Schichten, eine Designsprache. Für alles jenseits einzelner Komponenten sind die Schwesterprojekte der vorgesehene Weg.

SchichtProjektEinsatz
CSS-Fundament🧊 GlassKit (Sie sind hier)Die Designsprache: Pure-CSS-Komponenten, Design Tokens, Dark & Light.
App-SchichtGlassKit ElementsDrop-in Web Components (glk-button, glk-card, …) für App-UIs.
Website-SchichtGlassKit WebEine komplette Website bauen – das offizielle Astro-Template mit Sections, i18n und SEO auf dem GlassKit-Fundament.