Svelte コンポーネントの <style> ブロック内で :global() 修飾子を使う
Svelte コンポーネントの <style>
ブロック内で使用できる :global()
修飾子の使い方について少しハマったのでメモです。
やりたいこと
Svelte コンポーネントでは特定のコンポーネント内の <style>
ブロック内で定義した CSS スタイルを他のコンポーネントに適用したり、そのコンポーネントの子孫コンポーネントに適用したりすることができる :global()
修飾子が用意されています。
<style> :global(body) { /* これは <body> に適用されます */ margin: 0; } div :global(strong) { /* これは、このコンポーネント内の <div> 要素の中にある任意のコンポーネント内の <strong> 要素に適用されます */ color: goldenrod; } p:global(.red) { /* これは、このコンポーネントに属し、red クラスを持つすべての <p> 要素に適用されます。(後略)*/ } </style>
例えば以下のような焼酎一覧を表示する ShochuList.svelte
というコンポーネントがあるとします。
<script>
import Shochu from '$lib/components/Shochu.svelte';
const shochuList = [
{ name: '黒霧島', brewery: '霧島酒造' },
{ name: '明るい農村', brewery: '霧島町蒸留所'},
/* : */
]
</script>
<table>
<thead>
<tr>
<th>銘柄</th>
<th>蔵元</th>
</tr>
</thead>
<tbody>
{#each shochuList as shochu}
<Shochu {shochu} />
{/each}
</tbody>
</table>
このコンポーネントは以下の子コンポーネント Shochu.svelte
を参照しています。
<script>
export let shochu;
</script>
<tr>
<td>{shochu.name}</td>
<td>{shochu.brewery}</td>
</tr>
ここで偶数行には背景色を指定したいと思います。Shochu.svelte
で
<style>
tr:nth-of-type(2n) {
background-color: #ffcccc;
}
</style>
とすれば済む話ですが、本記事では :global()
修飾子の話がしたいので、ShochuList.svelte
にスタイルを指定したいと思います。
ShochuList.svelte
コンポーネント内の偶数番目の tr
に背景色を指定したいと思い、以下のように記述しました。
<style>
tbody:global(tr:nth-of-type(2n)) {
background-color: #ffcccc;
}
</style>
ところが以下のエラーが発生しました。
:global(...) not at the start of a selector sequence should not contain type or universal selectors svelte(css-invalid-global-selector-position)
セレクターの位置に要素型セレクター (この場合 tr
) やユニバーサルセレクター ( *
) をいれるな、と言われています。
ちょっとよくわからなかったので色々試してみました。
:global()
の前のセレクターと引数のセレクターで考える
公式ドキュメントでは :global()
について多くは語られていませんが、もう一度眺めてみます。
div :global(strong) { color: goldenrod; } p:global(.red) { }
すると前者の div :global(strong)
は普通に CSS で書く場合 div strong
を意図しており、後者の p:global(.red)
は p.red
を意図していることに気づき、:global()
修飾子の引数のセレクタによって、:global()
修飾子の前のセレクタとの間に結合子(結合子 - ウェブ開発を学ぶ | MDN)が必要なのではと気づきました。
従って上記公式ドキュメントの div :global(strong)
の div
と :global(strong)
の間のスペースは単なるスペースではなく、子孫結合子(結合子 - ウェブ開発を学ぶ | MDN)ということです。
試しに先ほどの ShochuList.svelte
のスタイルで tbody
と :global()
修飾子の間に子結合子(結合子 - ウェブ開発を学ぶ | MDN)を入れてみたところ、エラーが消え想定通り、偶数行に背景色が付きました。
<style>
tbody>:global(tr:nth-of-type(2n)) {
background-color: #ffcccc;
}
</style>
これにより、:global()
修飾子は前のセレクターと引数のセレクターで、通常 CSS で書くときのことを意識すると正しくワークすることがわかりました。
様々なセレクターの組み合わせパターン
:global()
修飾子の使い方がわかりましたのでセレクタのパターンによる書き方を紹介します。(そんなの :global()
使って書かないだろみたいなのもありますが)
ID セレクター
/* <div id="top"> を選択 */
div:global(#top) {}
/* <div> 内にある id="top" の要素を選択 */
div :global(.box) {}
クラスセレクター
/* <div class="box"> を選択 */
div:global(.box) {}
/* <div> 内にある class="box" の要素を選択 */
div :global(.box) {}
属性セレクター
/* <input disabled> を選択 */
input:global([disabled]) {}
/* <div> 内にある href 属性を持つ要素を選択 */
div :global([href]) {}
疑似クラス
/* 兄弟要素の <p> のうち最初の <p> を選択 */
p:global(:first-of-type)
できない書き方
試してみてできなかった書き方も紹介します。
global()
修飾子の前の結合子を引数には入れられない
先ほど 子結合子を付けて tbody>:global(tr:nth-of-type)
とすることでワークしたと書きましたが、試しにこの子結合子を global()
修飾子の引数に入れて tbody:global(>tr:nth-of-type)
としてみましたが、冒頭と同じエラーが出てダメでした。
global()
修飾子の前のセレクターとの結合子は前のセレクターと global()
修飾子の間に入れましょう。
global()
修飾子の引数内でセレクターリストは使えない
global()
修飾子の引数内だけでセレクターリスト使えたら便利だなと思い、table :global(th, td)
のように書いてみましたが、以下のエラーの通り、:global()
修飾子は一つのセレクターしか含められないとのことでした…。
:global(...) must contain a single selector svelte(css-invalid-global-selector)
あとがき
Svelte コンポーネント <style>
内での :global()
修飾子の使い方について調べてみました。わかれば当たり前のような気もするのですが、少しハマったので記事にしてみました。