@media Nesting
In Sass, media queries (@media) can be nested inside selectors. In plain CSS, @media blocks must be written in a separate location in the file, but with Sass nesting, the breakpoints for a selector can be kept in the same block. After compilation, they are automatically expanded into standard @media blocks.
In plain CSS, the selector and media query end up separated like this.
/* Plain CSS: selector and media query are written in different locations */
.card {
font-size: 1rem;
padding: 24px;
}
@media (max-width: 768px) {
.card {
font-size: 0.9rem;
padding: 16px;
}
}
Syntax
// ========================================
// Basic syntax for nesting @media inside a selector
// ========================================
// Write @media inside a selector block
.selector {
property: value;
// Nest the media query here
@media (condition) {
property: value; // Applies to .selector
}
}
// Compiled CSS (reference)
// .selector { property: value; }
// @media (condition) { .selector { property: value; } }
// ========================================
// Explicit parent selector reference with &
// ========================================
// Using & makes the parent selector reference more explicit
.selector {
property: value;
@media (condition) {
& {
property: value;
}
}
}
Main @media nesting patterns
| Pattern | Example | Description |
|---|---|---|
| max-width (non-mobile-first) | @media (max-width: 768px) { ... } | Applies styles to screens at or below the specified width. |
| min-width (mobile-first) | @media (min-width: 768px) { ... } | Applies styles to screens at or above the specified width. |
| Range | @media (min-width: 768px) and (max-width: 1023px) { ... } | Applies styles only within a specific width range, such as tablets. |
@media print { ... } | Print-only styles can be written alongside the selector. | |
| Color scheme | @media (prefers-color-scheme: dark) { ... } | Dark mode styles can be grouped per component. |
| Combined with @mixin | @include sp { ... } | Wrap media queries in a mixin for shorter notation. |
Sample Code
Using a card component and navigation as examples, this demonstrates responsive styles with nested @media. Breakpoint definitions for each component are consolidated in one place, making changes easy to spot.
// ========================================
// Card component responsive layout
// ========================================
.card {
display: grid;
grid-template-columns: 200px 1fr; // Desktop: thumbnail + text 2-column
gap: 24px;
padding: 24px;
background-color: #ffffff;
border: 1px solid #dee2e6;
border-radius: 8px;
// Switch to 1 column at tablet width
@media (max-width: 1023px) {
grid-template-columns: 1fr;
gap: 16px;
padding: 20px;
}
// Further reduce padding at smartphone width
@media (max-width: 767px) {
padding: 16px;
gap: 12px;
}
// Card title
&__title {
font-size: 1.25rem;
font-weight: bold;
color: #2c3e50;
margin: 0 0 8px;
// Reduce font size at smartphone width
@media (max-width: 767px) {
font-size: 1.1rem;
}
}
// Card body text
&__body {
font-size: 1rem;
line-height: 1.7;
color: #495057;
@media (max-width: 767px) {
font-size: 0.9rem;
}
}
}
// ========================================
// Navigation responsive layout
// ========================================
.nav {
display: flex;
align-items: center;
gap: 8px;
padding: 0 32px;
background-color: #2c3e50;
height: 60px;
// Switch to vertical drawer menu at smartphone width
@media (max-width: 767px) {
display: none; // Toggle with JavaScript
&.is-open {
display: flex;
flex-direction: column;
align-items: stretch;
height: auto;
padding: 8px 0;
}
}
// Navigation link
&__link {
display: block;
padding: 8px 16px;
color: #ecf0f1;
text-decoration: none;
border-radius: 4px;
transition: background-color 0.2s;
&:hover {
background-color: #34495e;
}
// Increase padding for easier tapping at smartphone width
@media (max-width: 767px) {
padding: 12px 24px;
border-radius: 0;
border-bottom: 1px solid #34495e;
}
}
}
// ========================================
// Hero section: managing multiple breakpoints
// ========================================
.hero {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 480px;
padding: 64px 32px;
text-align: center;
background-color: #f0f4f8;
// Tablet width
@media (max-width: 1023px) {
min-height: 360px;
padding: 48px 24px;
}
// Smartphone width
@media (max-width: 767px) {
min-height: 280px;
padding: 40px 16px;
}
// Heading
&__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;
}
}
// Subtext
&__sub {
font-size: 1.1rem;
color: #636e72;
max-width: 600px;
@media (max-width: 767px) {
font-size: 0.95rem;
}
}
}
// ========================================
// Dark mode support (prefers-color-scheme)
// ========================================
.article {
background-color: #ffffff;
color: #2c3e50;
padding: 32px;
border-radius: 8px;
// Applies when the user's OS is set to dark mode
@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;
}
}
}
// ========================================
// Print styles nested inside selectors
// ========================================
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 32px;
background-color: #2c3e50;
color: #ffffff;
// Use white background when printing to avoid relying on background printing settings
@media print {
background-color: #ffffff;
color: #000000;
border-bottom: 1px solid #000000;
}
}
// Hide navigation when printing
.nav {
@media print {
display: none;
}
}
Common Mistakes
Nesting @media too deeply, resulting in many duplicate media queries in the compiled output
Sass @media nesting does not automatically merge identical media query conditions across multiple selectors. Writing the same breakpoint nested inside each component expands them into separate @media blocks in the compiled CSS.
ng_example.scss
// The same breakpoint is nested inside each of three components
.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;
}
}
/* Compiled output: @media (max-width: 767px) appears three times */
.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; }
}
This repetition is easier to manage by naming breakpoints with a mixin and keeping component nesting to a minimum. The impact on CSS file size is largely absorbed by gzip compression, but when duplication is heavy, grouping @media blocks at the end of the file is also an option.
Misunderstanding that nesting @media conditions produces OR logic instead of AND
When one @media block is nested inside another, Sass combines the two conditions with and. Writing @media (max-width: 1024px) inside @media (min-width: 768px) does not produce OR logic — it compiles to "min-width: 768px AND max-width: 1024px".
ng_example.scss
// Written under the assumption that the inner @media becomes an OR condition
.sidebar {
display: block;
@media (min-width: 768px) {
width: 240px;
// Intending to narrow down to tablet width only
@media (max-width: 1024px) {
width: 200px;
}
}
}
/* Compiled output: AND combination (may or may not be intended, but easy to misread) */
.sidebar { display: block; }
@media (min-width: 768px) {
.sidebar { width: 240px; }
}
@media (min-width: 768px) and (max-width: 1024px) {
.sidebar { width: 200px; }
}
If a range is the goal, write it as a single @media (min-width: 768px) and (max-width: 1024px) from the start — this makes the intent clearer. Nesting @media only narrows conditions further (AND); it never adds alternatives (OR).
Notes
Sass @media nesting allows breakpoint styles related to a selector to be written inside that selector's block. In plain CSS, @media blocks must be placed elsewhere in the file, making it necessary to search for where a component's responsive definitions are. With Sass nesting, all states of a component can be collected in one place, making changes, deletions, and moves straightforward. After compilation, the output is standard @media blocks, so there is no difference in the size or behavior of the resulting CSS. Using a mixin to define breakpoint conditions by name allows even shorter notation such as @include sp { ... }. Combining with nesting and parent selector reference (&) makes component-based style management more effective.
If you find any errors or copyright issues, please contact us.