西田@CX事業本部です
今回は2023年の2月に全てのブラウザに実装されたCSSのコンテナクエリを SvelteKit で試していきたいと思います
コンテナクエリとは?
任意の要素の幅を基準にしてCSSを切り替えられる仕組みです。同じような用途ではメディアクエリが使われています
メディアクエリが画面の表示領域のサイズに応じてCSSを切り替えるのに対して、コンテナクエリは、親要素や、祖先要素を基準にCSSを切り替えることができます
役立つシチュエーション
レスポンシブで切り替わるコンポーネントを親の要素の幅に応じて切り替えることができます
例えば、同じコンポーネントを1カラムと2カラムそれぞれに当てはめた場合のレスポンシブが容易に記述できるようになります
以下の画像(Gif)は同じコンポーネントを1カラムと2カラムでそれぞれ当てはめた例です。横幅が900px付近で2カラムの方でCSSが切り替わり、450付近で1カラムの要素が切り替わっています。このようなケースのCSSを簡潔に書くことができます
試してみる
今回は SvelteKit を使ってコンポーネントを作成し、一つのコンポーネントが、親の幅に合わせてレスポンシブするようにしていきます
1カラムと2カラムで同じコンポーネントを使用し、親の要素の幅で切り替わるようにします
SvelteKit プロジェクトを作成します
npm create svelte@latest container
今回は以下を選択しました
- スケルトンプロジェクト
- TypeScript
- ESLint + Prettier
┌ Welcome to SvelteKit!
│
◇ Which Svelte app template?
│ Skeleton project
│
◇ Add type checking with TypeScript?
│ Yes, using TypeScript syntax
│
◇ Select additional options (use arrow keys/space bar)
│ Add ESLint for code linting, Add Prettier for code formatting
※ 執筆時点(2023年3月末)では Svelte はコンテナクエリがサポートされていなかったので、今回は有志の方が公開している fork 版のパッケージを使用します。 PR を出されており、これが本体に取り込まれれば以下の変更は必要ありません
package.json の overrides で有志の方が公開しているパッケージを使うように指定します
package.json
"devDependencies": {
//... 省略
},
"overrides": {
"svelte": "npm:@typhonjs-svelte/svelte@3.55.1-cq"
},
参考:
https://github.com/sveltejs/svelte/pull/8275
https://github.com/sveltejs/svelte/issues/6969#issuecomment-1467183195
ライブラリをインストールします
npm install
コンポーネント構成
今回は 1カラムと2カラムのレイアウトを用意して、同じ Card
コンポーネントを置く構成で作成します
親コンポーネント
親になるコンポーネントです
1カラムと、2カラムのそれぞれのレイアウトに import した Card
コンポーネントを、1カラムの方に一つ、2カラムの方にそれぞれ3つづつ置いてます
src/routes/+page.svelte
<script lang="ts">
import Card from "./card.svelte";
</script>
<div class="one_col">
<Card />
</div>
<div class="two_col">
<div>
<Card />
<Card />
<Card />
</div>
<div>
<Card />
<Card />
<Card />
</div>
</div>
<style>
.two_col {
display: flex;
}
.two_col div {
width: 100%;
}
</style>
カードコンポーネント
親コンポーネントからインポートされてるカートのコンポーネントです
svelteでは、コンポーネントに書かれたCSSがコンポーネント内に閉じるので、要素に対するセレクタを使ってCSSを記述してます
src/routes/card.svelte
<div class="container">
<div class="wrapper">
<img class="image" src="https://source.unsplash.com/random/640x400" />
<div>
<dl>
<dt>長いラベル</dt>
<dd>投稿しました</dd>
</dl>
<dl>
<dt>長いラベル</dt>
<dd>¥100,000</dd>
</dl>
</div>
</div>
</div>
<style>
.container{
container-type: inline-size;
margin: 10px;
border: solid 1px #eee;
box-shadow: 0 5px 5px 0 rgba(0, 0, 0, .2);
}
.wrapper {
display: flex;
flex-direction: column;
padding: 10px;
gap: 10px;
}
.image {
height: 200px;
width: 100%;
border-radius: 10px;
object-fit: cover;
}
dl {
display: flex;
flex-direction: column;
}
dt {
font-size: 1.1rem;
font-weight: 700;
}
dd {
font-size: 1.0rem;
font-weight: 400;
}
@container (min-width: 420px) {
.wrapper {
flex-direction: row;
}
.image {
max-width: 50%
}
dl {
flex-direction: row;
}
dd {
margin-left: 10px
}
}
</style>
解説
Card
コンポーネントの一番上位の要素に対して container-type: inline-size
を指定してます。この要素配下のコンテナクエリは、この要素を基準にCSSを切り替えられます
<div class="container">
<!-- 省略 -->
</div>
.container{
container-type: inline-size;
}
コンテナクエリで指定の幅より狭くなった場合の指定をしています。以下の例だと container-type: inline-type
が420pxを下回った場合にコンテナクエリ内のCSSが有効になります
@container (min-width: 420px) {
/* 省略 */
}
まとめ
今回はコンテナクエリを Svelte で試してみました
全てのソースコードは Github に Push してます
この記事が誰かの参考になれば幸いです