レスポンシブ対応のブレイクポイント管理 - 変数とミックスインの使い分け

レスポンシブ対応のコードでは、メディアクエリのブレイクポイントがプロジェクト全体に散らばりがちです。768px1024px といったマジックナンバーがファイルごとに直書きされていると、デバイス対応の基準を変更するときに全ファイルを検索・置換する羽目になります。

マジックナンバーの散在

以下のようなコードは多くのプロジェクトで見かけます。

/* header.css */
@media (max-width: 768px) {
  .header { padding: 8px; }
}

/* sidebar.css */
@media (max-width: 768px) {
  .sidebar { display: none; }
}

/* card.css */
@media (max-width: 1024px) {
  .card { flex-direction: column; }
}

768px がタブレットの境界なのかスマートフォンの境界なのか、数値だけでは意図が読み取れません。さらに、ある開発者は 768px、別の開発者は 767px を使うといったブレがプロジェクト内で発生します。

CSS カスタムプロパティでの管理

CSS カスタムプロパティ(CSS 変数)でブレイクポイントを一元管理する方法がまず思いつきますが、実はこれには制約があります。

:root {
  --bp-sm: 576px;
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;
}

/* これは動かない */
@media (max-width: var(--bp-md)) {
  .header { padding: 8px; }
}

CSS カスタムプロパティはメディアクエリの条件式の中では使えません。これは仕様上の制限です。

メディアクエリはカスケードの外側で評価されるため、カスケードに依存するカスタムプロパティを参照できない。

カスタムプロパティが有効なのは、プロパティの値として使う場面に限られます。メディアクエリの条件にブレイクポイントの変数を渡すには、Sass 変数やプリプロセッサの仕組みが必要です。

Sass 変数によるブレイクポイント定義

Sass では変数がコンパイル時に展開されるため、メディアクエリの条件にそのまま使えます。

/* _variables.scss */
$bp-sm: 576px;
$bp-md: 768px;
$bp-lg: 1024px;
$bp-xl: 1280px;

この変数をメディアクエリに直接書くこともできます。

@media (max-width: $bp-md) {
  .header { padding: 8px; }
}

ただし、この書き方にはまだ問題があります。max-width なのか min-width なのかが呼び出し側に委ねられるため、プロジェクト内でモバイルファースト(min-width)とデスクトップファースト(max-width)が混在するリスクがあります。

ミックスインによる抽象化

ブレイクポイントの値だけでなく、メディアクエリの書き方自体をミックスインで統一します。

/* _mixins.scss */
@mixin sp {
  @media (max-width: #{$bp-md - 1px}) { @content; }
}

@mixin tab {
  @media (min-width: $bp-md) and (max-width: #{$bp-lg - 1px}) { @content; }
}

@mixin pc {
  @media (min-width: $bp-lg) { @content; }
}

呼び出し側はブレイクポイントの数値を意識する必要がなくなります。

.header {
  padding: 16px;

  @include sp {
    padding: 8px;
  }
}

.sidebar {
  display: block;

  @include sp {
    display: none;
  }
}
変数の直接使用

ブレイクポイントの数値は統一されるが、max-width / min-width の方向やクエリの書き方は開発者ごとにばらつく。

ミックスイン経由

数値・方向・クエリ構文のすべてが統一される。呼び出し側は「sp ならスマートフォン向け」という意味だけを意識すればよい。

命名の方針

ミックスイン名にデバイス名を使うか、抽象的なサイズ名を使うかはチームの方針によります。

方針特徴
デバイス名sp, tab, pc直感的だがデバイスの境界が曖昧
サイズ名sm, md, lg, xlTailwind 等と一貫性がある

デバイス名は「sp はスマートフォン」という対応が明確ですが、タブレットとデスクトップの境界は年々曖昧になっています。サイズ名のほうが特定のデバイスに縛られない分、長期的に安定します。ただし md が何ピクセルなのかをチーム全員が把握していないと混乱の元になるため、ドキュメントやコメントでの補足が必要です。

マップとループによる拡張

ブレイクポイントが増えてきた場合、Sass のマップで管理すると追加・削除が容易になります。

/* _variables.scss */
$breakpoints: (
  sm: 576px,
  md: 768px,
  lg: 1024px,
  xl: 1280px,
);

/* _mixins.scss */
@mixin mq-up($key) {
  $value: map-get($breakpoints, $key);
  @media (min-width: $value) { @content; }
}

@mixin mq-down($key) {
  $value: map-get($breakpoints, $key) - 1px;
  @media (max-width: $value) { @content; }
}
/* 使用例 */
.card {
  flex-direction: column;

  @include mq-up(lg) {
    flex-direction: row;
  }
}

mq-up はモバイルファースト(min-width)、mq-down はデスクトップファースト(max-width)に対応します。プロジェクトでどちらか一方に統一するのが理想的ですが、部分的に逆方向が必要な場面もあるため、両方用意しておくのが現実的です。

変数だけで十分なケース

小規模プロジェクトや個人開発で、メディアクエリの記述量が少なく、方向(min/max)の統一が自然に保てる場合。

ミックスインが必要なケース

複数人での開発や長期運用プロジェクトで、ブレイクポイントの数値・方向・構文をすべて強制的に統一する必要がある場合。

規約としてどちらを採用するかは、プロジェクトの規模とチームの人数で決まります。1 人で書いているなら変数だけでも問題は起きにくいですが、3 人以上が同じ CSS に触れる環境では、ミックスインによる強制のほうがブレを防げます。