続・criticalで不等号ありのメディアクエリを扱う

前回は、Stylelintの警告をきっかけに、従来の @media (min-width: 768px) を新しい構文 @media (width >= 768px) に変更したところ、criticalでCSSの抽出がうまくいかなくなったこと、そして @media (width>=768px)と演算子の前後の空白を詰めて記述することで、CSSが抽出されることを確認しました。

👉 前回の記事を読む

今回は、画面サイズによって変わる動作を確認していきます。

試した環境

  • Ubuntu 24.04.2 LTS
  • npm 10.9.2
  • critical 7.2.1
  • penthouse 2.3.3
  • css-mediaquery 0.1.2

動作検証

今回もできるだけシンプルな構成の HTML および CSS ファイルを新たに用意し、画面サイズに応じたスタイル制御の挙動を確認していきます。

CSSでは、横幅に応じて以下の3パターンで文字色を変更するルールを設定します:

  • 〜767px:文字色は 黒
  • 768px〜1199px:文字色は 赤
  • 1200px〜:文字色は 青

これらの条件は、min-width を使って指定しても、width を使って指定しても、いずれも同じ結果になるはずです。

画面サイズを任意に指定しながら、それぞれの条件に対する動作を個別に確認していくことにします。

検証結果の確認には、今回も標準出力(CLI の出力結果)を用いて、実際にどのスタイルが適用されているかをチェックします。

テストファイルの準備

htmlを表示(test.html)
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>テストページ</title>
  <link rel="stylesheet" href="test-style.css">
</head>
<body>
      <div class="sub-title-min">min-widthで指定したカラー</div>
      <div class="sub-title-ineq">widthで指定したカラー</div>
</body>
</html>
cssを表示(test-style.css)
body { background-color: white; }
.sub-title-min  { color: black; }   
.sub-title-ineq { color: black; }   
@media (min-width: 768px)  { .sub-title-min { color: red;  }}
@media (min-width: 1200px) { .sub-title-min { color: blue; }}
@media (width>=768px)     { .sub-title-ineq { color: red;  }}
@media (width>=1200px)    { .sub-title-ineq { color: blue; }}

用意した HTML ファイル(test.html)と CSS ファイル(test-style.css)は、今回もdev ディレクトリの直下に配置しました。

テスト実行

devディレクトリ直下で以下のコマンドを実行します。幅は600pxを指定します。メディアクエリは抽出されないはずです。

cat test.html | critical --width 600 
実行結果を表示
#実行コマンドと結果
~/dev$ cat test.html | critical --width 600
Not rebasing assets for test-style.css. Use "rebase" option
body{background-color:#fff}.sub-title-min{color:#000}.sub-title-ineq{color:#000}@media (width>=768px){.sub-title-ineq{color:red}}@media (width>=1200px){.sub-title-ineq{color:#00f}}~/dev$

1000px指定でも試してみます。

cat test.html | critical --width 1000 
実行結果を表示
#実行コマンドと結果
~/dev$ cat test.html | critical --width 1000 
Not rebasing assets for test-style.css. Use "rebase" option
body{background-color:#fff}.sub-title-min{color:#000}.sub-title-ineq{color:#000}@media (min-width:768px){.sub-title-min{color:red}}@media (width>=768px){.sub-title-ineq{color:red}}@media (width>=1200px){.sub-title-ineq{color:#00f}}~/dev$

出力に差がある

実行結果を確認すると、min-width を用いたメディアクエリは、期待通り、指定した画面サイズの条件に合致しない場合には適用されていないことがわかりました。

一方で、width>= のような形式で指定した場合は、指定した画面サイズに関係なく、すべてのスタイルが抽出されていることが確認できました。

この違いから推測できるのは、width>= 形式での指定は、実際の画面サイズに基づいてスタイルを抽出しているのではなく、単に @media ルール内の文字列として処理され、そのまま抽出されている可能性が高いということです。

メディアクエリの解析

前回の調査では、critical のソースコード内にメディアクエリ関連の明確な処理は見つかりませんでした。そこで今回は、critical が内部で利用している Penthouse に注目してみます。

GitHubで公開されている Penthouse のソースコードを確認したところ、「画面サイズにマッチしない @media ルールを削除する」処理を担う non-matching-media-query-remover.js というモジュールが存在していることが分かりました。

このファイルでは、CSSのメディアクエリをパースし、現在の画面サイズに合致するかどうかを判断するために css-mediaquery.parse() が利用されています。

私自身はまだこの処理をJavaScriptでテストしたわけではないため断言はできませんが、css-mediaquery のソースコードを確認した限りでは、比較演算子(例:width>=768px)を含む形式のメディアクエリには対応していないようです。

このような形式は、内部的に使用されている RE_MEDIA_QUERY の正規表現パターンにマッチしないため、パラメータとして渡したとしても 正しく解釈されない可能性が高く、パースエラーになると予想されます。

また、ファイルの更新日時を確認すると、css-mediaquery は約10年前を最後に更新が止まっているようです。そのため、比較演算子を含む形式のメディアクエリには対応していないと考えるのが妥当でしょう。

もし指定された画面サイズにかかわらず、すべてのスタイルがそのまま抽出されたとしても、この挙動を理解していれば、それが大きな問題につながるケースはあまり多くないと考えられます。

たとえばブログのような用途では、あらかじめすべての画面サイズに対応できるようスタイルが設計されていることが多く、むしろ画面サイズに関連するすべてのメディアクエリが抽出されることが望ましいからです。

実際に、実運用を想定して3パターンの画面サイズ(小・中・大)でスタイルを抽出してみます。

cat test.html | critical --dimensions=405x667 --dimensions=1024x768 --dimensions=1366x768 
実行結果を表示
#実行コマンドと結果
/dev$ cat test.html | critical --dimensions=405x667 --dimensions=1024x768 --dimensions=1366x768 
Not rebasing assets for test-style.css. Use "rebase" option
body{background-color:#fff}.sub-title-min{color:#000}.sub-title-ineq{color:#000}@media (min-width:768px){.sub-title-min{color:red}}@media (min-width:1200px){.sub-title-min{color:#00f}}@media (width>=768px){.sub-title-ineq{color:red}}@media (width>=1200px){.sub-title-ineq{color:#00f}}~/dev$

結果を確認すると、いずれの画面サイズにおいても、期待通りのCSSが正しく抽出されていました。 このことから、通常の運用環境においては、メディアクエリの抽出処理に関する大きな懸念はないと考えられます。

📌今回のまとめ

今回の検証を通じて、@media (width>=768px) のような比較演算子を含む記述については、これらのツールでは画面サイズとして正しく解釈はされないものの、文字列としてそのまま抽出されることが確認できました。 つまり、条件としてのマッチング結果にかかわらず、構文上存在していれば抽出の対象になるということです。

メディアクエリの記述について不安がある場合は、より確実に処理されるよう、min-widthmax-width などの従来の構文を使うのも一つの選択肢です。これらは広くサポートされており、ツールによる解析にも安定して対応しています。

「古い構文で書くか、それとも新しい構文に移行するか」は、ツールやブラウザの対応状況や既存のコードとの整合性を見ながら判断する必要があります。とはいえ、現在では多くのモダンブラウザが比較演算子を含む記法にも対応しているため、新しい書き方を取り入れて問題ないケースが増えています。

これらの仕様や挙動を正しく理解したうえで、新しい構文を積極的に活用しつつ、状況に応じて柔軟に対応していくのが最善ではないかと思いました。