CSS Media Query Recipes (Retina, DPI, Color Depth, Aspect Ratio)
Media queries do far more than `min-width`. This snippet collects the recipes you reach for in real projects: viewport breakpoints, high-DPI / retina screens, color-depth and color-gamut gating, user preference queries (`prefers-color-scheme`, `prefers-reduced-motion`), and aspect-ratio plus orientation for video and game UIs. Drop the ones you do not need; keep the rest as a starting point.
874 views
13
/* Base styles target the smallest screens (phones in portrait). */
.container {
padding: 1rem;
max-width: 100%;
}
/* Tablet and up: 640px is a common min-width breakpoint for two-column layouts. */
@media (min-width: 640px) {
.container {
padding: 1.5rem;
max-width: 600px;
margin-inline: auto;
}
}
/* Small laptop and up: bump to wider container, tighter line-height. */
@media (min-width: 1024px) {
.container {
max-width: 960px;
}
body {
line-height: 1.6;
}
}
/* Large desktop. */
@media (min-width: 1440px) {
.container {
max-width: 1200px;
}
}
/* Avoid mixing min-width and max-width in the same stylesheet; pick one
direction (mobile-first = min-width is the modern default) and stick to it. */Mobile-first means your base CSS is the smallest screen and each @media (min-width: ...) block layers on richer styles for larger viewports. This avoids the override-fest that happens when you write desktop styles first and then max-width queries to undo them. Pick three or four breakpoints and stick with them; common starting points are 640px (small tablet), 1024px (small laptop), and 1440px (large desktop). Use rem and % for sizing inside the queries so a user changing the browser zoom or root font size still gets a comfortable layout.
/* Default: ship a 1x background image. */
.logo {
background-image: url('logo.png');
background-size: 100px 40px;
}
/* Modern, vendor-neutral: min-resolution in dppx. 2dppx covers most retina-class
displays (iPhone, modern Macs, high-DPI Android phones). */
@media (min-resolution: 2dppx) {
.logo {
background-image: url('[email protected]');
}
}
/* Print-quality 300 DPI: useful for icons rendered for print stylesheets. */
@media (min-resolution: 300dpi) {
.badge {
background-image: url('badge-print.png');
}
}
/* Legacy WebKit fallback. Keep only if you must support Safari < 16 / very old
iOS. Modern Safari handles min-resolution: 2dppx, so this block is optional. */
@media (-webkit-min-device-pixel-ratio: 2) {
.logo {
background-image: url('[email protected]');
}
}
/* For most teams, a single 2dppx query plus a 1x default is enough; do not
ship 3x assets unless you have measured the bandwidth cost. */dppx is dots per pixel: 1dppx is 96 device-pixels per CSS-pixel (a regular monitor), 2dppx is the retina-class threshold, 3dppx is the very-high-density tier on flagship phones. The modern recipe is a single min-resolution: 2dppx block that swaps in a higher-resolution asset; for print, use 300dpi. The legacy -webkit-min-device-pixel-ratio query is still recognized by older WebKit but is no longer required for current browsers. Prefer <picture> and srcset for responsive <img> tags; CSS background-image swapping is best reserved for purely decorative assets.
/* color-gamut targets wider color spaces for displays that support them. */
@media (color-gamut: p3) {
.accent {
/* Display-P3 covers ~25% more colors than sRGB. Modern Safari, Chrome
on macOS, and recent iPhones support it. */
color: color(display-p3 1 0 0.4);
}
}
/* color: N gates by bits-per-component. (color: 8) means at least 8 bits per
channel, i.e. true-color. Rarely needed today; almost every consumer device
meets the minimum. Listed here for completeness. */
@media (color: 8) {
.photo {
filter: contrast(1.05);
}
}
/* User preference: dark mode follows the OS-level setting. */
@media (prefers-color-scheme: dark) {
:root {
--bg: #111;
--fg: #eee;
}
}
@media (prefers-color-scheme: light) {
:root {
--bg: #fff;
--fg: #111;
}
}
/* User preference: reduced motion. Always provide a no-animation fallback. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
/* Other handy preferences:
- prefers-contrast: more / less / no-preference
- prefers-reduced-data: reduce (save data on metered connections)
- forced-colors: active (Windows High Contrast mode) */color-gamut and color both describe display capabilities; in practice, only color-gamut: p3 matters for new work because it lets you opt into wider color spaces with a clean fallback. Far more important are the user-preference queries: prefers-color-scheme is how dark mode should be wired (CSS variables flipping inside the media query is the cleanest pattern), and prefers-reduced-motion is an accessibility requirement, not a nicety. The (color: 8) recipe maps to the legacy "4 bits per color component" and "256-color device" use cases; modern devices easily clear that bar, so treat it as historical. Always design for prefers-reduced-motion: reduce first and add motion as enhancement, not the other way around.
/* Match a specific aspect ratio (16:9 widescreen video, 4:3 classic, 1:1 square). */
@media (aspect-ratio: 16/9) {
.player {
max-height: 100vh;
}
}
/* Range queries: at least square, at most ultrawide. */
@media (min-aspect-ratio: 1/1) {
.gallery {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-aspect-ratio: 21/9) {
.gallery {
gap: 1rem;
}
}
/* Orientation: landscape vs portrait. Useful for mobile games and media apps. */
@media (orientation: landscape) {
.stage {
flex-direction: row;
}
}
@media (orientation: portrait) {
.stage {
flex-direction: column;
}
}
/* Combine queries with `and`. Logical OR uses a comma-separated list. */
@media (orientation: landscape) and (min-width: 768px) {
.toolbar {
position: fixed;
right: 1rem;
top: 50%;
}
}
@media (max-width: 480px), (orientation: portrait) {
/* Either condition triggers this block. */
.sidebar {
display: none;
}
}aspect-ratio accepts a fraction (16/9, 4/3, 1/1) and the min- and max- variants give you ranges. orientation: landscape means width is greater than height, regardless of how the device is held; do not assume landscape means desktop. The comma between media queries is logical OR; the and keyword is logical AND. Combine viewport-width with orientation when you want a specific phone-in-landscape layout that should not affect tablets. Keep the rule list short; if a media query has more than two conditions, consider whether a CSS custom property + a different breakpoint would be cleaner.
