Chrome 111 betaでついに、Style Container Queriesが対応されました。
Chrome 111 beta - Chrome Developers
今まではサイズのみ対応で、Container Queriesとして使い物になりませんでした。
しかし今回のベータ版のアップデートで style()
関数の対応しています。
前々からContainer Queriesは、CSS設計を変革させる機能だと思って非常に楽しみにしていました。ベータ版とはいえ、嬉しく思います。
追記: 2023年2月18日
まだ style(color: #000)
的な書き方は実装されていないので、完全体ではありませんでした。
Container Queriesで考えていたこと
Media Queries
「Media QueriesでCustom Propertiesを使いたい」というのをたまにTwitterなどで見かけます。
Media QueriesでCustom Propertiesを使えるようする仕様書などもあります。
ただうろ覚えなのですが、Media Queriesは独自で分離しており、:rootなどからCustom Propertiesを認識できるようにするのが大変、というのをどこかで見た気がします。
大変そうな処理なのでMedia Queriesよりも、Style Container Queriesで擬似的な実装ができないか考えていました。
:root {
--viewport: initial;
--viewport-sm: initial;
--viewport-md: initial;
container-name: root;
}
@media (800px > width) {
:root {
--viewport: md;
--viewport-md: true;
}
}
@media (400px > width) {
:root {
--viewport: sm;
--viewport-sm: true;
}
}
まずは :root
に対して、containerの名前空間を定義しておきます。 (root
)
そしてCustom PropertiesをMedia Queriesで切り替えます。
これで下準備は完了です。
なお、ここでは --viewport
と --viewport-**
の2つのCustom Propertiesを定義しています。
--viewport
は変数の値が切り替わるので、 特定のViewportに対してCSSが適用されます。
--viewport-**
はその指定のViewport以下に適用するためにCustom Propertiesを分離しています。
実際の記述を見てみましょう。
@container root style(--viewport: md) {
.test {
border: 8px solid #000;
}
}
--viewport
を指定しているので、400pxから800pxの間のみ、線が出るようになります。
もし800px以下で常に線を出したい場合は style(--viewport-md: true)
と変えます。
まだ実験的な手法を考えて試しているだけなので、もう少しブラッシュアップが必要だと思いますが、これで擬似的にMedia QueriesをCustom Propertiesで扱うことができます。
pre と code
自分の個人ブログではpre
とcode
を利用しています。
ただp要素の中のcode
は、装飾が必要ですが、pre code
はコードハイライト用のCSSを反映させるためにcode
のスタイルが不要になります。
<p>自分の個人ブログでは<code>code</code>を利用しています。</p>
<pre><code>// ここの`code`のスタイルはいらない</code></pre>
そのため、下記のようにCustom Propertiesのinitialテクニックを使用して、pre code
の場合はスタイルを表示させないようにしています。
code {
--_code: initial;
background: var(--_code, var(--color-gray-pale));
border-radius: var(--_code, 4px);
padding-block: var(--_code, 0.2em);
padding-inline: var(--_code, 0.4em);
}
pre code {
--_code: ;
}
ただContainer Queriesを使用すれば、もっとよくなりそうです。
試しに書きかえてみましたが、下記のコードでは動きませんでした。
code {
--el-code: true;
container-name: code;
}
pre code {
--el-code: false;
}
@container code style(--el-code: true) {
code {
background: #eee;
border-radius: 4px;
padding: 0.2em 0.4em;
}
}
上記のコードが動かない理由として、code
をContainer Queriesの「コンテナ」になってしまったからのようです。code
要素の中にcode
要素が入っていれば、子のcode
要素のスタイルが変化しますが、コンテナになったセレクタはcontainerの基準になってしまうため、@containerQueries の適用範囲から外れてしまうようです。(this
的なものが欲しい...)
そのため下記のように書きかえれば動作しますが、綺麗ではないですね。
:is(p, table) {
--el-code: true;
container-name: text;
}
@container text style(--el-code: true) {
code {
background: #eee;
border-radius: 4px;
padding: 0.2em 0.4em;
}
}
pre
要素のcode
要素にスタイルが当たらないようにするなら、:not()
で良くない?と思い直して書きかえました。
下記の方がスマートだし、Container Queriesいらんかった。
:not(pre) > code {
background: #eee;
border-radius: 4px;
padding: 0.2em 0.4em;
}
pre と code の失敗から
Container Queriesの指定先は変化させることができない、という制限があるようなので使い所が難しいと思っていたのですが、ふと思いつきました。
CSSをコンポーネントでScopeできるんじゃね?
まずは適当なHTMLを用意します。
<div class="a">demo</div>
<div class="card">
<div class="a">demo</div>
</div>
CSSは下記です。
:root {
--scope: true;
}
.card {
container-name: card;
}
@container card style(--scope: true) {
.a {
background: #faa;
border-radius: 4px;
margin: 1em 0;
padding: 0.2em 0.4em;
}
}
.card
のコンポーネントの中で Scope できました。
HTMLの要素をさらに増やします。
<h2 class="title">title</h2>
<div class="a">demo</div>
<p>本文のダミーテキスト</p>
<div class="card">
<h2 class="title">title</h2>
<div class="a">demo</div>
<p>本文のダミーテキスト</p>
</div>
CSSは下記。
:root {
--scope: true;
}
.card {
container-name: card;
border: 1px solid #000;
padding: 1em;
}
@container card style(--scope: true) {
.title {
margin: 0;
font-size: 1rem;
}
.a {
background: #faa;
border-radius: 4px;
margin: 1em 0;
padding: 0.2em 0.4em;
}
p {
margin: 0;
color: #aaa;
}
}
Container Queriesは名前空間も設定できるので、コンポーネントごとに container-name
プロパティを設定しておけば、Scopeとして扱うことができます。
Container Queriesは、まだChromeのベータ版しか対応されていませんが、やはり今後が楽しみな技術でした。