Webサイト制作で「こんな時だけデザインを変えたいのに、JavaScriptを使わないと無理かな…」と感じたことはありませんか? ユーザーの操作やデバイスの状態に合わせて、表示を柔軟に切り替えたい。例えば、フォームの入力エラー時に赤字で警告したり、ログイン状況によってボタンのデザインを変えたり…。このような「もしも」の条件に応じてスタイルを変化させることは、JavaScript(JS)を使えば実現できますが、できればCSSだけで完結させたいと思う方も多いでしょう。
しかし、CSSにも「条件式」のような考え方があり、特定の条件下でスタイルを適用する機能が備わっているのをご存知でしょうか? 近年進化を続けるCSSでは、これまでJSでしか実現できなかったような動的な表現も、CSSだけで手軽に実装できるようになっています。
この記事では、「CSS 条件式」に焦点を当て、JSに頼らずにWebサイトの表現力を格段に向上させるテクニックをわかりやすく解説します。基本から実践的な応用例まで、具体的にどうすればよいのかを丁寧に説明しますので、読み終える頃にはあなたのCSSスキルが飛躍的にアップしているはずです。
CSSのif()関数について
CSSのif()
関数は、現在W3CのCSSワーキンググループで提案され、仕様策定が進められている新しい機能です。2025年3月時点ではまだ実験段階であり、主要なブラウザに広く実装されているわけではありませんが、Chrome Canaryのような開発者向けバージョンではフラグを有効にすることで利用できるプロトタイプが既に存在します。
if()関数とは?
CSSのif()
関数は、プロパティの値に対してインラインで条件分岐を記述できるようにすることを目的としています。これまでのCSSでは、条件に応じたスタイルの適用は、セレクタ(疑似クラス、属性セレクタなど)や@media
、@supports
ルールなどを使って実現してきましたが、if()
関数が登場すると、より直接的に「もしこの条件が真ならばこの値を使い、そうでなければこの値を使う」といった記述が可能になります。
基本的な構文は以下のようになることが提案されています。
property: if( <condition> , <value-if-true> , <value-if-false> );
または、複数の条件を連鎖させるような記述も検討されています。
property: if( <condition1>: <value1>; <condition2>: <value2>; else: <default-value> );
これにより、例えばカスタムプロパティ(CSS変数)の値に基づいて色を切り替えたり、特定の要素の状態に応じて異なるフォントサイズを適用したりする際に、CSSの可読性が向上し、より簡潔に記述できるようになることが期待されています。
いつごろ実装されるか
正確な実装時期を特定することは困難ですが、W3Cでの仕様策定の進捗や、各ブラウザベンダーの実装状況によります。
検索結果によると、if()
関数はCSS Values and Units Module Level 5の仕様の一部として提案されており、2025年の早い段階で「Interoperable 2nd Public Working Draft (I2P)」や「Interoperable 2nd Stable Working Draft (I2S)」といった段階に進んでいることが示されています。これは、仕様が成熟し、複数のブラウザが相互運用性のある実装に向けて動き出していることを意味します。
しかし、仕様が固まり、それがすべての主要なブラウザに広く展開されるまでには、通常さらに時間がかかります。一般的には、仕様がCandidate Recommendation(勧告候補)の段階に達すると、非実験的な実装が可能になり、その後各ブラウザが段階的にリリースしていきます。
したがって、2025年中から2026年にかけて、主要ブラウザでの本格的な実装と普及が始まる可能性が高いと言えるでしょう。現時点ではまだ実験的な機能として捉え、実際のプロジェクトで広く採用する際は、ブラウザのサポート状況を注意深く確認することが重要です。
CSSのif関数の実装が待ち遠しいですが今回は今ある技術のみでif文と同じ効果を得るための情報を紹介します。
CSSだけで実現する条件分岐の基本と限界
CSS自体には、JavaScriptなどのプログラミング言語にあるようなif/else
といった直接的な条件式は存在しません。これは、CSSが「スタイルを記述するための言語」であり、「ロジックを記述するための言語」ではないという設計思想に基づいています。CSSの主な役割は、HTML要素がどのように表示されるかを宣言的に定義することにあります。
しかし、この制約があるからこそ、CSSは独自の「条件に応じたスタイルの適用」メカニズムを発展させてきました。特定の状態にある要素や、特定の属性を持つ要素、あるいは特定の表示環境下においてのみスタイルを適用する、といった方法で、実質的な条件分岐を実現しているのです。これにより、余計なJavaScriptコードを記述することなく、パフォーマンスに優れたスタイリングが可能になります。

CSSでif文のような条件分岐を実現する方法
CSSでプログラミング言語のif
文のような条件分岐を実現するには、主に以下の強力なセレクタやルールを活用します。
- 疑似クラス (Pseudo-classes): 要素の特定の状態(ホバー時、クリック時、アクティブ時、入力検証の状態など)や、ドキュメントツリーにおける位置(最初の子要素、最後の要素など)に基づいてスタイルを適用します。例えば、
:hover
は要素にマウスカーソルが重なった場合にのみスタイルを適用し、:focus
は要素がフォーカスされた場合に適用します。これらは「もし要素がこの状態なら、このスタイルを適用する」という条件を表現します。 - 疑似要素 (Pseudo-elements): 要素の一部(最初の行、最初の文字、要素の前後に追加されるコンテンツなど)に対してスタイルを適用します。
::before
や::after
は、特定の条件(例えば、親要素に特定のクラスがある場合)と組み合わせて、動的にコンテンツを生成する際に利用できます。 - 属性セレクタ (Attribute Selectors): HTML要素が持つ特定の属性(
href
、class
、id
、type
など)の値に基づいてスタイルを適用します。例えば、[type="submit"]
はtype
属性がsubmit
である要素にのみスタイルを適用します。これは「もし要素がこの属性を持ち、その値がこれこれなら、このスタイルを適用する」という条件を表現します。 - メディアクエリ (Media Queries): デバイスの特性(ビューポートの幅、高さ、解像度、画面の向きなど)に基づいてスタイルを適用します。レスポンシブデザインの根幹をなす機能であり、「もし画面の幅がこの範囲なら、このスタイルを適用する」という条件を表現します。
これらの組み合わせにより、JavaScriptに頼ることなく、CSSだけで複雑な条件に応じたスタイリングが可能になります。
CSSの条件分岐に使える主な疑似クラス一覧
CSSには非常に多くの疑似クラスが存在し、それぞれが異なる条件を表します。ここでは、特に「条件分岐」として活用できる主要な疑似クラスをいくつか紹介します。
:has():
CSSセレクタLevel 4で導入された、非常に強力な疑似クラスです。「もし子孫要素として特定のセレクタを持つ要素があれば」という条件でスタイルを適用できます。これにより、これまでJavaScriptでしか実現できなかった「親要素への条件適用」が可能になりました。
/* 例1: <a>要素を子に持つ<div>要素の背景色を変更 */
div:has(a) {
background-color: lightblue;
}
/* 例2: <input>要素を子に持ち、かつ:focus状態の子要素を持つ<form>要素の境界線を変更 */
form:has(input:focus) {
border: 2px solid royalblue;
}
:has()
は、その内部に記述されたセレクタにマッチする子孫要素が存在する場合に、自身(:has()
が適用されているセレクタ)にスタイルを適用します。この機能により、DOMツリーの上位の要素が下位の要素の状態を「感知」し、それに応じて自身のスタイルを変更できるようになりました。これは、非常に強力な機能であり、特に複雑なUIコンポーネントにおいてJavaScriptの介入を減らすのに役立ちます。
:not():
「指定したセレクタにマッチしない要素」にスタイルを適用する否定疑似クラスです。非常に汎用性が高く、他のセレクタと組み合わせて多様な条件を表現できます。
/* 例1: クラス"highlight"を持たない全ての<p>要素の文字色をグレーに */
p:not(.highlight) {
color: gray;
}
/* 例2: <a>要素を子に持つが、同時にクラス"disabled"を持たない<div>要素にパディングを追加 */
div:has(a):not(.disabled) {
padding: 10px;
}
:not()
は単独で使うこともできますが、他の疑似クラスやセレクタと組み合わせることで、より具体的な条件を除外する際に威力を発揮します。
:nth-child(n) / :nth-of-type(n):
「兄弟要素の中でn番目の要素」、または「兄弟要素の中で同じ型のn番目の要素」にスタイルを適用します。リストの奇数/偶数行に異なるスタイルを適用する際によく利用されます。n
にはodd
(奇数)、even
(偶数)のキーワードや、An+B
形式の計算式を使用できます。
/* 例1: リストの奇数番目の項目に背景色を適用 */
li:nth-child(odd) {
background-color: #f0f0f0;
}
/* 例2: リストの偶数番目の項目に文字色を適用 */
li:nth-child(even) {
color: #333;
}
/* 例3: 3の倍数の<p>要素に境界線を適用 */
p:nth-of-type(3n) {
border-bottom: 1px dashed lightgray;
}
これらの疑似クラスは、要素の順番に基づいて条件を付ける際に非常に役立ちます。
その他にも、以下のような疑似クラスが条件分岐に利用できます。
- :hover, :active, :focus: ユーザーのインタラクション(マウスオーバー、クリック、フォーカス)に応じたスタイル変更。
- :checked: チェックボックスやラジオボタンが選択されている場合にスタイルを適用。
- :disabled, :enabled: フォーム要素が無効/有効な場合にスタイルを適用。
- :valid, :invalid: フォームの入力値が有効/無効な場合にスタイルを適用。
- :empty: 子要素やテキストノードを持たない要素にスタイルを適用。
- :first-child, :last-child, :only-child: 兄弟要素の中での最初、最後、唯一の子要素にスタイルを適用。
これらの疑似クラスを理解し、適切に組み合わせることで、JavaScriptに頼らずとも非常に表現豊かなWebデザインが可能になります。
CSSでできること・できないこと:if文的ロジックの限界とは
CSSは非常に強力ですが、プログラミング言語ではないため、あくまで「スタイルを宣言的に適用する」という範囲に限られます。具体的な「if文的ロジック」における限界は以下の通りです。
CSSだけでできないこと:
- 複雑な計算やデータ操作: ユーザーが入力した数値に基づいて複雑な計算を行い、その結果に応じてスタイルを動的に生成する、といったことはCSS単体ではできません。
calc()
関数は基本的な四則演算は可能ですが、条件分岐を伴う複雑なロジックには対応していません。 - 動的な要素の生成・削除: HTML要素そのものをJavaScriptのように動的に生成したり、削除したりすることはCSSではできません。
::before
や::after
疑似要素でコンテンツを追加することは可能ですが、これはあくまでスタイルの一部であり、DOMツリーに新たな要素が追加されるわけではありません。 - 外部データとの連携: データベースから取得した情報や、APIから受け取ったデータに基づいてスタイルを変更する、といったことはできません。CSSはあくまでHTMLの構造と自身の定義に基づいて動作します。
- ユーザーの入力値の直接的な参照と評価: フォームの入力フィールドにユーザーが入力した具体的な値(例: 「もし入力された値が100以上なら」)を直接参照し、その値に基づいて条件分岐を行うことはできません。
:valid
や:invalid
は入力の妥当性を判断しますが、具体的な値の範囲を評価するものではありません。 - 時間の概念に基づくスタイル変更: 特定の時間が経過したらスタイルを変更する、といったタイムベースのイベント制御はCSS単体では困難です。アニメーションは時間の概念を持ちますが、これは事前に定義された変化を時間軸に沿って再生するものであり、ロジックによる動的な時間制御ではありません。
CSSでできること(if文的ロジックの代替):
上記のような限界はあるものの、この記事で解説する様々なセレクタやルールを組み合わせることで、多くの「条件に応じたスタイルの適用」を実現できます。
- 状態に応じたスタイルの切り替え:
:hover
,:active
,:focus
,:checked
,:disabled
,:valid
,:invalid
などの疑似クラスを使って、ユーザーのインタラクションやフォーム要素の状態に基づいてスタイルを切り替えることができます。 - コンテンツの有無や構造に応じたスタイル:
:empty
,:first-child
,:last-child
,:only-child
,:nth-child()
,:has()
などの疑似クラスを使って、要素が持つコンテンツの有無や、兄弟要素内での位置、特定の子要素の有無に基づいてスタイルを変更できます。 - 属性値に応じたスタイル: 属性セレクタ(
[attr]
,[attr="value"]
,[attr^="value"]
など)を使って、HTML要素に設定された属性の値に基づいてスタイルを適用できます。これは、JavaScriptでデータ属性を操作することで、CSS側の条件を動的に変更する強力な手段となります。 - デバイス特性に応じたスタイル: メディアクエリ(
@media
)を使って、画面の幅、高さ、解像度、画面の向きなど、デバイスの特性に基づいてレイアウトやフォントサイズなどを最適化できます。
CSSはあくまで宣言的なスタイルシート言語であり、その強みは「ロジックからスタイリングを分離する」ことにあります。これにより、コードの管理が容易になり、パフォーマンスも向上します。JavaScriptとの適切な連携によって、CSS単体では難しい高度な動的表現も実現可能になります。
状態に応じたスタイル切り替えの具体テクニック
Webサイトのユーザーインターフェース(UI)では、要素がどのような状態にあるかによって、その見た目を変化させることが非常に重要です。例えば、ユーザーがボタンにマウスを重ねたとき、入力フィールドにフォーカスしたとき、またはフォームの入力値が正しくないときなど、CSSの「条件式」を活用することで、これらの状態変化を視覚的にフィードバックし、ユーザー体験(UX)を向上させることができます。
フォーム要素の状態に応じてスタイルを変える
フォームはユーザーからの入力を受け付ける重要な要素であり、その状態に応じて適切にスタイルを変化させることで、ユーザーに使いやすさを提供できます。CSSには、フォーム要素の様々な状態を捉える疑似クラスが用意されています。
入力値の検証状態に応じたスタイリング (:valid
, :invalid
)
HTML5では、required
属性やpattern
属性、type="email"
、type="url"
などの入力規則が導入されました。これらの規則に則って入力値が有効か無効かをCSSで判定し、スタイルを適用できます。
<!-- HTMLコード例 -->
<form>
<div class="input-group">
<label for="username">ユーザー名 (必須):</label>
<input type="text" id="username" required>
<span class="error-message">ユーザー名は必須です。</span>
</div>
<div class="input-group">
<label for="email">メールアドレス:</label>
<input type="email" id="email" placeholder="email@example.com">
<span class="error-message">有効なメールアドレスを入力してください。</span>
</div>
<button type="submit">送信</button>
</form>
/* CSSコード例 */
/* デフォルトのエラーメッセージを非表示 */
.error-message {
display: none;
color: #d9534f; /* 赤色 */
font-size: 0.8em;
margin-top: 5px;
}
/* :invalid 疑似クラスで入力値が無効な場合にスタイルを適用 */
input:invalid {
border-color: #d9534f; /* 枠線を赤色に */
box-shadow: 0 0 5px rgba(217, 83, 79, 0.5); /* 赤い影を追加 */
}
/* 入力値が無効で、かつユーザーが操作した(フォーカスを外したなど)場合にエラーメッセージを表示 */
/* 注意: この方法は一部ブラウザや状況で動作しない場合があるため、JavaScriptとの併用が推奨されます */
input:invalid:not(:placeholder-shown) + .error-message {
display: block; /* エラーメッセージを表示 */
}
/* より堅牢なエラー表示のためにはJavaScriptと組み合わせるのが一般的です。 */
/* :valid 疑似クラスで入力値が有効な場合にスタイルを適用 */
input:valid {
border-color: #5cb85c; /* 枠線を緑色に */
box-shadow: 0 0 5px rgba(92, 184, 92, 0.5); /* 緑の影を追加 */
}
/* inputがrequiredであり、かつ:invalidの場合のみ送信ボタンを無効にする例(:has()の活用) */
form:has(input[required]:invalid) button[type="submit"] {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none; /* クリックイベントを無効化 */
}
/* 初期状態のinputには有効・無効のスタイルを適用しない */
/* input:placeholder-shownは、placeholderが表示されている状態(つまりまだ何も入力されていない状態)を指す */
input:placeholder-shown:not(:focus) {
border-color: #ccc; /* 通常の枠線色に戻す */
box-shadow: none; /* 影を削除 */
}
上記の例では、input:invalid
とinput:valid
を使用して、入力フィールドの境界線の色と影を動的に変更しています。特に重要なのは、form:has(input[required]:invalid)
のように**:has()**を組み合わせることで、「必須入力項目が無効な場合、送信ボタンを無効にする」という、これまでJavaScriptでしか実現できなかったようなロジックをCSSのみで表現している点です。これにより、ユーザーは送信前に必須項目の不備に気づきやすくなります。
チェックボックス・ラジオボタンの状態に応じたスタイリング (:checked
)
チェックボックスやラジオボタンが選択されているかどうかに基づいて、関連する要素のスタイルを変更することができます。
<!-- HTMLコード例 -->
<div class="option-group">
<input type="checkbox" id="newsletter" name="preferences">
<label for="newsletter">ニュースレターを受け取る</label>
</div>
<div class="option-group">
<input type="radio" id="payment-card" name="payment" value="card">
<label for="payment-card">クレジットカード</label>
</div>
<div class="option-group">
<input type="radio" id="payment-cash" name="payment" value="cash">
<label for="payment-cash">現金払い</label>
</div>
/* CSSコード例 */
/* チェックボックスがチェックされた場合に、隣接するラベルの文字色を変える */
input[type="checkbox"]:checked + label {
color: royalblue; /* チェックされたら青色に */
font-weight: bold;
}
/* ラジオボタンがチェックされた場合に、親要素(例えばdiv.option-group)の背景色を変える */
/* :has()疑似クラスが非常に強力です。 */
.option-group:has(input[type="radio"]:checked) {
background-color: aliceblue; /* チェックされたラジオボタンを含むグループの背景色を薄い青に */
border-radius: 8px;
padding: 10px;
}
:checked
疑似クラスは、チェックボックスやラジオボタンのUIをカスタムする際にも頻繁に用いられます。デフォルトのチェックマークを非表示にし、+ label
や:has()
と組み合わせてカスタムデザインのチェックマークや背景色を適用するテクニックは非常に一般的です。
属性値やクラスに応じた動的スタイル
HTML要素の属性やクラスは、CSSがスタイルを適用するための最も基本的な「条件」となります。これらを活用することで、要素の特性や状態を細かく制御し、動的な表現を実現できます。
属性セレクタによる条件指定
特定の属性を持つ要素、または特定の属性値を持つ要素にスタイルを適用します。
[attr]
:attr
属性を持つ全ての要素。[attr="value"]
:attr
属性が完全にvalue
と一致する要素。[attr~="value"]
:attr
属性がスペース区切りの単語リストであり、その中にvalue
を含む要素。[attr^="value"]
:attr
属性がvalue
で始まる要素。[attr$="value"]
:attr
属性がvalue
で終わる要素。[attr*="value"]
:attr
属性がvalue
をどこかに含む要素。
<!-- HTMLコード例 -->
<a href="<https://example.com>" target="_blank">外部リンク</a>
<a href="/internal/page">内部リンク</a>
<button data-status="pending">保留中</button>
<button data-status="completed">完了</button>
<input type="text" placeholder="名前">
<input type="password" placeholder="パスワード">
/* CSSコード例 */
/* target="_blank"属性を持つ<a>要素に外部リンクアイコンを追加 */
a[target="_blank"]::after {
content: " ↗"; /* 外部リンクを示す矢印アイコン */
font-size: 0.8em;
color: #007bff;
}
/* data-status属性が"pending"のボタンに警告色を適用 */
button[data-status="pending"] {
background-color: #ffc107; /* 黄色 */
color: #333;
border: none;
padding: 8px 15px;
border-radius: 5px;
}
/* data-status属性が"completed"のボタンに成功色を適用 */
button[data-status="completed"] {
background-color: #28a745; /* 緑色 */
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
}
/* input要素のtype属性が"password"の場合に特定のフォントを適用 */
input[type="password"] {
font-family: 'Consolas', monospace; /* パスワード入力欄に等幅フォントを適用 */
letter-spacing: 2px; /* 文字間隔を広げる */
}
データ属性 (data-*
) は、特にJavaScriptとの連携において非常に強力です。JavaScriptでdata-*
属性の値を変更するだけで、CSSのスタイルを動的に切り替えることができるため、HTMLに余分なクラスを追加することなく、状態管理をシンプルに行うことができます。
ユーザーインタラクションをCSSだけで制御する
ユーザーのインタラクション(マウス操作やキーボード操作)は、Webサイトの応答性を高めるために不可欠です。CSSの疑似クラスを利用すれば、JavaScriptを使わずにこれらのインタラクションに対する視覚的なフィードバックを実装できます。
ホバー・アクティブ・フォーカス状態
- :hover: マウスカーソルが要素の上に重なったときに適用。
- :active: 要素がアクティブ化されている間(例: マウスボタンが押されている間)に適用。
- :focus: 要素がキーボードやプログラム的にフォーカスされているときに適用。
<!-- HTMLコード例 -->
<button class="styled-button">クリックしてください</button>
<input type="text" class="styled-input" placeholder="ここに入力">
<a href="#" class="styled-link">リンクの例</a>
/* CSSコード例 */
/* ボタンの基本スタイル */
.styled-button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.1s ease; /* スムーズなアニメーション */
}
/* ボタンにマウスが乗ったときの色とわずかな拡大 */
.styled-button:hover {
background-color: #0056b3; /* 色を濃くする */
transform: scale(1.02); /* わずかに拡大 */
}
/* ボタンがクリックされたとき(アクティブ状態)の色と縮小 */
.styled-button:active {
background-color: #004085; /* さらに色を濃くする */
transform: scale(0.98); /* わずかに縮小し、クリック感を出す */
}
/* 入力フィールドの基本スタイル */
.styled-input {
padding: 8px 12px;
border: 2px solid #ccc;
border-radius: 4px;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
/* 入力フィールドがフォーカスされたときのスタイル変更 */
.styled-input:focus {
border-color: #007bff; /* 青い枠線 */
box-shadow: 0 0 8px rgba(0, 123, 255, 0.2); /* 青い影 */
outline: none; /* デフォルトのアウトラインを消す */
}
/* リンクの基本スタイル */
.styled-link {
color: #007bff;
text-decoration: none;
transition: color 0.2s ease;
}
/* リンクにマウスが乗ったときの色と下線表示 */
.styled-link:hover {
color: #0056b3;
text-decoration: underline;
}
これらの疑似クラスは、シンプルなボタンやリンクだけでなく、ナビゲーションメニューの項目や、カード型UIなど、様々なインタラクティブ要素に適用できます。transition
プロパティと組み合わせることで、スタイルの変化を滑らかにアニメーションさせ、より洗練されたユーザー体験を提供することができます。
現場で役立つ実践テクニック!CSS条件式でUI/UXを劇的に改善
ここまで解説してきたCSSの「条件式」の概念と具体的な疑似クラス、属性セレクタ、メディアクエリを組み合わせることで、実際のWeb開発現場で役立つ多様なUI/UX改善が可能です。JavaScriptの介入を最小限に抑えつつ、パフォーマンスが高く、メンテナンスしやすいコードを実現できます。
フォームの入力値に応じたリアルタイムなエラー表示とデザイン変更
ユーザーがフォームに入力する際、入力値の妥当性をリアルタイムでフィードバックすることは、ユーザーフレンドリーな体験を提供するために不可欠です。CSSの:valid
、:invalid
、そして:placeholder-shown
などの疑似クラスを組み合わせることで、JavaScriptなしでも基本的な入力検証の視覚的なフィードバックを実現できます。
<!-- HTMLコード例 -->
<form id="myForm" class="contact-form">
<h2>お問い合わせフォーム</h2>
<div class="form-group">
<label for="name">お名前 (必須)</label>
<input type="text" id="name" required minlength="2" placeholder="田中 太郎">
<p class="error-text">2文字以上で入力してください。</p>
</div>
<div class="form-group">
<label for="email">メールアドレス (必須)</label>
<input type="email" id="email" required placeholder="example@domain.com">
<p class="error-text">有効なメールアドレスを入力してください。</p>
</div>
<div class="form-group">
<label for="phone">電話番号 (任意)</label>
<input type="tel" id="phone" pattern="^\\d{10,11}$" placeholder="09012345678">
<p class="error-text">ハイフンなしの半角数字10桁または11桁で入力してください。</p>
</div>
<div class="form-group">
<label for="message">お問い合わせ内容 (必須)</label>
<textarea id="message" required minlength="10" placeholder="お問い合わせ内容を10文字以上でご記入ください。"></textarea>
<p class="error-text">10文字以上で入力してください。</p>
</div>
<button type="submit" class="submit-button">送信</button>
</form>
/* Interフォントを適用 */
body {
font-family: 'Inter', sans-serif;
}
/* CSSコード例 */
.contact-form {
max-width: 600px;
margin: 40px auto;
padding: 30px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
font-family: 'Inter', sans-serif;
}
.contact-form h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
font-size: 1.8em;
font-weight: 700;
}
.form-group {
margin-bottom: 20px;
position: relative;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #555;
font-weight: 600;
}
.form-group input[type="text"],
.form-group input[type="email"],
.form-group input[type="tel"],
.form-group textarea {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 8px;
box-sizing: border-box;
font-size: 1em;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus {
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.2);
outline: none;
}
/* エラーテキストの基本スタイル */
.error-text {
color: #dc3545; /* 赤色 */
font-size: 0.85em;
margin-top: 5px;
display: none; /* デフォルトで非表示 */
}
/* ----- CSS条件式によるリアルタイムバリデーション ----- */
/* :invalid 疑似クラス: 入力値が無効な場合に枠線を赤くする */
input:invalid:not(:focus):not(:placeholder-shown),
textarea:invalid:not(:focus):not(:placeholder-shown) {
border-color: #dc3545;
box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.2);
}
/* :valid 疑似クラス: 入力値が有効な場合に枠線を緑にする */
input:valid:not(:focus):not(:placeholder-shown),
textarea:valid:not(:focus):not(:placeholder-shown) {
border-color: #28a745;
box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.2);
}
/* :invalid な input/textarea の直後の .error-text を表示 (ただし、まだ何も入力されていない状態ではない場合) */
input:invalid:not(:placeholder-shown) + .error-text,
textarea:invalid:not(:placeholder-shown) + .error-text {
display: block;
}
/* 送信ボタンのスタイル */
.submit-button {
display: block;
width: 100%;
padding: 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 8px;
font-size: 1.1em;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s ease, opacity 0.3s ease;
}
.submit-button:hover {
background-color: #0056b3;
}
/* :has() 疑似クラス: フォーム内に無効な必須入力欄がある場合に送信ボタンを無効化 */
/* 複雑な条件をCSSだけで実現できる強力な例です。 */
form:has(input[required]:invalid:not(:placeholder-shown)),
form:has(textarea[required]:invalid:not(:placeholder-shown)) {
/* ここにフォーム全体へのスタイル適用(例:薄暗くするなど)も可能ですが、ここではボタンに直接適用 */
}
/* 上記の :has() を使った条件で、送信ボタン自体にスタイルを適用する */
/* 無効な入力がある場合に送信ボタンを薄暗くし、クリックを無効化 */
.contact-form:has(input[required]:invalid:not(:placeholder-shown)) .submit-button,
.contact-form:has(textarea[required]:invalid:not(:placeholder-shown)) .submit-button {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none; /* JavaScriptのクリックイベントも発生させない */
}
この例では、ユーザーが入力フィールドを操作し、その値が不正な場合に枠線を赤くし、エラーメッセージを表示しています。:invalid:not(:placeholder-shown)
とすることで、初期状態(プレースホルダーが表示されている状態)ではエラーを表示せず、ユーザーが入力し始めた後にのみ検証が機能するようにしています。さらに、**:has()**をform
要素に適用することで、「フォーム内に一つでも必須かつ無効な入力フィールドがあれば、送信ボタンを無効にする」という高度なロジックをCSSだけで実現しています。これにより、「CSS 条件 によって 色 を 変える」というユーザーの意図を直接的に満たしています。
ログイン状態や在庫数でコンテンツの表示を切り替えるCSS設計
Webアプリケーションでは、ユーザーのログイン状態や商品の在庫状況など、アプリケーションの状態に応じてコンテンツの表示を切り替える要件が頻繁に発生します。これらは通常JavaScriptで制御されますが、data-*
属性とCSSの属性セレクタ、そして:has()
疑似クラスを組み合わせることで、CSSだけでも一部の表示制御を実現できます。
データ属性と:has()
を用いた状態管理
<!-- HTMLコード例 -->
<!-- ログイン状態をbody要素のdata属性で管理 -->
<!-- JavaScriptでこの属性値を変更することでCSSが反応します -->
<body data-user-status="logged-out">
<header>
<h1>マイサイト</h1>
<nav>
<a href="#" class="nav-item user-status-logged-out">ログイン</a>
<a href="#" class="nav-item user-status-logged-in">ログアウト</a>
<a href="#" class="nav-item user-status-logged-in">マイページ</a>
</nav>
</header>
<main>
<section class="product-list">
<h2>商品一覧</h2>
<div class="product-card" data-stock-status="in-stock">
<h3>商品A</h3>
<p>在庫あり</p>
<button class="add-to-cart">カートに追加</button>
</div>
<div class="product-card" data-stock-status="out-of-stock">
<h3>商品B (人気商品)</h3>
<p>現在在庫切れ</p>
<button class="add-to-cart" disabled>カートに追加</button>
</div>
<div class="product-card" data-stock-status="low-stock">
<h3>商品C</h3>
<p>残りわずか!</p>
<button class="add-to-cart">カートに追加</button>
</div>
</section>
<section class="admin-panel user-status-logged-in">
<h2>管理者パネル</h2>
<p>管理メニューにアクセスできます。</p>
<button>ユーザー管理</button>
<button>商品登録</button>
</section>
</main>
<button id="toggleLoginStatus">ログイン状態を切り替える (JS)</button>
<script>
// JavaScriptでdata属性を切り替える例
document.getElementById('toggleLoginStatus').addEventListener('click', function() {
const body = document.body;
if (body.dataset.userStatus === 'logged-out') {
body.dataset.userStatus = 'logged-in';
this.textContent = 'ログアウト状態に切り替える (JS)';
} else {
body.dataset.userStatus = 'logged-out';
this.textContent = 'ログイン状態を切り替える (JS)';
}
});
</script>
</body>
body {
font-family: 'Inter', sans-serif;
}
/* CSSコード例 */
/* ログイン状態に応じたナビゲーション表示の切り替え */
/* 初期状態(ログアウト)ではログインボタンを表示、ログアウト・マイページを非表示 */
.nav-item.user-status-logged-in {
display: none; /* デフォルトで非表示 */
}
/* body要素のdata-user-statusが"logged-in"の場合 */
body[data-user-status="logged-in"] .nav-item.user-status-logged-in {
display: inline-block; /* ログイン時のみ表示 */
}
body[data-user-status="logged-in"] .nav-item.user-status-logged-out {
display: none; /* ログイン時のみ非表示 */
}
/* 管理者パネルの表示・非表示 */
.admin-panel {
display: none; /* デフォルトで非表示 */
margin-top: 30px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
}
/* body要素のdata-user-statusが"logged-in"の場合に管理者パネルを表示 */
body[data-user-status="logged-in"] .admin-panel {
display: block;
}
/* 商品の在庫状況に応じたスタイル変更 */
.product-card[data-stock-status="out-of-stock"] {
opacity: 0.6; /* 在庫切れ商品は薄暗く表示 */
pointer-events: none; /* クリックイベントを無効化 */
border: 2px dashed #ccc;
}
.product-card[data-stock-status="out-of-stock"] .add-to-cart {
background-color: #ccc;
cursor: not-allowed;
}
.product-card[data-stock-status="in-stock"] {
border: 2px solid #28a745; /* 在庫ありは緑色の境界線 */
box-shadow: 0 2px 10px rgba(40, 167, 69, 0.1);
}
.product-card[data-stock-status="low-stock"] {
border: 2px solid #ffc107; /* 在庫わずかはオレンジ色の境界線 */
box-shadow: 0 2px 10px rgba(255, 193, 7, 0.1);
}
.product-card[data-stock-status="low-stock"] p {
color: #dc3545; /* 残りわずかのテキストを赤くする */
font-weight: bold;
}
/* --- 一般的なスタイル(上記の条件分岐とは直接関係なし) --- */
body {
margin: 0;
padding: 0;
background-color: #f4f7f6;
color: #333;
}
header {
background-color: #343a40;
color: white;
padding: 15px 30px;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 0 0 10px 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
header h1 {
margin: 0;
font-size: 1.5em;
}
nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 8px 12px;
border-radius: 5px;
transition: background-color 0.2s ease;
}
nav a:hover {
background-color: #495057;
}
main {
padding: 30px;
display: grid;
gap: 30px;
}
.product-list {
background-color: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.product-list h2 {
text-align: center;
color: #333;
margin-bottom: 25px;
}
.product-card {
background-color: #fff;
border: 1px solid #eee;
border-radius: 10px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03);
transition: all 0.3s ease;
}
.product-card h3 {
color: #007bff;
margin-top: 0;
}
.product-card p {
color: #666;
font-size: 0.9em;
margin-bottom: 15px;
}
.add-to-cart {
background-color: #007bff;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 0.9em;
transition: background-color 0.3s ease;
}
.add-to-cart:hover:not(:disabled) {
background-color: #0056b3;
}
.admin-panel button {
background-color: #6c757d;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
transition: background-color 0.3s ease;
}
.admin-panel button:hover {
background-color: #5a6268;
}
#toggleLoginStatus {
display: block;
margin: 20px auto;
padding: 10px 20px;
background-color: #20c997;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
#toggleLoginStatus:hover {
background-color: #17a2b8;
}
この例では、<body>
要素にdata-user-status
属性(logged-in
またはlogged-out
)を設定し、それに基づいてナビゲーションリンクや管理者パネルの表示・非表示を切り替えています。同様に、商品カードのdata-stock-status
属性(in-stock
、out-of-stock
、low-stock
)によって、商品の見た目や「カートに追加」ボタンの有効/無効を制御しています。
ポイントは、JavaScriptで動的に変更するのはdata-*
属性の値だけであり、実際のスタイルの変更は全てCSSの属性セレクタによって行われている点です。これにより、「CSS 値 によって 色 を 変える」という要件を、属性値という「値」の変化によって実現しています。このアプローチは、アプリケーションの状態変化とUIの表示を分離し、よりクリーンなコードベースを保つ上で非常に有効です。
@mediaと@supportsを使ったレスポンシブデザインと機能検知のベストプラクティス
現代のWebサイトは、様々なデバイスで最適に表示される必要があります。メディアクエリ (@media
) は、このレスポンシブデザインを実現するための最も基本的なCSSの「条件式」です。さらに、@supports
ルールは、特定のCSSプロパティや値がブラウザでサポートされているかどうかに基づいてスタイルを適用する、より高度な条件分岐を提供します。
メディアクエリによるレスポンシブデザイン
ビューポートの幅に基づいてレイアウトやフォントサイズを調整する一般的な例です。
/* CSSコード例 */
/* 基本スタイル(モバイルファースト) */
body {
font-size: 16px;
margin: 0;
padding: 20px;
background-color: #f8f9fa;
color: #333;
font-family: 'Inter', sans-serif;
}
.container {
max-width: 960px;
margin: 0 auto;
padding: 20px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
h1 {
color: #007bff;
text-align: center;
margin-bottom: 25px;
}
.grid-layout {
display: grid;
grid-template-columns: 1fr; /* モバイルでは1カラム */
gap: 20px;
}
.grid-item {
background-color: #e9ecef;
padding: 20px;
border-radius: 8px;
text-align: center;
}
/* メディアクエリ: 画面幅が600px以上の場合(タブレット向け) */
@media screen and (min-width: 600px) {
.grid-layout {
grid-template-columns: repeat(2, 1fr); /* 2カラム */
}
h1 {
font-size: 2.2em;
}
}
/* メディアクエリ: 画面幅が992px以上の場合(デスクトップ向け) */
@media screen and (min-width: 992px) {
.grid-layout {
grid-template-columns: repeat(3, 1fr); /* 3カラム */
}
body {
font-size: 18px; /* デスクトップではフォントサイズを大きく */
}
h1 {
font-size: 2.5em;
}
}
/* 画面の向きに応じた調整 (例: 横向きの場合) */
@media screen and (orientation: landscape) {
.container {
padding: 10px 40px; /* 横向きでは左右のパディングを増やす */
}
}
この例では、min-width
を使用して、ビューポートが特定の幅を超えた場合にのみスタイルを適用しています。これにより、モバイルファーストのアプローチで、画面サイズに応じてカラム数やフォントサイズを動的に変更できます。orientation
(画面の向き)のようなメディア特性も利用して、さらに細かな調整が可能です。
@supports
ルールによる機能検知
特定のCSSプロパティがブラウザでサポートされているかどうかに基づいて、代替スタイルや強化されたスタイルを適用できます。これは、新しいCSS機能を利用しつつ、古いブラウザへのフォールバックを提供する際に非常に有用です。
/* CSSコード例 */
.feature-box {
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
margin-bottom: 20px;
background-color: #f0f0f0;
}
/* CSS変数(カスタムプロパティ)をサポートしているブラウザの場合 */
@supports ( --my-variable: 0 ) {
.feature-box {
background-color: var(--box-bg-color, #e6f7ff); /* デフォルトは水色 */
border: 2px solid var(--box-border-color, #91d5ff); /* デフォルトは濃い水色 */
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
}
/* CSS Grid をサポートしているブラウザの場合 */
@supports ( display: grid ) {
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
/* Gridをサポートしていないブラウザのためのフォールバック(Flexboxなど)は、@supportsブロックの外に記述する */
.grid-container > div {
/* フォールバックのためのスタイル */
margin-bottom: 20px; /* 例えばFlexboxで縦並びにする場合の余白 */
}
}
/* backdrop-filter をサポートしているブラウザの場合 */
@supports ( backdrop-filter: blur(10px) ) or ( -webkit-backdrop-filter: blur(10px) ) {
.translucent-overlay {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); /* Webkit系ブラウザ用 */
background-color: rgba(255, 255, 255, 0.7); /* フォールバック */
}
}
/* backdrop-filter をサポートしていないブラウザの場合 */
@supports not ( backdrop-filter: blur(10px) ) and not ( -webkit-backdrop-filter: blur(10px) ) {
.translucent-overlay {
background-color: rgba(255, 255, 255, 0.9); /* 半透明だがぼかしなし */
}
}
@supports
ルールは、ブラウザが特定のCSS機能に対応しているかどうかを判定し、対応している場合にのみスタイルを適用するために使用されます。これにより、新しいCSSプロパティ(例えばgrid
やbackdrop-filter
など)を積極的に採用しつつ、それらをサポートしない古いブラウザでは機能が適用されないようにフォールバック(代替スタイル)を提供できます。or
やand
、not
といった論理演算子を組み合わせることで、より複雑な機能検知も可能です。
カスタムプロパティ (CSS変数) と JavaScript を組み合わせた条件分岐 (補足)
ここまでCSSのみで実現できる条件分岐について解説してきましたが、CSSには限界があることも事実です。より複雑なロジックや、外部データとの連携、ユーザーの具体的な入力値に基づく動的なスタイリングが必要な場合、カスタムプロパティ(CSS変数)とJavaScriptを組み合わせることが、最も柔軟で強力な解決策となります。
JavaScriptでCSS変数を変更し、動的にスタイルを調整する
CSSカスタムプロパティは、CSS内で変数を定義し、その値を再利用できるようにする機能です。この変数の値はJavaScriptから簡単に読み書きできるため、JavaScriptでロジックを処理し、その結果に基づいてCSS変数の値を変更することで、CSSのスタイルを動的に制御することができます。これは、「CSS 値 によって 色 を 変える」「CSS 条件 によって 色 を 変える」といった、ユーザーが期待するような動的な色の変化やその他のスタイルの調整を、効率的に実現する主要な手段となります。
<!-- HTMLコード例 -->
<div class="theme-switcher-container">
<h2>テーマ選択</h2>
<button id="lightTheme" class="theme-button" data-theme="light">ライトテーマ</button>
<button id="darkTheme" class="theme-button" data-theme="dark">ダークテーマ</button>
<button id="customColorTheme" class="theme-button" data-theme="custom">カスタムカラー</button>
<input type="color" id="colorPicker" value="#007bff">
</div>
<div class="content-box">
<h3>動的に変わるコンテンツ</h3>
<p>ここでは、選択されたテーマやカスタムカラーに応じて、背景色や文字色が変わります。</p>
<div class="status-indicator" data-status="active">
<p>現在のステータス: アクティブ</p>
</div>
</div>
body {
font-family: 'Inter', sans-serif;
}
/* CSSカスタムプロパティの定義とデフォルト値 */
:root {
--main-bg-color: #f0f2f5; /* 全体の背景色 */
--card-bg-color: #ffffff; /* カードの背景色 */
--text-color: #333333; /* 文字色 */
--accent-color: #007bff; /* 強調色 */
--border-color: #e0e0e0; /* 境界線色 */
}
body {
background-color: var(--main-bg-color);
color: var(--text-color);
transition: background-color 0.5s ease, color 0.5s ease;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.theme-switcher-container {
background-color: var(--card-bg-color);
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
text-align: center;
}
.theme-switcher-container h2 {
color: var(--text-color);
margin-bottom: 20px;
}
.theme-button {
background-color: var(--accent-color);
color: white;
border: none;
padding: 10px 18px;
border-radius: 6px;
cursor: pointer;
font-size: 1em;
margin: 0 8px;
transition: background-color 0.3s ease;
}
.theme-button:hover {
background-color: color-mix(in srgb, var(--accent-color) 80%, black); /* 強調色を少し濃く */
}
#colorPicker {
margin-left: 15px;
border: 1px solid var(--border-color);
border-radius: 5px;
cursor: pointer;
width: 60px;
height: 35px;
vertical-align: middle;
}
.content-box {
background-color: var(--card-bg-color);
color: var(--text-color);
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
width: 100%;
max-width: 500px;
text-align: center;
}
.content-box h3 {
color: var(--accent-color);
margin-bottom: 15px;
}
/* ステータスインジケータの動的な色変更 */
.status-indicator {
padding: 10px 15px;
border-radius: 8px;
margin-top: 20px;
display: inline-block;
}
.status-indicator[data-status="active"] {
background-color: #e6ffe6; /* 淡い緑 */
color: #28a745; /* 濃い緑 */
}
.status-indicator[data-status="inactive"] {
background-color: #ffe6e6; /* 淡い赤 */
color: #dc3545; /* 濃い赤 */
}
.status-indicator[data-status="pending"] {
background-color: #fff3e6; /* 淡いオレンジ */
color: #ffc107; /* 濃いオレンジ */
}
// DOMContentLoadedを待つ
document.addEventListener('DOMContentLoaded', () => {
const root = document.documentElement; // :root 要素に対応
const lightThemeButton = document.getElementById('lightTheme');
const darkThemeButton = document.getElementById('darkTheme');
const customColorThemeButton = document.getElementById('customColorTheme');
const colorPicker = document.getElementById('colorPicker');
const statusIndicator = document.querySelector('.status-indicator');
// テーマを適用する関数
function applyTheme(theme) {
if (theme === 'light') {
root.style.setProperty('--main-bg-color', '#f0f2f5');
root.style.setProperty('--card-bg-color', '#ffffff');
root.style.setProperty('--text-color', '#333333');
root.style.setProperty('--accent-color', '#007bff');
root.style.setProperty('--border-color', '#e0e0e0');
} else if (theme === 'dark') {
root.style.setProperty('--main-bg-color', '#2c3e50');
root.style.setProperty('--card-bg-color', '#34495e');
root.style.setProperty('--text-color', '#ecf0f1');
root.style.setProperty('--accent-color', '#3498db');
root.style.setProperty('--border-color', '#4a6b8a');
} else if (theme === 'custom') {
// カスタムカラーボタンが押されたときも現在のカラーピッカーの色を適用
const customColor = colorPicker.value;
root.style.setProperty('--accent-color', customColor);
// 背景色などはデフォルトに戻すか、別途カスタムで定義
root.style.setProperty('--main-bg-color', '#f0f2f5');
root.style.setProperty('--card-bg-color', '#ffffff');
root.style.setProperty('--text-color', '#333333');
root.style.setProperty('--border-color', '#e0e0e0');
}
}
// テーマボタンのイベントリスナー
lightThemeButton.addEventListener('click', () => applyTheme('light'));
darkThemeButton.addEventListener('click', () => applyTheme('dark'));
customColorThemeButton.addEventListener('click', () => {
// カスタムカラーボタンクリック時は、colorPickerの値をアクセントカラーに設定
const selectedColor = colorPicker.value;
root.style.setProperty('--accent-color', selectedColor);
// その他の色はライトテーマに準拠するか、必要に応じてカスタム定義
root.style.setProperty('--main-bg-color', '#f0f2f5');
root.style.setProperty('--card-bg-color', '#ffffff');
root.style.setProperty('--text-color', '#333333');
root.style.setProperty('--border-color', '#e0e0e0');
});
// カラーピッカーのイベントリスナー
colorPicker.addEventListener('input', (event) => {
const selectedColor = event.target.value;
root.style.setProperty('--accent-color', selectedColor); // 選択された色を強調色に適用
// カスタムカラーが選択されたことを示すため、カスタムカラーボタンにフォーカスを当てるか、
// ユーザーにカスタムテーマがアクティブであることを視覚的に示すことができます。
});
// ステータスインジケータの動的な切り替え(JavaScriptのロジック)
let currentStatusIndex = 0;
const statuses = ['active', 'inactive', 'pending'];
setInterval(() => {
currentStatusIndex = (currentStatusIndex + 1) % statuses.length;
statusIndicator.dataset.status = statuses[currentStatusIndex];
statusIndicator.querySelector('p').textContent = `現在のステータス: ${
statuses[currentStatusIndex] === 'active' ? 'アクティブ' :
statuses[currentStatusIndex] === 'inactive' ? '非アクティブ' : '保留中'
}`;
}, 2000); // 2秒ごとにステータスを切り替え
});
この例では、CSSカスタムプロパティ(--main-bg-color
, --card-bg-color
など)を定義し、JavaScriptからこれらのプロパティの値を変更しています。テーマ切り替えボタンをクリックすると、JavaScriptがdocument.documentElement.style.setProperty()
メソッドを使って:root
要素のCSS変数を更新します。これにより、CSS全体でその変数が使用されている箇所のスタイルが動的に変化します。
また、status-indicator
の例では、JavaScriptがdata-status
属性の値を2秒ごとに切り替えており、CSSの属性セレクタによって背景色と文字色が変化しています。これは「CSS 条件 によって 色 を 変える」という検索意図に合致する、実践的な色の変更例です。JavaScriptが状態を管理し、CSSがその状態を視覚的に表現するという、役割分担の明確な設計パターンを示しています。
各機能のメリット・デメリット、注意点
CSSで条件分岐を模倣する様々なテクニックを理解したところで、それぞれのメリット、デメリット、そして利用する上での注意点を把握しておくことは、堅牢でパフォーマンスの高いWebサイトを構築するために不可欠です。
パフォーマンスへの影響、ブラウザサポート状況、コードの可読性など
1. 疑似クラス(:hover
, :checked
, :has()
など)

メリット:
- パフォーマンス: 非常に高いパフォーマンスを発揮します。ブラウザのネイティブ機能として最適化されており、JavaScriptを使用するよりも軽量です。
- シンプルさ: 簡潔な記述で直感的にスタイルを適用できます。特に
:hover
や:focus
などは、CSSだけでインタラクティブなフィードバックを容易に実装できます。 - 関心の分離: スタイルに関するロジックをCSSファイル内に完結させ、HTMLやJavaScriptを汚染しません。
デメリット:
- 限定的な条件: HTML要素の状態やDOMツリー内の位置関係に限定され、JavaScriptのような複雑なデータ処理やビジネスロジックに基づく条件分岐はできません。
:has()
のブラウザサポート::has()
は比較的新しい機能(CSSセレクタLevel 4)であり、モダンブラウザでは広くサポートされていますが、古いブラウザ(IEなど)ではサポートされていません。利用する際は、Can I use…などで最新のサポート状況を確認し、必要に応じてフォールバックを検討する必要があります。- チェーンの複雑化: 複数の疑似クラスを連結しすぎると、セレクタが非常に長く、可読性が低下する可能性があります。
注意点:
:has()
を使用する際は、その複雑性からパフォーマンスに影響を与える可能性が指摘されています(特に大規模なDOMツリーや頻繁な更新がある場合)。しかし、通常の使用では問題になることは稀であり、メリットの方が大きい場合が多いです。:-webkit-any()
,:-moz-any()
のような古いベンダープレフィックスの疑似クラスは使用せず、標準化された:is()
や:where()
などの代替を検討してください。
2. 属性セレクタ ([attr="value"]
など)

メリット:
- 柔軟性: 属性の種類や値によって細かくスタイルを制御できます。
data-*
属性と組み合わせることで、JavaScriptで状態を管理し、CSSでスタイルを適用する際の強力な接点となります。 - 意味論的なマークアップ: スタイルに特化したクラス名を追加する代わりに、HTMLの属性が持つ意味(例:
type="email"
、data-status="active"
)に基づいてスタイリングできるため、よりセマンティックなHTMLを維持できます。 - パフォーマンス: 一般的にパフォーマンスは良好です。
デメリット:
- 値の一致に依存: 属性値が正確に一致しているか、特定のパターンに合致しているかに依存します。動的な計算や複雑な文字列操作はできません。
- HTMLへの依存: HTML側に適切な属性が設定されている必要があります。HTML構造の変更がCSSに影響を与える可能性があります。
注意点:
- 大量の属性セレクタや、非常に長い属性値に対する部分一致セレクタ(
=
)は、パフォーマンスにわずかな影響を与える可能性があります。 - 属性名や属性値に誤りがあると、意図しないスタイルが適用されたり、全く適用されなかったりします。
3. メディアクエリ (@media
)

メリット:
- レスポンシブデザインの根幹: デバイスの画面サイズ、解像度、向きなど、様々な環境特性に基づいてスタイルを適用できるため、異なるデバイスに最適化されたUIを提供できます。
- パフォーマンス: ブラウザがレンダリング前に適切なスタイルシートをロード・適用するため、非常に効率的です。
- フォールバックが容易:
min-width
やmax-width
を使って、古いブラウザや小さな画面に基本的なスタイルを提供しつつ、より新しい/大きな画面に高度なスタイルを適用する「モバイルファースト」のアプローチに適しています。
デメリット:
- CSSファイルの肥大化: 多くのブレイクポイントを設定すると、CSSファイルが長くなり、管理が複雑になる可能性があります。
- 条件の限界: デバイスの特性に限定され、ユーザーの行動やアプリケーションの状態といった動的な条件には直接対応できません。
注意点:
- モバイルファースト: 小さな画面向けのスタイルを先に定義し、その後
min-width
で大きな画面向けのスタイルを上書きしていく「モバイルファースト」のアプローチを採用することを強く推奨します。 em
やrem
といった相対単位をブレイクポイントに利用すると、ユーザーのフォントサイズ設定に対応しやすくなります。
4. @supports
ルール

メリット:
- プログレッシブエンハンスメント: 最新のCSS機能を利用しつつ、それらをサポートしないブラウザには安全なフォールバックを提供できるため、サイトの堅牢性が向上します。
- 機能検知の明確化: CSSのコード内で、どのスタイルが特定の機能に依存しているかを明確に示せます。
デメリット:
- 複雑性の増加: 古いブラウザへの対応が必要な場合、通常のCSSルールに加えて
@supports
ブロックを記述するため、コード量が増える可能性があります。 - すべてのケースに対応できない: JavaScriptのような複雑なブラウザ機能検出には適していません(例: 特定のAPIが利用可能か、WebAssemblyをサポートしているかなど)。
注意点:
@supports not (...)
を使って、特定の機能がサポートされていない場合にのみスタイルを適用することも可能です。- ベンダープレフィックス(
webkit-
,moz-
など)が必要なプロパティを検出する場合、それらをor
で繋いで両方をチェックする必要があります。
全体的な注意点:複雑になりすぎるとメンテナンスが困難になる
どんなに強力な機能も、過度に使用したり、不必要に複雑なセレクタを記述したりすると、コードの可読性やメンテナンス性が著しく低下します。
- CSSの特異性(Specificity): 複雑なセレクタほど特異性が高くなり、意図しないスタイルが適用されたり、スタイルを上書きするのが難しくなったりします。
- BEM (Block Element Modifier) や SMACSS (Scalable and Modular Architecture for CSS) のようなCSS設計手法を導入し、セレクタの命名規則や構造化を徹底することで、可読性とメンテナンス性を向上させることができます。
- コメントの活用: 特に複雑な条件分岐を使用する場合は、なぜそのスタイルを適用しているのか、どのような条件で発動するのかを明確にコメントで残しましょう。
- JavaScriptとの適切な役割分担: CSSだけで無理に複雑なロジックを実装しようとせず、データの操作やイベントドリブンな振る舞いはJavaScriptに任せ、CSSは「状態」を視覚的に表現する役割に徹するという線引きが重要です。これにより、各レイヤーが持つ特性を最大限に活かし、効率的な開発が可能になります。
よくある質問(FAQ)
-
CSSに「if/else」文は本当にないのですか?
-
はい、CSSにはJavaScriptのようなプログラミング言語にあるような、直接的なif/else文や条件分岐の構文は存在しません。CSSは「スタイルシート言語」であり、要素が持つ状態や属性、環境(メディアクエリ)に基づいて宣言的にスタイルを適用するのがその役割です。しかし、この記事で解説した疑似クラス(:has(), :not(), :checked, :validなど)、属性セレクタ、メディアクエリ、そして@supportsルールなどを用いることで、実質的に「もしこの条件が満たされたら、このスタイルを適用する」というような、条件に応じたスタイリングを実現できます。
-
:has()セレクタはどこまで使えますか? ブラウザの対応状況はどうですか?
-
:has()はCSSセレクタLevel 4で導入された非常に強力な疑似クラスで、「親要素が特定の子孫要素を持つ場合にスタイルを適用する」という、これまでCSS単体では困難だったことを可能にしました。例えば、:has(img)と書けば画像を含む要素を、:has(+ p)と書けば直後に<p>要素が続く要素を対象にできます。
ブラウザの対応状況に関しては、2023年以降の主要なモダンブラウザ(Chrome, Firefox, Safari, Edgeなど)では幅広くサポートされています。しかし、Internet Explorerのような古いブラウザではサポートされていません。そのため、もしこれらの古いブラウザもサポート対象に含む必要がある場合は、JavaScriptでのフォールバックや、:has()を使用しない代替手段を検討する必要があります。Can I use…で最新の対応状況を確認することを強く推奨します。
-
JavaScriptを使わずにCSSだけで動的なコンテンツ表示を切り替えることはできますか?
-
一部の「動的なコンテンツ表示の切り替え」であれば、CSSだけで実現可能です。例えば、チェックボックスの:checkedと隣接セレクタ(+や~)を組み合わせて、要素の表示/非表示を切り替える「タブUI」や「アコーディオンUI」などを作成できます。
また、data-*属性をHTML要素に設定し、JavaScriptでその属性値を変更するだけで、CSSの属性セレクタが反応してスタイルを切り替えることも可能です。例えば、data-visibility=”hidden”のような属性をJavaScriptで切り替え、CSSで[data-visibility=”hidden”] { display: none; }とすることで、見た目を制御できます。
しかし、複雑なユーザーインタラクション、外部からのデータ取得、URLの変更に基づくルーティングなどは、CSS単独では対応できず、JavaScriptとの連携が不可欠となります。CSSは「表示」に特化し、JavaScriptは「振る舞い」に特化するという役割分担が重要です。
-
「CSS 値 によって 色 を 変える」や「CSS 条件 によって 色 を 変える」をCSSだけで実現する方法は?
-
これらの表現は、CSSの様々な「条件式」のテクニックを組み合わせることで実現可能です。
疑似クラスの活用:
:hover
で要素にマウスが乗ったときに色を変える。:focus
で入力フィールドがフォーカスされたときに枠線の色を変える。:valid
/:invalid
でフォームの入力値の妥当性に応じてテキストや枠線の色を変える。:checked
でチェックボックスが選択されたときに隣接するラベルの色を変える。
属性セレクタの活用:
[data-status="active"] { color: green; }
のように、HTMLのdata-*
属性の値に応じて要素の色を変える。[type="button"] { background-color: blue; }
のように、特定の属性を持つ要素の色を変える。
カスタムプロパティ (CSS変数) とJavaScriptの連携:
- 最も柔軟な方法です。CSSで
-primary-color: blue;
のような変数を定義し、JavaScriptでユーザーの選択やアプリケーションの状態に応じてdocument.documentElement.style.setProperty('--primary-color', 'red');
のように変数の値を変更します。これにより、CSS全体でその変数が使われている要素の色が動的に変わります。これは、「CSS 値 によって 色 を 変える」という意図を直接的に実現する強力な方法です。
このように、CSS単体でも多くのケースで色の変更を条件付きで実現できますが、JavaScriptとの組み合わせることで、より複雑なロジックや外部データに基づいた色の変更が可能になります。
まとめ
この記事では、「CSS 条件式」というキーワードから、CSSが持つ宣言的な特性を深く掘り下げ、JavaScriptのような直接的なif/else
構文が存在しないにも関わらず、いかにして「条件に応じたスタイルの適用」を実現できるかを詳細に解説しました。
CSSの「条件式」をマスターすることは、あなたのWeb制作スキルを飛躍的に向上させます。JavaScriptの介入を最小限に抑えつつ、パフォーマンスが高く、メンテナンスしやすいコードで、より洗練されたユーザー体験を提供できるようになるでしょう。この記事で紹介した知識と実践例を参考に、ぜひあなたのプロジェクトでCSSの可能性を最大限に引き出してください。
