AIはAstroのスコープドCSSがJavaScriptで生成した要素に適用されない事を忘れる
冒頭でいきなり結論
こんな話Next/Nuxt使ってる人皆知ってると思うがAIくんは毎度同じことかますんで、 Claude.md/Gemini.md/Agents.mdに書かせてちゃんと頭入れとけって言っとくと面倒がないって話。 以下は何の話かわからない人向け。
問題
Astroコンポーネント内の <style> タグはデフォルトでスコープドCSSとしてコンパイルされる。これはコンポーネント間のスタイル汚染を防ぐ便利な機能だが、JavaScriptで動的に生成したDOM要素には適用されないという落とし穴がある。
なぜ適用されないのか
Astroのスコープドは、ビルド時にHTMLとCSSを解析し、一意のクラス(data-astro-cid-xxxxxのような属性)を付与することで実現している。
<!-- ビルド前 -->
<div class="card">Hello</div>
<style>
.card { background: blue; }
</style>
<!-- ビルド後(イメージ) -->
<div class="card" data-astro-cid-abc123>Hello</div>
<style>
.card[data-astro-cid-abc123] { background: blue; }
</style>
JavaScriptで動的に生成した要素にはこの属性が付与されないため、セレクタがマッチせずスタイルが適用されない。
<div id="container"></div>
<style>
.dynamic-item { color: red; } /* 適用されない! */
</style>
<script>
const container = document.getElementById('container');
const item = document.createElement('div');
item.className = 'dynamic-item'; // data-astro-cid-xxx がない
item.textContent = 'Dynamic content';
container.appendChild(item);
</script>
解決策
1. is:global を使う
<style is:global> を使うとスコープドではなくグローバルCSSとして出力される。
<style is:global>
.dynamic-item { color: red; } /* 適用される */
</style>
ただしグローバルなので、他のページやコンポーネントにも影響する可能性がある。命名規則(BEM、プレフィックス等)で衝突を避けるか、十分にユニークなクラス名を使う。
2. :global() セレクタを使う
一部のセレクタだけをグローバルにしたい場合は :global() 擬似クラスを使う。
<style>
/* スコープド(静的要素用) */
.container { padding: 1rem; }
/* グローバル(動的要素用) */
:global(.dynamic-item) { color: red; }
</style>
3. インラインスタイルを使う
JavaScript側で直接スタイルを設定する。
const item = document.createElement('div');
item.style.color = 'red';
item.style.fontSize = '14px';
シンプルなスタイルなら有効だが、複雑になると管理しづらい。
4. Tailwind CSSを使う
Tailwind CSSはユーティリティクラスとしてグローバルに定義されているため、動的要素でも問題なく使える。
const item = document.createElement('div');
item.className = 'text-red-500 text-sm font-bold';
「全部グローバルCSSにすればいいじゃん」
そう思うかもしれないが、スコープドCSSには以下のメリットがある:
- 名前衝突を気にしなくていい:
.cardや.buttonみたいな汎用的な名前を各コンポーネントで自由に使える - デッドコード削除: 使われなくなったコンポーネントのCSSは自動的に消える。グローバルCSSは手動で掃除が必要
- 影響範囲が明確: このコンポーネントのスタイルはこのファイル内で完結、という安心感
動的生成がないページではスコープドのままでいい。動的生成があるページだけ is:global を使えばいい。全部グローバルにするのは「問題を回避する」ではなく「メリットを捨てる」になる。
まとめ
| 方法 | 適用範囲 | 用途 |
|---|---|---|
<style is:global> | ページ全体 | 動的生成が多いページ |
:global(.class) | 特定クラスのみ | 一部だけ動的な場合 |
| インラインスタイル | 要素単位 | シンプルなスタイル |
| Tailwind CSS | グローバル | プロジェクト全体で使用時 |
AIにはちゃんとやれって教えておけって話。