Astroはコンポーネントごとに <style> を書くと、自動的にクラス名をスコープしてくれる機能を持っています。便利な機能ですが、このサイトでは使わず、src/styles/global.css という1枚のファイルにすべてのスタイルを集約しています。

ムードの切り替えは「全体で1つの状態」

このサイトの最大の特徴は、<html data-mode="night|dusk|day"> という1つの属性で、空・カード・文字色などページ全体の見た目が連動して変わることです。

[data-mode="night"] { --accent: #ffd479; --glow: rgba(255, 212, 121, 0.35); }
[data-mode="dusk"]  { --accent: #ffce6b; --glow: rgba(255, 180, 110, 0.4); }
[data-mode="day"]   { --accent: #e8943a; --glow: rgba(232, 148, 58, 0.22); }

CSSカスタムプロパティはコンポーネントを越境して継承されるため、この手のグローバルな状態を扱う場合は、そもそもスコープを切る意味があまりありません。むしろ「どのコンポーネントも同じ変数セットを参照する」という前提を守るために、変数の定義とそれを使う実装を同じファイルに置いておいた方が見通しが良くなります。

クラス名の衝突は命名規則で防ぐ

scoped styleを使わない代わりに、クラス名の衝突は命名の一貫性で防いでいます。.card、.reveal、.chip のような汎用クラスは複数コンポーネントで意図的に共有し、コンポーネント固有のスタイルには .service-card、.skill-card のように接頭辞を付ける、という緩いルールで運用しています。BEMほど厳格ではありませんが、「共有してよいものは共有し、固有のものだけ名前空間を切る」という発想はBEMに近い考え方です。

1枚のファイルであることの実利

セクションが増えるたびにCSSファイルが分散すると、「このアニメーションのタイミングはどこで定義されていたか」を探すのに複数ファイルを行き来する必要が出てきます。1300行という規模はエディタの検索1つで全体を把握できる範囲に収まっており、Ctrl+F で完結する検索性を優先した結果です。ファイル数を増やして関心を分離するメリットより、「ムード切替という横断的な関心事を1箇所で完結させる」メリットの方が、このサイトの規模では上回ると判断しました。

コンポーネント分割の粒度と、スタイルの分割の粒度は、必ずしも一致させる必要はない——というのが、このサイトを作りながら得た感覚です。