Devlog · Phase 0 · 6/7
Verre, das Design-System von Aubia
Verre ersetzt das alte Design von aubia.dev und gilt künftig auch für aubia.cloud und die Desktop-Anwendung: zwei Themes in neutralen Grautönen, graues Glas, Geist und Komponenten aus einer Registry.
von Mike, Gründer von Aubia Veröffentlicht am Aktualisiert am 9 Min. Lesezeit
- design-system
- verre
- tailwind
- motion



Der dunkle Hintergrund von aubia.dev ist ein fast schwarzes Grau, #0E0F12, ohne den Blaustich, den er bei der ersten Veröffentlichung dieses Artikels hatte. Das helle Theme, "Morgendämmerung", basiert auf einem sehr blassen Grau, #EDEFF2. Beide stammen aus Verre, der neuen visuellen Identität von Aubia, deren Mockup ich am 5. Oktober 2026 freigegeben habe.
Im August beschrieb dieser Artikel eine andere Website: zwei vollständige Catppuccin-Themes, ein Glas, durch das die Farbflächen des Hintergrunds schienen, und Chakra Petch, eine kantige Schrift, die den Überschriften von aubia.dev vorbehalten war. Jede App von Aubia legte ihre eigenen Farben und Komponenten fest, und die Desktop-Anwendung sah vier Theme-Varianten vor, während die Website zwei anbot. Verre gibt den drei Apps eine gemeinsame Identität, und die Website setzt sie als erste um.
Von Luma, dem Komponentenstil von shadcn/ui, habe ich die Eckenradien, die Abstände, die Elevation und die Zustandsanimationen übernommen. Material, Farben, Typografie und Icons legt Verre selbst fest.
Eine shadcn-Registry liefert die Komponenten
Die Komponenten von Verre liegen in einer privaten shadcn-Registry, die über GitHub bereitgestellt wird. Eine App installiert sie nicht als Abhängigkeit: Der Befehl shadcn add kopiert ihren Quellcode in das Repository der App, neben dem Rest ihres Codes. Ein versioniertes npm-Paket habe ich verworfen: Es hätte kompilierten Code anstelle dieser Quellen geliefert, und ich hätte eine weitere private npm-Registry veröffentlichen und absichern müssen.
In die Website habe ich 91 Elemente der Registry installiert. Ein Manifest verzeichnet den SHA-256-Hash jeder kopierten Datei, und ein Test prüft, dass die Kopie mit der Quelle identisch bleibt. Damit diese Prüfung greift, wird eine Kopie nie direkt bearbeitet: Eine Stilanpassung kommt in ein eigenes Stylesheet, das nach denen der Registry importiert wird.
Diese Komponenten bauen auf Base UI 1.9.0 auf, das shadcn inzwischen anstelle von Radix empfiehlt. Base UI übernimmt Fokus, Tastaturnavigation und ARIA-Rollen. Ein als Button gestalteter Link läuft über eine eigene Komponente ButtonLink, weil die Primitive Button einem Link die Rolle eines Buttons aufzwingen würde.
Die Icons stammen von Hugeicons, im Stil Stroke Rounded, unter MIT-Lizenz. Ich habe sie Font Awesome Pro vorgezogen: Dessen Lizenz ist kostenpflichtig, und das freigegebene Mockup ist mit Hugeicons gezeichnet.
Zwei Themes, standardmäßig nach der Systemeinstellung
Verre definiert ein helles Theme, "Morgendämmerung", und ein dunkles, "Nacht". Das dunkle besteht aus neutralen Grautönen für Flächen, Linien und Texte. Von Catppuccin, der Palette der alten Website, behält Verre die Akzente: den Pfirsichton von Latte im hellen Theme, #FE640B, den von Mocha im dunklen, #FAB387, dazu auf der Startseite die Töne Blaugrün und Lavendel der Zwischenüberschriften und den Verlauf der Hauptüberschrift. Pfirsich färbt Links, Aufzählungszeichen und die Umrandung des Tastaturfokus.
Die Farben bilden keine vollständige Palette mehr, die als Klassen bereitsteht. Sie sind nach ihrer Rolle benannt, etwa Hintergrund, Glas, Text oder Akzent, und jede Rolle erhält in jedem der beiden Themes einen eigenen Wert. Ein @theme inline-Block verbindet diese Namen mit denen von shadcn und mit den Utility-Klassen von Tailwind: Eine Komponente verwendet bg-background oder text-muted-foreground, und der Wert folgt dem angezeigten Theme.
Ich habe die Farben des Mockups unverändert übernommen, auch dort, wo Texte in beiden Themes unter den Kontrastschwellen nach WCAG AA liegen: Das Design-System weist auf diese schwachen Kontraste hin, ohne sie zu korrigieren. Die übrigen Anforderungen an die Barrierefreiheit bleiben bestehen: Tastaturnavigation, sichtbarer Fokus, Touch-Ziele von mindestens 44 px auf Bildschirmen unter 768 px Breite, ein Skip-Link.
Mit dem alten Design öffnete sich die Website im dunklen Theme, unabhängig davon, was der Besucher bevorzugte. Ich habe das Theme des Systems zum Standard gemacht: Sie haben Ihre Präferenz bereits in Ihrem Betriebssystem festgelegt, und dieselbe Wahl wird in allen drei Apps gelten. Der Umschalter bietet weiterhin "Hell", "Dunkel" und "System" an.
Das Theme steht vor dem ersten Rendering fest
Ein Skript im <head>, vor den Stylesheets, liest die unter dem Schlüssel aubia-theme gespeicherte Wahl und die Systemeinstellung. Es setzt data-theme für eine ausdrückliche Wahl und data-scheme für die Systemeinstellung: Das CSS wendet das richtige Theme an, ohne auf React zu warten. Ohne JavaScript schaltet die Media Query prefers-color-scheme auf das dunkle Theme, wenn das System es verlangt. Ist der Local Storage blockiert, gilt Ihre Wahl nur für den aktuellen Besuch.
Der Theme-Wechsel selbst ist animiert. Nach einem Klick auf den Umschalter wartet die Seite 200 ms, bis der Indikator des Umschalters auf die neue Wahl geglitten ist, dann legt die View Transition API das neue Theme in 650 ms frei, in einem Kreis, der vom Button ausgeht. Im Modus "System" löst eine Änderung der Einstellung eine Überblendung der ganzen Seite aus. Verlangt das System reduzierte Bewegung, wechselt das Theme schlagartig.
Das CSS des Modus "System" liest data-scheme statt der Media Query. Chromium zeigt bereits die Farben der neuen Einstellung an, wenn es das change-Ereignis auslöst: Folgte die Seite der Media Query, hätte sie sich schon geändert, bevor das Skript den Ausgangszustand des Übergangs erfassen könnte.
Die Karten übernehmen das graue Glas der Navigationsleiste
Der Glaseffekt entsteht durch die Eigenschaft backdrop-filter: Sie zeichnet weich, was hinter einem Element vorbeizieht, und ein halbtransparenter Hintergrund färbt das Ergebnis ein. Im alten Design erhöhte dieser Filter auch die Sättigung, und die Farbflächen des Hintergrunds schienen durch die Karten. Das gewöhnliche Glas des Design-Systems, das der Auswahlmenüs und Benachrichtigungen, erhöht die Sättigung weiterhin auf 160 %, bei einer Unschärfe von 24 px. Das Glas der Navigationsleiste zeichnet ebenso stark weich, entfernt aber jede Farbe:
@utility surface-bar {
background: var(--bar-glass);
border: 1px solid var(--glass-line);
backdrop-filter: var(--bar-blur);
}
--bar-blur hat den Wert blur(24px) grayscale(100%), --bar-glass ist ein Weiß mit 80 % Deckkraft im hellen Theme und ein Grau mit 70 % im dunklen: Die Lichthalos des Hintergrunds färben dieses Glas nicht mehr. Auf der Website übernehmen es die Karten, die Fragen der FAQ sowie in den Artikeln das Titelbild und die Tabellen, ohne Lichtreflex, wenn der Zeiger darüberfährt.
Das Sprachmenü wird außerhalb der Navigationsleiste angezeigt
Hat ein Vorfahrenelement bereits einen backdrop-filter, definiert es eine Backdrop Root: Der Filter eines Nachfahren sieht nicht mehr, was hinter diesem Vorfahren liegt. Das Sprachmenü der Navigationsleiste, ein Menü von Base UI, wird in einem Portal gerendert: Die Navigationsleiste ist nicht mehr sein Vorfahre, und das Menü wendet einen eigenen Weichzeichner an. Das Glas der Navigationsleiste ist auf dem Wurzelelement des Dokuments definiert, damit auch dieses Menü darauf zugreifen kann.
Dieses Menü wird nur auf dem Client gemountet: Seine Links fehlen im initialen HTML. Die Fußzeile wiederholt diese Links daher in einem nativen <details>-Element: Sie stehen im vom Server-Rendering erzeugten HTML, und das Öffnen funktioniert ohne JavaScript. React ergänzt die Navigation mit den Pfeiltasten und das Schließen mit Escape oder per Klick außerhalb und setzt beim Öffnen den Fokus auf die aktuelle Sprache.
Halos und ein Sternenraster unter dem Inhalt
Der Seitenhintergrund besteht aus zwei fest positionierten Ebenen. Die erste zeichnet in jede Ecke eine Lichtellipse in der Farbe, die das Symbol des Logos in derselben Ecke hat: Violett oben links, Rosa oben rechts, Koralle unten links, helles Pfirsich unten rechts, im dunklen Theme zurückhaltender als im hellen. Die zweite ist ein Raster aus 7 px großen Formen, Sonnensternen im hellen Theme und Sternen im dunklen, das im Licht der Halos sichtbar wird. Der Rasterabstand, rund 32 px, teilt die Breite des Inhalts ohne Rest: An beiden Rändern des Inhalts liegt je eine Spalte von Formen.
Überall auf der Website wächst alle 0,3 bis 0,9 Sekunden eine Form und nimmt einen Farbton des Themes an, höchstens sechs gleichzeitig, und eine Lupe hellt die Formen in der Nähe des Zeigers auf. Die Animationsschleife stoppt, wenn der Tab nicht sichtbar ist.
Geist für Text wie für Code
Chakra Petch habe ich entfernt. In Geist ist nun der gesamte Text gesetzt, die Hauptüberschrift der Startseite eingeschlossen, in Geist Mono der Code, die Pfade und die Befehle. Beide Schriften sind variabel: Eine einzige lateinische WOFF2-Datei pro Schrift deckt die Schriftstärken von 100 bis 900 ab.
Sie sind selbst gehostet. Das Schriften-Plugin von laravel-vite-plugin liest die Dateien der installierten Fontsource-Pakete und erzeugt beim Build die @font-face-Deklarationen; die Blade-Direktive @fonts fügt sie samt Preload in alle Seiten ein. Es geht keine Anfrage an einen Schriftanbieter.
Mit display: 'swap' erscheint der Text zunächst in einer Ersatzschrift. Für Geist passt das Plugin die Metriken dieser Ersatzschrift an, und der Text wird beim Eintreffen der Schrift kaum neu umbrochen. Geist Mono behält eine gewöhnliche Monospace-Ersatzschrift: Das Plugin sucht in der Datei die Kategorie der Schrift, Monospace oder serifenlos, findet keine von beiden und würde Arial als Vorlage nehmen.
Bewegung und eine einzige Regel, die sie reduziert
Animationsdauern und Easing-Kurven stammen aus CSS-Variablen, --d-* und --ease-*, die Stylesheets und Skripte gleichermaßen lesen. Die Kurve der Mikrointeraktionen, --ease-out, hat den Wert cubic-bezier(.2, .8, .2, 1).
Beim Laden beginnt kein sichtbarer Text bei Deckkraft null, weil Chromium ein unsichtbares Element vom LCP ausschließt. Die Hauptüberschrift der Startseite gleitet um 14 px herein. Einblendungen bleiben Texten vorbehalten, die weiter unten auf der Seite beim Scrollen erscheinen, sowie den Dekorationen.
Vor Verre betrafen die Regeln für reduzierte Bewegung bestimmte Animationen und ließen die übrigen Übergänge laufen. Verre ersetzt sie durch eine einzige, für die ganze Website:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
transition: none !important;
animation: none !important;
}
}
Jeder Zustand erscheint dann sofort. Farben wechseln weiterhin, nur schlagartig; Bewegungen bei Hover und beim Drücken laufen über die Tailwind-Variante motion-safe:, die dann nicht mehr greift.
Skriptgesteuerte Animationen fallen nicht unter diese Regel, etwa die des Einleitungstexts der Startseite, dessen Sätze Zeichen für Zeichen erscheinen. Sie lesen die Einstellung über den Hook useReducedMotion der Registry, der auf useSyncExternalStore aufbaut: Er abonniert das change-Ereignis von matchMedia, und eine Änderung der Einstellung während des Besuchs stoppt die laufende Animation sofort. Der Server kennt diese Einstellung nicht. Der Hook gibt daher beim Server-Rendering und während der Hydrierung false zurück und liest danach den tatsächlichen Wert, wie es die React-Dokumentation zu useSyncExternalStore beschreibt. Das Server-HTML enthält bereits den letzten Satz, und der Einleitungstext behält ihn bei reduzierter Bewegung.
Der Code in den Artikeln behält die Farben von Catppuccin
Die Codeblöcke dieses Blogs hebt Phiki 2.2 beim Build auf dem Server farbig hervor, mit Catppuccin Latte für das helle und Mocha für das dunkle Theme. Eine PHP-Klasse kopiert diese beiden Themes und ersetzt ihre Neutraltöne, also Hintergrund, Text, Kommentare und Satzzeichen, durch CSS-Variablen von Verre; entfernt ein Phiki-Update eine der Farbregeln, die sie ersetzt, bricht die Klasse ab, statt ein unvollständiges Theme auszuliefern. Ein Codeblock übernimmt daher den Hintergrund des angezeigten Themes, und nur seine Syntaxfarben stammen noch aus Catppuccin.
Die Beta von Aubia ist kostenlos und zeitlich unbegrenzt. Melden Sie sich an, um Ihre Einladung zu erhalten.
Auf die Warteliste