@mediaのネスト(レスポンシブ記述)
『Sass』ではメディアクエリ(@media)をセレクタの中にネストして記述できます。通常の CSS では @media ブロックをファイルの別の場所に書く必要がありますが、Sass のネスト構文を使うと、そのセレクタに関連するブレークポイントを同じブロック内にまとめることができます。コンパイル後は通常の @media ブロックに自動的に展開されます。
通常の CSS では次のようにセレクタとメディアクエリが分離してしまいます。
/* 通常の CSS:セレクタとメディアクエリが離れた場所に書かれます */
.card {
font-size: 1rem;
padding: 24px;
}
@media (max-width: 768px) {
.card {
font-size: 0.9rem;
padding: 16px;
}
}
構文
// ========================================
// @media をセレクタ内にネストする基本構文
// ========================================
// セレクタブロックの中に @media を書きます
.セレクタ {
プロパティ: 値;
// メディアクエリをネストします
@media (条件) {
プロパティ: 値; // この中は .セレクタ に適用されます
}
}
// コンパイル後の CSS(参考)
// .セレクタ { プロパティ: 値; }
// @media (条件) { .セレクタ { プロパティ: 値; } }
// ========================================
// & を使った明示的な親セレクタ参照
// ========================================
// & を使うと親セレクタをより明示的に記述できます
.セレクタ {
プロパティ: 値;
@media (条件) {
& {
プロパティ: 値;
}
}
}
@media ネストの主なパターン
| パターン | 記述例 | 説明 |
|---|---|---|
| max-width(モバイルファースト以外) | @media (max-width: 768px) { ... } | 画面幅が指定値以下の端末にスタイルを適用します。 |
| min-width(モバイルファースト) | @media (min-width: 768px) { ... } | 画面幅が指定値以上の端末にスタイルを適用します。 |
| 範囲指定 | @media (min-width: 768px) and (max-width: 1023px) { ... } | タブレットなど特定の幅の範囲にだけスタイルを適用します。 |
| 印刷用 | @media print { ... } | 印刷時にのみ適用するスタイルをセレクタと同じ場所に書けます。 |
| 配色モード | @media (prefers-color-scheme: dark) { ... } | ダークモード時のスタイルをコンポーネントごとにまとめられます。 |
| @mixin との組み合わせ | @include sp { ... } | mixin でメディアクエリを包んで、より短く書けます。 |
サンプルコード
カードコンポーネントとナビゲーションを例に、@media のネストを活用したレスポンシブスタイルの書き方を示します。各コンポーネントのブレークポイント定義が一か所にまとまり、変更箇所が一目でわかります。
// ========================================
// カードコンポーネントのレスポンシブ対応
// ========================================
.card {
display: grid;
grid-template-columns: 200px 1fr; // PC はサムネイル + テキストの 2 カラム
gap: 24px;
padding: 24px;
background-color: #ffffff;
border: 1px solid #dee2e6;
border-radius: 8px;
// タブレット幅では 1 カラムに切り替えます
@media (max-width: 1023px) {
grid-template-columns: 1fr;
gap: 16px;
padding: 20px;
}
// スマートフォン幅ではパディングをさらに縮小します
@media (max-width: 767px) {
padding: 16px;
gap: 12px;
}
// カードのタイトル
&__title {
font-size: 1.25rem;
font-weight: bold;
color: #2c3e50;
margin: 0 0 8px;
// スマートフォン幅ではフォントサイズを小さくします
@media (max-width: 767px) {
font-size: 1.1rem;
}
}
// カードの本文
&__body {
font-size: 1rem;
line-height: 1.7;
color: #495057;
@media (max-width: 767px) {
font-size: 0.9rem;
}
}
}
// ========================================
// ナビゲーションのレスポンシブ対応
// ========================================
.nav {
display: flex;
align-items: center;
gap: 8px;
padding: 0 32px;
background-color: #2c3e50;
height: 60px;
// スマートフォン幅では縦並びのドロワーメニューに切り替えます
@media (max-width: 767px) {
display: none; // JavaScript でトグル表示します
&.is-open {
display: flex;
flex-direction: column;
align-items: stretch;
height: auto;
padding: 8px 0;
}
}
// ナビゲーションリンク
&__link {
display: block;
padding: 8px 16px;
color: #ecf0f1;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.2s;
&:hover {
background-color: #34495e;
}
// スマートフォン幅では上下のパディングを広げてタップしやすくします
@media (max-width: 767px) {
padding: 12px 24px;
border-radius: 0;
border-bottom: 1px solid #34495e;
}
}
}
// ========================================
// ヒーローセクション:複数ブレークポイントの管理
// ========================================
.hero {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 480px;
padding: 64px 32px;
text-align: center;
background-color: #f0f4f8;
// タブレット幅
@media (max-width: 1023px) {
min-height: 360px;
padding: 48px 24px;
}
// スマートフォン幅
@media (max-width: 767px) {
min-height: 280px;
padding: 40px 16px;
}
// 見出し
&__heading {
font-size: 2.5rem;
font-weight: bold;
color: #2c3e50;
margin: 0 0 16px;
line-height: 1.3;
@media (max-width: 1023px) {
font-size: 2rem;
}
@media (max-width: 767px) {
font-size: 1.5rem;
margin-bottom: 12px;
}
}
// サブテキスト
&__sub {
font-size: 1.1rem;
color: #636e72;
max-width: 600px;
@media (max-width: 767px) {
font-size: 0.95rem;
}
}
}
// ========================================
// ダークモード対応(prefers-color-scheme)
// ========================================
.article {
background-color: #ffffff;
color: #2c3e50;
padding: 32px;
border-radius: 8px;
// ユーザーのOSがダークモードに設定されている場合に適用します
@media (prefers-color-scheme: dark) {
background-color: #1e272e;
color: #dfe6e9;
}
&__title {
font-size: 1.5rem;
border-bottom: 2px solid #dee2e6;
padding-bottom: 8px;
margin-bottom: 16px;
@media (prefers-color-scheme: dark) {
border-bottom-color: #4a5568;
}
}
}
// ========================================
// 印刷用スタイルのネスト
// ========================================
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 32px;
background-color: #2c3e50;
color: #ffffff;
// 印刷時はヘッダーの背景を白にして背景色の印刷設定に依存しないようにします
@media print {
background-color: #ffffff;
color: #000000;
border-bottom: 1px solid #000000;
}
}
// 印刷時に不要なナビゲーションは非表示にします
.nav {
@media print {
display: none;
}
}
よくあるミス
@media をネストしすぎてコンパイル後に大量の重複メディアクエリが出力される
Sass の @media ネストは、同じ条件のメディアクエリを複数のセレクタに書いても自動では統合されません。コンポーネントごとに同じブレークポイントをネストすると、コンパイル後の CSS に同一条件の @media ブロックが大量に展開されます。
ng_example.scss
// 3つのコンポーネントそれぞれに同じブレークポイントをネストしています
.header {
font-size: 1.5rem;
@media (max-width: 767px) {
font-size: 1.2rem;
}
}
.hero {
padding: 64px;
@media (max-width: 767px) {
padding: 32px;
}
}
.footer {
height: 120px;
@media (max-width: 767px) {
height: auto;
}
}
/* コンパイル後:同じ @media (max-width: 767px) が3回出力されます */
.header { font-size: 1.5rem; }
@media (max-width: 767px) {
.header { font-size: 1.2rem; }
}
.hero { padding: 64px; }
@media (max-width: 767px) {
.hero { padding: 32px; }
}
.footer { height: 120px; }
@media (max-width: 767px) {
.footer { height: auto; }
}
このような重複は、ブレークポイントを mixin で名前付き化しておき、コンポーネントのネストを最小限に抑えることで管理しやすくなります。CSS ファイルサイズへの影響は gzip 圧縮でほぼ吸収されますが、ルールの重複が多い場合は @media をファイルの末尾にまとめる構成も選択肢のひとつです。
@media の条件を入れ子にしたときに AND 結合されることへの誤解
@media ブロックの中に別の @media をネストすると、Sass はその2つの条件を and で結合します。@media (min-width: 768px) の中に @media (max-width: 1024px) を書いても OR にはならず、「768px 以上かつ 1024px 以下」という範囲指定として展開されます。
ng_example.scss
// 内側の @media が OR 条件になると思って書いてしまう例
.sidebar {
display: block;
@media (min-width: 768px) {
width: 240px;
// 「タブレット幅のみ」に絞り込もうとしてネストしています
@media (max-width: 1024px) {
width: 200px;
}
}
}
/* コンパイル後:AND 結合になります(意図通りではある場合もありますが、誤解しやすい点です) */
.sidebar { display: block; }
@media (min-width: 768px) {
.sidebar { width: 240px; }
}
@media (min-width: 768px) and (max-width: 1024px) {
.sidebar { width: 200px; }
}
範囲指定が目的であれば、最初から @media (min-width: 768px) and (max-width: 1024px) と1つの @media に書くほうが意図が明確です。@media の入れ子は条件を絞り込む(AND)ためにのみ機能します。
概要
『Sass』の @media ネストは、セレクタに関連するブレークポイントのスタイルをそのセレクタのブロック内にまとめて記述できる機能です。通常の CSS では @media ブロックをファイルの別の場所に書く必要があり、あるコンポーネントのレスポンシブ定義がどこにあるかを探す手間が生じます。Sass のネストを使うと、コンポーネントのすべての状態を一か所に集約でき、変更・削除・移動が容易になります。コンパイル後は通常の @media ブロックに自動的に展開されるため、出力される CSS のサイズや動作に違いはありません。また、mixin を使ってブレークポイント条件を名前付きで定義しておくと、@include sp { ... } のようにさらに簡潔に書けます。ネスト や 親セレクタ参照(&) も組み合わせることで、コンポーネント単位のスタイル管理がより効果的に行えます。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。