Astro 6 Architektur: Islands, Server Islands & Content Layer API im Detail
Technischer Deep-Dive in Astro 6: Islands Architecture, Server Islands für dynamische Inhalte, die neue Content Layer API und View Transitions mit ClientRouter.
Rechtliches & Info
Dark Mode architektonisch korrekt implementieren: semantische Token-Ebenen, das [data-theme]-Toggle-Muster, prefers-color-scheme ohne Media-Query-Kopplung und CSS color-mix().
Dark Mode ist in vielen Projekten ein nachträglicher Gedanke — man fügt einen dark:-Prefix in Tailwind hinzu und hofft, dass es reicht. Das Ergebnis ist meistens inkonsistent: manche Farben stimmen, andere nicht, und der nächste Entwickler weiß nicht, was das Token-System ist. Dieser Artikel zeigt eine architektonisch saubere Herangehensweise.
Ein primitiver Token ist eine konkrete Farbe: --color-blue-500: #0072BB. Er beschreibt, was der Wert ist, nicht wozu er dient.
/* Primitive Tokens — beschreiben "Was" */
:root {
--color-blue-500: #0072BB;
--color-blue-700: #003d74;
--color-gray-50: #f9fafb;
--color-gray-900: #111827;
}
/* Komponente nutzt primitive Tokens direkt */
.button {
background-color: var(--color-blue-500);
color: var(--color-gray-50);
}
/* Dark Mode: alle Primitiven überschreiben — nicht wartbar */
@media (prefers-color-scheme: dark) {
.button {
background-color: var(--color-blue-700);
color: var(--color-gray-900);
}
}
Für jede Komponente muss man separat Dark Mode definieren. Bei 50+ Komponenten wird das unbeherrschbar.
Semantische Tokens beschreiben wozu eine Farbe dient, nicht was sie ist. Sie sind eine Referenz-Ebene über den primitiven Tokens:
/* Primitive Tokens */
:root {
--color-blue-500: #0072BB;
--color-blue-400: #339ad8;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-white: #ffffff;
}
/* Semantische Tokens — Light Mode (Standard) */
:root {
--color-surface-primary: var(--color-white);
--color-surface-secondary: var(--color-gray-50);
--color-text-primary: var(--color-gray-900);
--color-text-secondary: var(--color-gray-600);
--color-border-default: var(--color-gray-200);
--color-interactive: var(--color-blue-500);
--color-interactive-hover: var(--color-blue-700);
}
Die Komponente nutzt nur semantische Tokens:
.button {
background-color: var(--color-interactive);
color: var(--color-white);
}
.button:hover {
background-color: var(--color-interactive-hover);
}
Das [data-theme]-Attribut auf dem <html>-Element ist die sauberste Methode für einen manuell umschaltbaren Dark Mode:
/* semantics/dark.css */
[data-theme='dark'] {
--color-surface-primary: var(--color-gray-900);
--color-surface-secondary: var(--color-gray-800);
--color-text-primary: var(--color-gray-50);
--color-text-secondary: var(--color-gray-400);
--color-border-default: var(--color-gray-700);
--color-interactive: var(--color-blue-400);
--color-interactive-hover: var(--color-blue-300);
}
Im HTML:
<html data-theme="light"> <!-- oder data-theme="dark" -->
Der Toggle:
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute('data-theme') ?? 'light';
const next = current === 'light' ? 'dark' : 'light';
html.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
Der häufige Fehler: man definiert Dark Mode in @media (prefers-color-scheme: dark) und via [data-theme] — das ergibt Konflikte. Eine klare Strategie:
// Beim Seitenstart (inline script im <head>, vor allem anderen)
function initTheme() {
const saved = localStorage.getItem('theme');
const system = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const theme = saved ?? system;
document.documentElement.setAttribute('data-theme', theme);
}
initTheme();
Das Script muss vor dem ersten Paint laufen — sonst gibt es ein Flash of Incorrect Theme (FOIT). In Astro:
---
// layouts/Layout.astro
---
<html>
<head>
<!-- Inline — MUSS vor allen anderen Scripts stehen -->
<script is:inline>
(function() {
var saved = localStorage.getItem('theme');
var system = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', saved || system);
})();
</script>
<!-- Rest des <head> -->
</head>
is:inline verhindert, dass Astro das Script in den Bundle aufnimmt und verzögert ausführt.
In Tailwind CSS 4 konfiguriert man die Dark-Mode-Variante:
/* global.css */
@import 'tailwindcss';
@custom-variant dark (&:where([data-theme='dark'] *));
Oder einfacher: Tailwind so konfigurieren, dass es data-theme als Selektor nutzt:
// tailwind.config.mjs
export default {
darkMode: ['selector', '[data-theme="dark"]'],
};
Dann funktionieren alle dark:-Utilities von Tailwind automatisch mit dem [data-theme]-Toggle:
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
Inhalt
</div>
CSS color-mix() erlaubt, Farben dynamisch zu variieren, ohne neue primitive Tokens zu definieren:
:root {
--color-interactive: #0072BB;
/* Hover: 15% dunkler */
--color-interactive-hover: color-mix(in srgb, var(--color-interactive) 85%, black);
/* Transparente Variante */
--color-interactive-subtle: color-mix(in srgb, var(--color-interactive) 15%, transparent);
}
Im Dark Mode kann man color-mix() nutzen, um die gleiche Farbe heller statt dunkler zu machen:
[data-theme='dark'] {
/* Im Dark Mode: heller statt dunkler für Hover */
--color-interactive-hover: color-mix(in srgb, var(--color-interactive) 85%, white);
}
Ein abrupter Theme-Wechsel fühlt sich unpoliert an. Eine kurze Transition macht ihn angenehm:
/* Transition nur bei Theme-Wechsel, nicht beim initialen Laden */
.theme-transitioning,
.theme-transitioning * {
transition:
background-color 200ms ease,
color 200ms ease,
border-color 200ms ease !important;
}
function toggleTheme() {
document.documentElement.classList.add('theme-transitioning');
// Theme wechseln
const next = current === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
// Transition-Klasse nach der Transition entfernen
setTimeout(() => {
document.documentElement.classList.remove('theme-transitioning');
}, 300);
}
Die Transition-Klasse verhindert, dass die Übergänge beim Seiten-Laden aktiv sind — nur der manuelle Toggle soll animiert sein.
Ein robustes Dark Mode Token-System braucht mindestens:
/* Semantische Tokens — mindestens diese Rollen */
:root {
/* Hintergründe */
--color-surface-primary: /* Haupthintergrund */;
--color-surface-secondary: /* Sekundäre Flächen, Cards */;
--color-surface-tertiary: /* Subtile Hintergründe */;
--color-surface-inverse: /* Invertierter Hintergrund für Badges etc. */;
/* Text */
--color-text-primary: /* Haupttext */;
--color-text-secondary: /* Unterstützender Text */;
--color-text-disabled: /* Deaktivierter Text */;
--color-text-inverse: /* Text auf dunklem Hintergrund */;
--color-text-link: /* Link-Farbe */;
/* Ränder */
--color-border-default: /* Standard-Trennlinie */;
--color-border-strong: /* Hervorgehobene Ränder */;
--color-border-focus: /* Fokus-Indikator */;
/* Interaktiv */
--color-interactive: /* Primärer CTA */;
--color-interactive-hover: /* Hover-Zustand */;
--color-interactive-pressed:/* Pressed-Zustand */;
--color-interactive-subtle: /* Sekundäre Buttons */;
}
Design System für Ihr Projekt? Wender Media entwirft skalierbare Token-Architekturen mit vollständiger Dark Mode Unterstützung — info@wendermedia.info.
Wender Media unterstützt Sie bei der praktischen Umsetzung — von der technischen Konzeption bis zum Launch. Schreiben Sie uns, wir antworten innerhalb von 24 Stunden.
Jetzt Beratung anfragenKostenlos & unverbindlich — info@wendermedia.info
Technischer Deep-Dive in Astro 6: Islands Architecture, Server Islands für dynamische Inhalte, die neue Content Layer API und View Transitions mit ClientRouter.
Was sich mit Tailwind CSS 4 wirklich ändert: CSS-first Konfiguration mit @theme, der Oxide Compiler, Container Queries, color-mix() und die Migration von v3.
TypeScript strict vollständig in Astro-Projekten nutzen: das strict-Flag, der satisfies-Operator, typsichere Props mit Generics und das Typsystem der Content Collections.