このサイトの右上には、昼・夕暮れ・夜を切り替えるスイッチがあります。ダークモードの拡張版のような機能ですが、仕組みはほぼCSSだけでできています。

カスタムプロパティをモードで束ねる

ルート要素の属性ひとつで、空・文字・カード・草原の色がまとめて切り替わります。

[data-mode="night"] {
  --sky-top: #0a0e2a;
  --sky-mid: #141a44;
  --sky-bot: #243056;
  --ink: #eef2ff;
}
[data-mode="dusk"] {
  --sky-top: #2a1a47;
  --sky-mid: #7a3b6b;
  --sky-bot: #e8814f;
  --ink: #fff3ec;
}

コンポーネント側は var(--sky-top) を参照するだけ。モードが増えても触るのは変数定義だけです。

空は transition でぬるっと移ろわせる

背景のグラデーションに長めの transition を当てると、空が時間とともに移ろうような演出になります。

.sky {
  background: linear-gradient(180deg, var(--sky-top), var(--sky-mid), var(--sky-bot));
  transition: background 1100ms cubic-bezier(0.22, 1, 0.36, 1);
}

星は消さずに、薄くする

星空は要素の付け外しではなく、不透明度の変数で表現しています。夜は 1、夕暮れは 0.45、昼は 0。DOMを触らないので軽量で、モード切替のたびに星が再生成されることもありません。

チラつき対策は「ペイント前の3行」

選んだモードは localStorage に保存していますが、復元のタイミングを誤ると、リロード時に一瞬デフォルトの夜が見えてから昼に切り替わる「フラッシュ」が起きます。対策は、<head> の中に同期実行のインラインスクリプトを置くことです(Astroなら is:inline を付けて、バンドル対象から外します)。

var m = localStorage.getItem("mode") || "night";
document.documentElement.setAttribute("data-mode", m);

最初の描画より前に属性が確定するので、どのモードを選んでいてもチラつきません。

JavaScriptの仕事は「属性の切り替え」と「保存・復元」だけ。残りを全部CSSに任せるのが、軽くて壊れにくい作り方だと思います。