Praxistest · Stand: 31. August 2026
Tag- und Nachtmodus nach dem echten Sonnenstand — ganz ohne API
Diese Website wechselt selbständig zwischen hellem und dunklem Erscheinungsbild. Nicht nach einer festen Uhrzeit und nicht nach der Systemeinstellung des Besuchers, sondern nach dem tatsächlichen Sonnenauf- und -untergang an meinem Wohnort. Wenn hier in Altensteig die Sonne hinter dem Waldrand verschwindet, wird die Seite dunkel — für alle Leser, überall.
Der naheliegende Weg dorthin wäre eine kostenlose Schnittstelle gewesen: Breitengrad und Längengrad hinschicken, Zeiten zurückbekommen, fertig. Genau das empfiehlt praktisch jede Anleitung, die man zu diesem Thema findet. Ich habe es trotzdem anders gemacht, und der Grund dafür ist der eigentlich interessante Teil dieses Artikels.
Warum die naheliegende Lösung die falsche war
Ein Schnittstellen-Aufruf im Browser ist kein technisches Detail, sondern eine Datenweitergabe. Jeder Besucher hätte bei jedem Seitenaufruf seine IP-Adresse an einen fremden Server geschickt, zusammen mit dem Zeitpunkt und der Information, dass er gerade diese Seite liest. Alles nur, um eine Farbe zu bestimmen.
Diese Seite verspricht an mehreren Stellen ausdrücklich, ohne Tracking, ohne Werbe-Cookies und ohne eingebundene Fremddienste zu arbeiten. Das steht in der Datenschutzerklärung, es steht in meinen Bewerbungen bei den Partnerprogrammen, und es steht als Versprechen auf der Startseite. Eine hübsche Spielerei hätte dieses Versprechen gebrochen — für Leser unsichtbar, aber deswegen nicht weniger falsch.
Die Erkenntnis dahinter ist banal und wird trotzdem ständig übersehen: Der Sonnenstand ist keine Information, die man abfragen muss. Er ist berechenbar. Ort und Zeitpunkt genügen, der Rest ist Astronomie aus dem 19. Jahrhundert. Ein Browser erledigt diese Rechnung in deutlich unter einer Millisekunde — ohne Netzwerk, ohne Wartezeit, ohne dass jemand mitliest.
Was dabei herauskommt
Das folgende Diagramm ist mit genau derselben Formel gerechnet, die auch oben im Seitenkopf läuft. Die beiden Linien sind Sonnenauf- und -untergang, der eingefärbte Bereich dazwischen ist der Tag — also die Zeitspanne, in der diese Website 2026 im hellen Modus erscheint:
Die Formel
Grundlage ist die klassische Sonnenaufgangsgleichung. Sie braucht Breitengrad, Längengrad und das Datum und liefert daraus zwei Werte: den Zeitpunkt des Sonnenhöchststands und den halben Tagbogen, also die Spanne vom Höchststand bis zum Untergang. Aufgang und Untergang sind dann schlicht Höchststand minus beziehungsweise plus dieser Spanne.
Hier ist die vollständige Funktion, so wie sie auf dieser Seite
arbeitet. Sie liefert true, wenn die Sonne über
Altensteig gerade über dem Horizont steht:
function tagInAltensteig() {
var R = Math.PI / 180, B = 48.5885, L = 8.6003, // Breite, Länge
jd = Date.now() / 864e5 + 2440587.5, // Julianisches Datum
n = Math.round(jd - 2451545.0008), // Tage seit J2000
js = n - L / 360, // östlicher = früher
M = (357.5291 + 0.98560028 * js) % 360, // mittlere Anomalie
C = 1.9148 * Math.sin(M * R) // Mittelpunktsgleichung
+ 0.02 * Math.sin(2 * M * R)
+ 0.0003 * Math.sin(3 * M * R),
la = (M + C + 282.9372) % 360, // ekliptische Länge
tr = 2451545 + js // Sonnenhöchststand
+ 0.0053 * Math.sin(M * R)
- 0.0069 * Math.sin(2 * la * R),
de = Math.asin(Math.sin(la * R) * Math.sin(23.44 * R)), // Deklination
w = Math.acos((Math.sin(-0.833 * R) - Math.sin(B * R) * Math.sin(de)) /
(Math.cos(B * R) * Math.cos(de))) / R / 360; // halber Tagbogen
if (w !== w) return !matchMedia("(prefers-color-scheme: dark)").matches;
return jd > tr - w && jd < tr + w;
}
Vier Stellen darin verdienen eine Erklärung.
Die minus 0,833 Grad sind kein Tippfehler. Sonnenaufgang gilt als der Moment, in dem der obere Sonnenrand den Horizont berührt, nicht der Mittelpunkt — das sind schon einmal gut 0,26 Grad. Dazu kommt die Lichtbrechung in der Atmosphäre, die uns die Sonne noch sehen lässt, wenn sie geometrisch längst untergegangen ist. Beides zusammen ergibt diesen krummen Wert.
Die Zeile js = n - L / 360 ist die
Stelle, an der ich mir eine Stunde Fehlersuche eingehandelt habe. Die
Tageslänge stimmte auf zwei Minuten genau, aber Auf- und Untergang
lagen beide gut eine Stunde zu spät. Der Grund war das Vorzeichen:
Die Lehrbuchformel rechnet traditionell mit westlicher
Länge, Altensteig liegt aber östlich. Und wer östlich wohnt, sieht
die Sonne früher, nicht später. Ein Minuszeichen, eine Stunde
Suche.
Der Vergleich w !== w ist ein Test
auf NaN, den einzigen Wert in JavaScript, der sich
selbst nicht gleicht. Nördlich des Polarkreises geht die Sonne im
Sommer gar nicht unter, dann hat der Arkuskosinus keine Lösung. In
Altensteig kann das nicht passieren, aber wer die Funktion für
Tromsø nachbaut, bekommt auf diesem Weg einen sauberen Rückfall auf
die Systemeinstellung des Besuchers statt eine kaputte Seite.
Gerechnet wird durchgehend in koordinierter Weltzeit, und das ist bequemer, als es klingt. Es gibt keine Zeitzonen-Logik, keine Sommerzeit-Sonderfälle und keine Fehler durch falsch eingestellte Uhren in fernen Ländern. Die Frage lautet ja nicht „wie spät ist es beim Besucher“, sondern „steht die Sonne gerade über Altensteig“ — und die beantwortet ein einziger Zeitstempel.
Der Einbau
Damit die Seite nicht kurz hell aufblitzt, bevor sie dunkel wird,
gehört der Code als kleines Skript direkt in den
<head>, vor das Stylesheet und ohne
defer. Er setzt ein Attribut am Wurzelelement, an dem
sich anschließend das CSS bedient:
window.vhPhase = function () { return tagInAltensteig() ? "tag" : "nacht"; };
try {
var phase = window.vhPhase(),
t = localStorage.getItem("vh-theme"); // manuelle Wahl?
// Die manuelle Wahl gilt nur für die laufende Phase und verfällt
// beim nächsten Sonnenauf- bzw. -untergang.
if (t && localStorage.getItem("vh-phase") !== phase) {
localStorage.removeItem("vh-theme");
localStorage.removeItem("vh-phase");
t = null;
}
document.documentElement.setAttribute(
"data-theme", t || (phase === "tag" ? "light" : "dark"));
} catch (e) {
document.documentElement.setAttribute("data-theme", "dark");
}
Das try ist dabei nicht bloß Zierde: In privaten
Fenstern und bei blockierten Website-Daten wirft schon der reine
Zugriff auf localStorage eine Ausnahme. Ohne diese
Absicherung bliebe die Seite komplett ohne Farbschema.
Im Umschalt-Knopf wird beim Speichern die aktuelle Phase mitgeschrieben. Das sind die zwei Zeilen, die den Verfall überhaupt erst möglich machen:
localStorage.setItem("vh-theme", next);
if (window.vhPhase) localStorage.setItem("vh-phase", window.vhPhase());
Und im CSS hängt dann alles an diesem einen Attribut. Die dunkle Fassung ist bei mir der Normalfall, die helle wird über den Attribut-Selektor gesetzt:
:root { --bg: #090b10; --text: #eceff6; }
[data-theme="light"] { --bg: #f6f4ef; --text: #14181f; }
body { background: var(--bg); color: var(--text); }
Warum die manuelle Wahl verfällt
Der Umschalter oben im Seitenkopf bleibt selbstverständlich erhalten — niemand soll sich meiner Astronomie beugen müssen. Die eigentlich interessante Frage war, wie lange so eine Entscheidung gelten soll. Für immer? Dann wäre die Automatik nach dem ersten Klick für diesen Besucher tot.
Meine Antwort: Die Wahl gilt für die laufende Phase. Wer nachts auf hell schaltet, behält hell — bis zum nächsten Sonnenaufgang. Danach übernimmt wieder die Automatik. Der schöne Nebeneffekt fiel mir erst beim Bauen auf: Der Rücksprung passiert immer genau in dem Moment, in dem Automatik und Nutzerwunsch ohnehin dasselbe wollen. Es gibt also nie eine Situation, in der die Seite jemandem vor der Nase umspringt.
Nachbauen und prüfen
Für den eigenen Ort ersetzt man B und L
durch die eigenen Koordinaten. Beide findet man, indem man in einer
Kartenanwendung lange auf den gewünschten Punkt tippt. Anschließend
sollte man die Rechnung unbedingt gegen die Wirklichkeit prüfen; ich
habe dafür die markanten Tage des Jahres genommen:
| Datum 2026 | Sonnenaufgang | Sonnenuntergang | Tageslänge |
|---|---|---|---|
| 20. März (Tagundnachtgleiche) | 06:29 | 18:37 | 12 h 08 min |
| 21. Juni (längster Tag) | 05:23 | 21:31 | 16 h 08 min |
| 31. August (heute) | 06:41 | 20:11 | 13 h 30 min |
| 21. Dezember (kürzester Tag) | 08:15 | 16:32 | 08 h 17 min |
Alle Werte gelten für Altensteig in Ortszeit, die Abweichung gegenüber amtlichen Tabellen liegt unter zwei Minuten. Für einen Farbwechsel ist das erheblich genauer als nötig — und wer es präziser braucht, baut vermutlich keine Website, sondern ein Observatorium.
Die erste Zeile ist übrigens ein guter Test dafür, ob man die Formel wirklich verstanden hat: An der Tagundnachtgleiche sollten Tag und Nacht doch gleich lang sein, hier stehen aber 12 Stunden und 8 Minuten. Das ist kein Fehler, sondern exakt die vorhin erklärten minus 0,833 Grad. Der Tag ist an diesem Datum tatsächlich ein paar Minuten länger als die Nacht, weil die Atmosphäre uns die Sonne vorne und hinten ein Stück länger zeigt.
Fazit
Der Mehraufwand gegenüber der Schnittstellen-Lösung betrug vielleicht eine Stunde, den größten Teil davon der Vorzeichenfehler. Der Gewinn: kein Netzwerk-Aufruf, keine Wartezeit, keine Abhängigkeit von einem Dienst, der irgendwann abgeschaltet wird oder Geld kostet — und vor allem keine Besucherdaten, die das Haus verlassen.
Es lohnt sich, bei jeder eingebundenen Fremd-Schnittstelle einmal zu fragen: Ist das wirklich Information, die ich holen muss? Oder ist es etwas, das mein Programm selbst ausrechnen kann? Bei Sonnenzeiten, Feiertagen, Umrechnungen und Zeitzonen lautet die Antwort überraschend oft: rechnen. Und wenn diese Seite gerade dunkel ist, dann nicht, weil ein fremder Server das so gesagt hat — sondern weil hier draußen wirklich die Sonne untergegangen ist.