name wp-responsive description Responsive design patterns — mobile-first breakpoints, fluid typography, responsive images, touch targets user-invocable false
Responsive Design Patterns
This skill defines the responsive design system used across all themes and demos. It uses mobile-first CSS with min-width media queries, fluid typography, and responsive image techniques.
Mobile-First Breakpoint System
All styles are written mobile-first. Base styles target the smallest screens, and min-width media queries progressively enhance for larger viewports.
Breakpoint Scale
Name Min-Width Target Devices Base (no query) All phones (320px+) Small 576pxLarge phones, small tablets Tablet 768pxTablets (portrait and landscape) Desktop 1024pxDesktops, laptops Large 1200pxLarge desktops Extra Large 1440pxUltra-wide screens
CSS Implementation
.services__grid {
display : grid;
grid-template-columns : 1 fr;
gap : var (--spacing-lg);
}
@media (min-width : 576px ) {
.services__grid {
grid-template-columns : repeat (2 , 1 fr);
}
}
@media (min-width : 768px ) {
.services__grid {
gap : var (--spacing-xl);
}
}
@media (min-width : 1024px ) {
.services__grid {
grid-template-columns : repeat (3 , 1 fr);
}
}
@media (min-width : 1200px ) {
.services__grid {
gap : var (--spacing-2 xl);
}
}
Rule: Always min-width, Never max-width
Use min-width queries exclusively. This enforces mobile-first thinking -- you start with the constrained layout and add complexity as space increases.
@media (min-width : 768px ) { ... }
@media (max-width : 767px ) { ... }
The only exception to the max-width rule is for the hamburger/mobile menu toggle (see Navigation section below), where max-width can be used to hide desktop nav on mobile. Even then, prefer showing/hiding with min-width when possible.
Container Max-Widths Per Breakpoint
The container stretches to fill the viewport on small screens and caps at the design system maximum on large screens.
.container {
width : 100% ;
max-width : var (--container-max);
margin-left : auto;
margin-right : auto;
padding-left : var (--spacing-md);
padding-right : var (--spacing-md);
}
@media (min-width : 768px ) {
.container {
padding-left : var (--spacing-xl);
padding-right : var (--spacing-xl);
}
}
@media (min-width : 1024px ) {
.container {
padding-left : var (--spacing-2 xl);
padding-right : var (--spacing-2 xl);
}
}
Responsive Navigation: Hamburger (Mobile) to Horizontal (Desktop)
HTML Structure
<header class ="header" >
<div class ="container" >
<div class ="header__inner" >
<a href ="/" class ="header__logo" >
<img src ="logo.svg" alt ="Site Name" >
</a >
<nav class ="header__nav" id ="main-nav" >
<a href ="#" class ="nav__link nav__link--active" > Home</a >
<a href ="#services" class ="nav__link" > Services</a >
<a href ="/pricing" class ="nav__link" > Pricing</a >
<a href ="#contact" class ="nav__link" > Contact</a >
</nav >
<a href ="#contact" class ="btn btn--primary header__cta" > Get Started</a >
<button class ="header__hamburger" aria-label ="Toggle menu" aria-expanded ="false" >
<span > </span >
<span > </span >
<span > </span >
</button >
</div >
</div >
<div class ="mobile-menu" id ="mobile-menu" aria-hidden ="true" >
<nav class ="mobile-menu__nav" >
<a href ="#" class ="mobile-menu__link" > Home</a >
<a href ="#services" class ="mobile-menu__link" > Services</a >
<a href ="/pricing" class ="mobile-menu__link" > Pricing</a >
<a href ="#contact" class ="mobile-menu__link" > Contact</a >
</nav >
<a href ="#contact" class ="btn btn--primary mobile-menu__cta" > Get Started</a >
</div >
</header >
CSS
.header__nav ,
.header__cta {
display : none;
}
.header__hamburger {
display : flex;
flex-direction : column;
justify-content : center;
gap : 5px ;
width : 44px ;
height : 44px ;
background : none;
border : none;
cursor : pointer;
padding : var (--spacing-sm);
}
.header__hamburger span {
display : block;
width : 24px ;
height : 2px ;
background : var (--color-text);
transition : var (--transition-base);
}
.mobile-menu {
position : fixed;
top : 0 ;
left : 0 ;
width : 100% ;
height : 100vh ;
background : var (--color-background);
display : flex;
flex-direction : column;
align-items : center;
justify-content : center;
gap : var (--spacing-xl);
transform : translateX (100% );
transition : transform 0.3s ease;
z-index : 999 ;
}
.mobile-menu .is-open {
transform : translateX (0 );
}
.mobile-menu__link {
font-size : var (--font-size-xl);
font-weight : var (--font-weight-medium);
padding : var (--spacing-md);
}
@media (min-width : 1024px ) {
.header__nav {
display : flex;
align-items : center;
gap : var (--spacing-lg);
}
.header__cta {
display : inline-flex;
}
.header__hamburger {
display : none;
}
.mobile-menu {
display : none;
}
}
JavaScript (Minimal)
const hamburger = document .querySelector ('.header__hamburger' );
const mobileMenu = document .getElementById ('mobile-menu' );
if (hamburger && mobileMenu) {
hamburger.addEventListener ('click' , () => {
const isOpen = mobileMenu.classList .toggle ('is-open' );
hamburger.setAttribute ('aria-expanded' , isOpen);
mobileMenu.setAttribute ('aria-hidden' , !isOpen);
document .body .style .overflow = isOpen ? 'hidden' : '' ;
});
}
CSS Grid and Flexbox Stacking
Grid: Columns on Desktop, Stacked on Mobile
.features__grid {
display : grid;
grid-template-columns : 1 fr;
gap : var (--spacing-lg);
}
@media (min-width : 768px ) {
.features__grid {
grid-template-columns : repeat (2 , 1 fr);
gap : var (--spacing-xl);
}
}
@media (min-width : 1024px ) {
.features__grid {
grid-template-columns : repeat (3 , 1 fr);
}
}
@media (min-width : 1200px ) {
.features__grid {
grid-template-columns : repeat (4 , 1 fr);
}
}
Flexbox: Row on Desktop, Column on Mobile
.hero__inner {
display : flex;
flex-direction : column;
gap : var (--spacing-xl);
}
@media (min-width : 1024px ) {
.hero__inner {
flex-direction : row;
align-items : center;
}
.hero__content {
flex : 1 ;
}
.hero__image {
flex : 1 ;
}
}
Reversing Order on Mobile
.hero__inner {
display : flex;
flex-direction : column-reverse;
gap : var (--spacing-xl);
}
@media (min-width : 1024px ) {
.hero__inner {
flex-direction : row;
}
}
Fluid Typography with clamp()
Use clamp() for headings and large text to smoothly scale between viewport sizes without media query jumps.
Syntax
font-size : clamp (<minimum>, <preferred>, <maximum>);
Examples
.hero__title {
font-size : clamp (2rem , 5vw , 4rem );
}
.section__title {
font-size : clamp (1.5rem , 3vw , 2.5rem );
}
.hero__subtitle {
font-size : clamp (1rem , 2.5vw , 1.25rem );
}
.display__heading {
font-size : clamp (2.5rem , 6vw , 5rem );
}
Guidelines
Use clamp() for h1 through h3 and hero/display text
Body text and small text usually do not need clamp() -- a fixed rem value works fine
The vw unit in the preferred value controls how aggressively the text scales
Common preferred values: 2vw (gentle), 3vw (moderate), 5vw (aggressive)
Responsive Images
srcset and sizes Attributes
Provide multiple image resolutions so the browser picks the best one for the viewport and pixel density.
<img
src ="image-800.jpg"
srcset ="image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w"
sizes ="(min-width: 1024px) 50vw,
(min-width: 768px) 75vw,
100vw"
alt ="Descriptive alt text"
loading ="lazy"
>
The <picture> Element
Use <picture> for art direction -- serving different crops or images per viewport.
<picture >
<source
media ="(min-width: 1024px)"
srcset ="hero-desktop.jpg"
>
<source
media ="(min-width: 768px)"
srcset ="hero-tablet.jpg"
>
<img
src ="hero-mobile.jpg"
alt ="Hero image description"
loading ="lazy"
>
</picture >
WordPress wp_get_attachment_image()
In WordPress templates, use the built-in function to output responsive images automatically.
<?php
$image_id = prefix_get_field ('hero_image' );
if ($image_id ) :
echo wp_get_attachment_image ($image_id , 'large' , false , array (
'class' => 'hero__image' ,
'loading' => 'lazy' ,
'sizes' => '(min-width: 1024px) 50vw, 100vw' ,
));
endif ;
?>
If you have an image array (from ACF/SCF) instead of just the ID:
<?php
$image = prefix_get_field ('hero_image' );
if ($image ) :
?>
<img
src="<?php echo esc_url($image ['url']); ?>"
srcset="<?php echo esc_attr($image ['sizes']['medium'] . ' 300w, ' . $image ['sizes']['large'] . ' 1024w, ' . $image ['url'] . ' ' . $image ['width'] . 'w'); ?>"
sizes="(min-width: 1024px) 50vw, 100vw"
alt="<?php echo esc_attr($image ['alt']); ?>"
width="<?php echo esc_attr($image ['width']); ?>"
height="<?php echo esc_attr($image ['height']); ?>"
loading="lazy"
>
<?php endif ; ?>
Lazy Loading
Add loading="lazy" to all images below the fold. Do NOT add it to the hero/LCP image (which should be preloaded instead).
<img src ="hero.jpg" alt ="Hero" fetchpriority ="high" >
<img src ="service.jpg" alt ="Service" loading ="lazy" >
Touch-Friendly Targets
All interactive elements (links, buttons, form inputs) MUST have a minimum tap area of 44x44 pixels on touch devices.
Buttons
.btn {
min-height : 44px ;
min-width : 44px ;
padding : var (--spacing-sm) var (--spacing-lg);
}
Navigation Links
.nav__link {
display : inline-flex;
align-items : center;
min-height : 44px ;
padding : var (--spacing-sm) var (--spacing-md);
}
.mobile-menu__link {
display : block;
padding : var (--spacing-md) var (--spacing-lg);
min-height : 44px ;
}
Form Inputs
input [type="text" ] ,
input [type="email" ] ,
input [type="tel" ] ,
textarea ,
select {
min-height : 44px ;
padding : var (--spacing-sm) var (--spacing-md);
font-size : var (--font-size-base);
}
Icon Buttons
For small icon buttons (close, hamburger, social links), ensure the clickable area is at least 44px even if the visible icon is smaller.
.icon-button {
display : inline-flex;
align-items : center;
justify-content : center;
width : 44px ;
height : 44px ;
padding : var (--spacing-sm);
}
Every Section MUST Have Responsive Styles
When building any section, you MUST define how it looks at each major breakpoint. No section should rely on desktop-only styles.
Pattern for Every Section
.values {
padding : var (--spacing-2 xl) 0 ;
}
.values__grid {
display : grid;
grid-template-columns : 1 fr;
gap : var (--spacing-lg);
}
.values__title {
font-size : clamp (1.5rem , 3vw , 2.5rem );
text-align : center;
margin-bottom : var (--spacing-xl);
}
@media (min-width : 768px ) {
.values__grid {
grid-template-columns : repeat (2 , 1 fr);
gap : var (--spacing-xl);
}
}
@media (min-width : 1024px ) {
.values {
padding : var (--spacing-3 xl) 0 ;
}
.values__grid {
grid-template-columns : repeat (3 , 1 fr);
}
}
No Horizontal Scroll
The page MUST NOT scroll horizontally at any viewport width. This is tested starting at 320px minimum .
Common Causes and Fixes
img {
max-width : 100% ;
height : auto;
}
.container {
width : 100% ;
overflow-x : hidden;
}
.content {
overflow-wrap : break-word;
word-wrap : break-word;
}
pre, code {
overflow-x : auto;
max-width : 100% ;
}
.table-wrapper {
overflow-x : auto;
-webkit-overflow -scrolling: touch;
}
Testing Rule
Before finalizing, verify no horizontal scroll exists at these widths:
320px (oldest small phones)
375px (iPhone SE / standard)
576px (large phones)
768px (tablets)
1024px (desktops)
1440px (large desktops)
Reduced Motion
Respect the user's preference to reduce animations and motion.
.card {
transition : transform 0.3s ease, box-shadow 0.3s ease;
}
.card :hover {
transform : translateY (-4px );
box-shadow : var (--shadow-lg);
}
@media (prefers-reduced-motion : reduce) {
*,
*::before ,
*::after {
animation-duration : 0.01ms !important ;
animation-iteration-count : 1 !important ;
transition-duration : 0.01ms !important ;
scroll-behavior : auto !important ;
}
}
This MUST be included in every stylesheet that uses animations or transitions.
Responsive Section Spacing
Section vertical padding should scale with the viewport.
.section {
padding : var (--spacing-2 xl) 0 ;
}
@media (min-width : 768px ) {
.section {
padding : var (--spacing-3 xl) 0 ;
}
}
@media (min-width : 1200px ) {
.section {
padding : calc (var (--spacing-3 xl) * 1.5 ) 0 ;
}
}
Responsive Typography Scale
While clamp() handles most heading sizes, ensure consistent scaling across the page.
h1 { font-size : clamp (2rem , 5vw , 3.5rem ); }
h2 { font-size : clamp (1.5rem , 3.5vw , 2.5rem ); }
h3 { font-size : clamp (1.25rem , 2.5vw , 1.75rem ); }
h4 { font-size : var (--font-size-lg); }
p {
font-size : var (--font-size-base);
line-height : var (--line-height-normal);
}
@media (min-width : 768px ) {
p {
font-size : var (--font-size-md);
}
}
Responsive Footer
Footers typically use a multi-column grid on desktop and stack on mobile.
.footer__grid {
display : grid;
grid-template-columns : 1 fr;
gap : var (--spacing-xl);
}
@media (min-width : 768px ) {
.footer__grid {
grid-template-columns : repeat (2 , 1 fr);
}
}
@media (min-width : 1024px ) {
.footer__grid {
grid-template-columns : 2 fr 1 fr 1 fr 1 fr;
}
}
.footer__bottom {
display : flex;
flex-direction : column;
align-items : center;
gap : var (--spacing-sm);
text-align : center;
padding-top : var (--spacing-xl);
border-top : 1px solid var (--color-border);
margin-top : var (--spacing-xl);
}
@media (min-width : 768px ) {
.footer__bottom {
flex-direction : row;
justify-content : space-between;
text-align : left;
}
}
Testing Checklist
Before marking any page or section as complete, verify responsiveness at these viewport widths:
Width Device Class Check 375px Mobile (iPhone SE) Layout stacks, text readable, touch targets 44px+ 576px Large phone Grid may shift to 2 columns 768px Tablet 2-column layouts, larger padding 1024px Desktop Full navigation visible, 3+ column grids 1440px Large desktop Max container width respected, generous whitespace
What to Verify at Each Breakpoint
Summary Checklist
Mobile-first CSS with min-width media queries only
Breakpoints: 576px, 768px, 1024px, 1200px, 1440px
Container with responsive padding and max-width
Hamburger menu on mobile, horizontal nav on desktop with ARIA attributes
CSS Grid/Flexbox with mobile stacking
clamp() used for headings and display text
Responsive images with srcset, sizes, and loading="lazy"
WordPress wp_get_attachment_image() used in templates
All touch targets minimum 44x44px
Every section has styles for all breakpoints
No horizontal scroll at any width (tested at 320px+)
prefers-reduced-motion media query included
Tested at: 375px, 576px, 768px, 1024px, 1440px