モダンCSS概要

近年のCSSは革命的な進化を遂げています。従来JavaScriptが必要だった機能がCSSだけで実装可能になりました。

主な新機能

  • Container Queries - 親要素のサイズに応じたスタイル
  • :has() - 子要素の存在に基づくスタイル
  • Subgrid - 入れ子グリッドの整列
  • :is()/:where() - セレクタの簡潔化
  • aspect-ratio - アスペクト比の指定

1. Container Queries(コンテナクエリ)

親要素のサイズに応じてスタイルを変更できる画期的な機能です。

従来のメディアクエリの問題

/* 画面幅でしか判定できない */
@media (max-width: 768px) {
  .card { font-size: 14px; }
}

/* 問題:カードがサイドバーにある場合も
   メイン領域にある場合も同じスタイル */

Container Queriesで解決

/* コンテナを定義 */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

.main {
  container-type: inline-size;
  container-name: main;
}

/* コンテナのサイズで判定 */
@container (max-width: 400px) {
  .card {
    font-size: 14px;
    padding: 1rem;
  }
}

@container (min-width: 600px) {
  .card {
    font-size: 18px;
    padding: 2rem;
    display: grid;
    grid-template-columns: 1fr 2fr;
  }
}

実装例

<style>
.container-demo {
  container-type: inline-size;
  border: 3px solid #2a2a2a;
  padding: 1rem;
  resize: horizontal;
  overflow: auto;
}

.responsive-card {
  background: #ffeb3b;
  border: 2px solid #000;
  padding: 1rem;
}

@container (min-width: 400px) {
  .responsive-card {
    padding: 2rem;
    font-size: 1.2rem;
  }
  .responsive-card::before {
    content: '📱→💻 幅400px以上';
    display: block;
    font-weight: bold;
    margin-bottom: 0.5rem;
  }
}

@container (max-width: 399px) {
  .responsive-card::before {
    content: '📱 幅400px未満';
    display: block;
    font-weight: bold;
    margin-bottom: 0.5rem;
  }
}
</style>

<div class="container-demo">
  <div class="responsive-card">
    このボックスの右下をドラッグしてリサイズ!
  </div>
</div>

実装結果

📱 Container Queries デモ
(一部ブラウザのみ対応)
このエリアをリサイズすると内容が変化します

container-typeの種類

/* 幅のみ監視(推奨) */
container-type: inline-size;

/* 高さのみ監視 */
container-type: block-size;

/* 幅と高さ両方監視 */
container-type: size;

実用例:カードコンポーネント

.card-container {
  container-type: inline-size;
}

/* 狭い場合は縦並び */
.card {
  display: flex;
  flex-direction: column;
}

/* 広い場合は横並び */
@container (min-width: 500px) {
  .card {
    flex-direction: row;
    gap: 2rem;
  }
  
  .card-image {
    width: 40%;
  }
  
  .card-content {
    width: 60%;
  }
}

2. :has() 擬似クラス

「親セレクタ」がついに実現!子要素の存在に応じて親のスタイルを変更できます。

基本構文

/* 画像を含むカードは特別なスタイル */
.card:has(img) {
  border: 3px solid #2196f3;
}

/* チェックボックスがチェックされている親 */
.form-group:has(input:checked) {
  background: #e8f5e9;
}

/* エラーメッセージがある場合 */
.input-wrapper:has(.error-message) {
  border-color: #f44336;
}

実装例

<style>
.has-demo-card {
  border: 2px solid #ccc;
  padding: 1.5rem;
  margin: 1rem 0;
  transition: all 0.3s;
}

.has-demo-card:has(.highlight) {
  border-color: #2196f3;
  background: #e3f2fd;
  border-width: 3px;
}
</style>

<div class="has-demo-card">
  <p>通常のカード</p>
</div>

<div class="has-demo-card">
  <p>この中に<span class="highlight">ハイライト</span>があるカード</p>
</div>

実装結果

通常のカード

この中にハイライトがあるカード(:has()で検出)

実用例:フォームバリデーション

/* エラーがあるフィールド */
.field:has(.error) {
  border-left: 4px solid #f44336;
  background: #ffebee;
}

/* 成功状態 */
.field:has(.success) {
  border-left: 4px solid #4caf50;
  background: #e8f5e9;
}

/* チェックされたラジオボタンの親 */
.option:has(input[type="radio"]:checked) {
  background: #2196f3;
  color: white;
  font-weight: bold;
}

兄弟要素の制御

/* ホバーしている要素以外を薄く */
.gallery:has(.item:hover) .item:not(:hover) {
  opacity: 0.5;
}

/* フォーカスがある場合、ラベルを強調 */
form:has(input:focus) label {
  color: #2196f3;
  font-weight: bold;
}

3. Subgrid

グリッドアイテムの中でも親グリッドの線を共有できます。

従来の問題

/* 親グリッド */
.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}

/* 子要素内のグリッドは独立 */
.grid-item {
  display: grid;
  grid-template-columns: 1fr 1fr; /* 親と揃わない */
}

Subgridで解決

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

.grid-item {
  display: grid;
  grid-column: span 3;
  grid-template-columns: subgrid; /* 親のグリッドを継承 */
}

実装例

<style>
.subgrid-demo {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  border: 3px solid #2a2a2a;
  padding: 1rem;
}

.subgrid-item {
  background: #ffeb3b;
  border: 2px solid #000;
  padding: 1rem;
  display: grid;
  grid-column: span 3;
  grid-template-columns: subgrid;
  gap: 1rem;
}

.subgrid-child {
  background: white;
  border: 2px solid #2196f3;
  padding: 0.5rem;
  text-align: center;
}
</style>

<div class="subgrid-demo">
  <div style="background: #e0e0e0; padding: 1rem;">Col 1</div>
  <div style="background: #e0e0e0; padding: 1rem;">Col 2</div>
  <div style="background: #e0e0e0; padding: 1rem;">Col 3</div>
  
  <div class="subgrid-item">
    <div class="subgrid-child">A</div>
    <div class="subgrid-child">B</div>
    <div class="subgrid-child">C</div>
  </div>
</div>

実装結果(対応ブラウザのみ)

Subgridデモ(Firefox/Safari対応)

Col 1
Col 2
Col 3
A
B
C

4. :is() と :where()

セレクタを簡潔に書ける便利な擬似クラスです。

:is() - セレクタリストの簡潔化

/* 従来 */
h1 a:hover,
h2 a:hover,
h3 a:hover {
  color: #2196f3;
}

/* :is()で簡潔に */
:is(h1, h2, h3) a:hover {
  color: #2196f3;
}

:where() - 詳細度0

/* :is()は最も高い詳細度を持つ */
:is(#id, .class) { } /* 詳細度: 1,0,0 */

/* :where()は常に詳細度0 */
:where(#id, .class) { } /* 詳細度: 0,0,0 */

/* 上書きしやすいデフォルトスタイルに便利 */
:where(button, .btn) {
  padding: 0.5rem 1rem;
  border: none;
}

/* 簡単に上書き */
.btn-large {
  padding: 1rem 2rem;
}

実用例

/* フォーム要素のまとめ */
:is(input, textarea, select):focus {
  outline: 2px solid #2196f3;
  outline-offset: 2px;
}

/* 見出しのまとめ */
:is(h1, h2, h3, h4, h5, h6) {
  font-weight: bold;
  line-height: 1.2;
  margin-bottom: 1rem;
}

/* ネガティブマージン除外 */
:is(article, section) > :where(h2, h3):first-child {
  margin-top: 0;
}

5. aspect-ratio

アスペクト比を直接指定できます。

従来のpadding-topトリック

/* 16:9の比率を保つ */
.video-wrapper {
  position: relative;
  padding-top: 56.25%; /* 9/16 * 100 */
}

.video-wrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

aspect-ratioで簡単に

.video-wrapper {
  aspect-ratio: 16 / 9;
}

.video-wrapper iframe {
  width: 100%;
  height: 100%;
}

/* その他の例 */
.square { aspect-ratio: 1; }
.photo { aspect-ratio: 4 / 3; }
.portrait { aspect-ratio: 2 / 3; }

実装結果

1:1
正方形
16:9
動画
4:3
写真

6. accent-color

フォーム部品の色を一括変更できます。

使い方

/* チェックボックス、ラジオボタン、レンジなど */
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
  accent-color: #2196f3;
}

/* 全体に適用 */
:root {
  accent-color: #4caf50;
}

実装結果

7. ブラウザ対応状況

Container Queries:
  ✅ Chrome 105+
  ✅ Edge 105+
  ✅ Safari 16+
  ✅ Firefox 110+

:has():
  ✅ Chrome 105+
  ✅ Edge 105+
  ✅ Safari 15.4+
  ✅ Firefox 121+

Subgrid:
  ✅ Firefox 71+
  ✅ Safari 16+
  ✅ Chrome 117+
  ✅ Edge 117+

:is()/:where():
  ✅ すべてのモダンブラウザ

aspect-ratio:
  ✅ すべてのモダンブラウザ

accent-color:
  ✅ Chrome 93+
  ✅ Edge 93+
  ✅ Firefox 92+
  ✅ Safari 15.4+

8. 実践的な組み合わせ例

レスポンシブカードグリッド

.grid {
  container-type: inline-size;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

.card {
  aspect-ratio: 1;
  display: grid;
  place-items: center;
}

/* 親が狭い場合 */
@container (max-width: 600px) {
  .card {
    aspect-ratio: 16 / 9;
  }
}

/* 画像を含むカードは特別なスタイル */
.card:has(img) {
  aspect-ratio: 4 / 3;
  background: linear-gradient(135deg, #667eea, #764ba2);
}

インタラクティブフォーム

/* アクセントカラー設定 */
:root {
  accent-color: #2196f3;
}

/* エラーがあるフィールド */
.field:has(.error) {
  border-left: 4px solid #f44336;
}

/* チェックされたオプション */
.option:has(input:checked) {
  background: #e3f2fd;
  border-color: #2196f3;
}

/* フォーカス時のラベル */
:is(.field, .option):has(:focus) label {
  color: #2196f3;
  font-weight: bold;
}

まとめ

モダンCSSで開発が劇的に効率化します!

主なメリット

  • ✅ JavaScriptが不要になるケースが増加
  • ✅ より直感的で保守しやすいコード
  • ✅ パフォーマンス向上
  • ✅ アクセシビリティ改善

学習の優先順位

  1. :has() - すぐに使える、効果大
  2. aspect-ratio - 頻繁に使う
  3. :is()/:where() - コード簡潔化
  4. Container Queries - 複雑だが強力
  5. Subgrid - 特定用途向け

今すぐ試そう

  • 既存のpadding-topトリックをaspect-ratioに置換
  • メディアクエリをContainer Queriesに移行検討
  • :has()でJavaScriptの条件分岐を削減
  • フォーム部品にaccent-colorを適用