
Responsive Design
- 16k installs
- 38.5k repo stars
- Updated July 22, 2026
- wshobson/agents
Responsive Design is part of a plugin marketplace containing 92 reusable agentic workflow building blocks and 162 skills distributed across five agent harnesses.
About
Responsive Design is part of the wshobson/agents agentic plugin marketplace providing 92 plugins and 162 skills across development domains. The marketplace offers domain-expert agents, modular skills with progressive disclosure, and commands for common workflows. Developers use it to load specialized capabilities on-demand for architecture, testing, security, infrastructure, and domain-specific coding tasks.
- 92 production-ready plugins with single-source marketplace for 5 harnesses
- Multi-harness support: Claude Code, Codex CLI, Cursor, OpenCode, Gemini CLI, Copilot
- 162 skills organized by domain (architecture, security, data, ML, backend, frontend)
Responsive Design by the numbers
- 16,027 all-time installs (skills.sh)
- +435 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #44 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
responsive-design capabilities & compatibility
- Capabilities
- design patterns · plugin architecture
- Use cases
- frontend
npx skills add https://github.com/wshobson/agents --skill responsive-designAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 16k |
|---|---|
| repo stars | ★ 38.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 22, 2026 |
| Repository | wshobson/agents ↗ |
How do you write mobile-first responsive CSS breakpoints?
Responsive Design is part of the wshobson/agents agentic plugin marketplace providing 92 plugins and 162 skills across development domains. The marketplace offers domain-expert agents, modular skills
Who is it for?
Loading domain-specific expertise on-demand, scaling agent capabilities beyond single harness, code generation and testing
Skip if: Simple chat or stateless interactions
When should I use this skill?
CSS layouts need mobile-first breakpoints, fluid spacing, or progressive enhancement instead of device-catalog media queries.
What you get
Mobile-first CSS rules, content-driven min-width breakpoints, and progressive enhancement layout code.
- mobile-first CSS rules
- breakpoint media queries
- progressive enhancement layout
By the numbers
- Example breakpoints at 640px and 1024px min-width in sample CSS
Files
Responsive Design
Master modern responsive design techniques to create interfaces that adapt seamlessly across all screen sizes and device contexts.
When to Use This Skill
- Implementing mobile-first responsive layouts
- Using container queries for component-based responsiveness
- Creating fluid typography and spacing scales
- Building complex layouts with CSS Grid and Flexbox
- Designing breakpoint strategies for design systems
- Implementing responsive images and media
- Creating adaptive navigation patterns
- Building responsive tables and data displays
Detailed patterns and worked examples
Detailed pattern documentation lives in references/details.md. Read that file when the navigation tier above is insufficient.
Best Practices
1. Mobile-First: Start with mobile styles, enhance for larger screens 2. Content Breakpoints: Set breakpoints based on content, not devices 3. Fluid Over Fixed: Use fluid values for typography and spacing 4. Container Queries: Use for component-level responsiveness 5. Test Real Devices: Simulators don't catch all issues 6. Performance: Optimize images, lazy load off-screen content 7. Touch Targets: Maintain 44x44px minimum on mobile 8. Logical Properties: Use inline/block for internationalization
Common Issues
- Horizontal Overflow: Content breaking out of viewport
- Fixed Widths: Using px instead of relative units
- Viewport Height: 100vh issues on mobile browsers
- Font Size: Text too small on mobile
- Touch Targets: Buttons too small to tap accurately
- Aspect Ratio: Images squishing or stretching
- Z-Index Stacking: Overlays breaking on different screens
Breakpoint Strategies
Overview
Effective breakpoint strategies focus on content needs rather than device sizes. Modern responsive design uses fewer, content-driven breakpoints combined with fluid techniques.
Mobile-First Approach
Core Philosophy
Start with the smallest screen, then progressively enhance for larger screens.
/* Base styles (mobile first) */
.component {
display: flex;
flex-direction: column;
padding: 1rem;
}
/* Enhance for larger screens */
@media (min-width: 640px) {
.component {
flex-direction: row;
padding: 1.5rem;
}
}
@media (min-width: 1024px) {
.component {
padding: 2rem;
}
}Benefits
1. Performance: Mobile devices load only necessary CSS 2. Progressive Enhancement: Features add rather than subtract 3. Content Priority: Forces focus on essential content first 4. Simplicity: Easier to reason about cascading styles
Common Breakpoint Scales
Tailwind CSS Default
/* Tailwind breakpoints */
/* sm: 640px - Landscape phones */
/* md: 768px - Tablets */
/* lg: 1024px - Laptops */
/* xl: 1280px - Desktops */
/* 2xl: 1536px - Large desktops */
@media (min-width: 640px) {
/* sm */
}
@media (min-width: 768px) {
/* md */
}
@media (min-width: 1024px) {
/* lg */
}
@media (min-width: 1280px) {
/* xl */
}
@media (min-width: 1536px) {
/* 2xl */
}Bootstrap 5
/* Bootstrap breakpoints */
/* sm: 576px */
/* md: 768px */
/* lg: 992px */
/* xl: 1200px */
/* xxl: 1400px */
@media (min-width: 576px) {
/* sm */
}
@media (min-width: 768px) {
/* md */
}
@media (min-width: 992px) {
/* lg */
}
@media (min-width: 1200px) {
/* xl */
}
@media (min-width: 1400px) {
/* xxl */
}Minimalist Scale
/* Simplified 3-breakpoint system */
/* Base: Mobile (< 600px) */
/* Medium: Tablets and small laptops (600px - 1024px) */
/* Large: Desktops (> 1024px) */
:root {
--bp-md: 600px;
--bp-lg: 1024px;
}
@media (min-width: 600px) {
/* Medium */
}
@media (min-width: 1024px) {
/* Large */
}Content-Based Breakpoints
Finding Natural Breakpoints
Instead of using device-based breakpoints, identify where your content naturally needs to change.
/* Bad: Device-based thinking */
@media (min-width: 768px) {
/* iPad breakpoint */
}
/* Good: Content-based thinking */
/* Breakpoint where sidebar fits comfortably next to content */
@media (min-width: 50rem) {
.layout {
display: grid;
grid-template-columns: 1fr 300px;
}
}
/* Breakpoint where cards can show 3 across without crowding */
@media (min-width: 65rem) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}Testing Content Breakpoints
// Find where content breaks
function findBreakpoints(selector) {
const element = document.querySelector(selector);
const breakpoints = [];
for (let width = 320; width <= 1920; width += 10) {
element.style.width = `${width}px`;
// Check for overflow, wrapping, or layout issues
if (element.scrollWidth > element.clientWidth) {
breakpoints.push({ width, issue: "overflow" });
}
}
return breakpoints;
}Design Token Integration
Breakpoint Tokens
:root {
/* Breakpoint values */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
/* Container widths for each breakpoint */
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1280px;
--container-2xl: 1536px;
}
.container {
width: 100%;
max-width: var(--container-lg);
margin-inline: auto;
padding-inline: var(--space-4);
}JavaScript Integration
// Breakpoint constants
export const breakpoints = {
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
"2xl": 1536,
} as const;
// Media query hook
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
setMatches(media.matches);
const listener = () => setMatches(media.matches);
media.addEventListener("change", listener);
return () => media.removeEventListener("change", listener);
}, [query]);
return matches;
}
// Breakpoint hook
function useBreakpoint() {
const isSmall = useMediaQuery(`(min-width: ${breakpoints.sm}px)`);
const isMedium = useMediaQuery(`(min-width: ${breakpoints.md}px)`);
const isLarge = useMediaQuery(`(min-width: ${breakpoints.lg}px)`);
const isXLarge = useMediaQuery(`(min-width: ${breakpoints.xl}px)`);
return {
isMobile: !isSmall,
isTablet: isSmall && !isLarge,
isDesktop: isLarge,
current: isXLarge
? "xl"
: isLarge
? "lg"
: isMedium
? "md"
: isSmall
? "sm"
: "base",
};
}Feature Queries
@supports for Progressive Enhancement
/* Feature detection instead of browser detection */
@supports (display: grid) {
.layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
}
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
@supports (aspect-ratio: 16/9) {
.video-container {
aspect-ratio: 16/9;
}
}
/* Fallback for older browsers */
@supports not (gap: 1rem) {
.flex-container > * + * {
margin-left: 1rem;
}
}Combining Feature and Size Queries
/* Only apply grid layout if supported and screen is large enough */
@supports (display: grid) {
@media (min-width: 768px) {
.layout {
display: grid;
grid-template-columns: 250px 1fr;
}
}
}Responsive Patterns by Component
Navigation
.nav {
/* Mobile: vertical stack */
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.nav {
/* Tablet+: horizontal */
flex-direction: row;
align-items: center;
}
}
/* Or with container queries */
.nav-container {
container-type: inline-size;
}
@container (min-width: 600px) {
.nav {
flex-direction: row;
}
}Cards Grid
.cards {
display: grid;
gap: 1.5rem;
grid-template-columns: 1fr;
}
@media (min-width: 640px) {
.cards {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.cards {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 1280px) {
.cards {
grid-template-columns: repeat(4, 1fr);
}
}
/* Better: auto-fit with minimum size */
.cards-auto {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}Hero Section
.hero {
min-height: 50vh;
padding: var(--space-lg) var(--space-md);
text-align: center;
}
.hero-title {
font-size: clamp(2rem, 5vw + 1rem, 4rem);
}
.hero-subtitle {
font-size: clamp(1rem, 2vw + 0.5rem, 1.5rem);
}
@media (min-width: 768px) {
.hero {
min-height: 70vh;
display: flex;
align-items: center;
text-align: left;
}
.hero-content {
max-width: 60%;
}
}
@media (min-width: 1024px) {
.hero {
min-height: 80vh;
}
.hero-content {
max-width: 50%;
}
}Tables
/* Mobile: cards or horizontal scroll */
.table-container {
overflow-x: auto;
}
.responsive-table {
min-width: 600px;
}
/* Alternative: transform to cards on mobile */
@media (max-width: 639px) {
.responsive-table {
min-width: 0;
}
.responsive-table thead {
display: none;
}
.responsive-table tr {
display: block;
margin-bottom: 1rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1rem;
}
.responsive-table td {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border: none;
}
.responsive-table td::before {
content: attr(data-label);
font-weight: 600;
}
}Print Styles
@media print {
/* Remove non-essential elements */
.nav,
.sidebar,
.footer,
.ads {
display: none;
}
/* Reset colors and backgrounds */
* {
background: white !important;
color: black !important;
box-shadow: none !important;
}
/* Ensure content fits on page */
.container {
max-width: 100%;
padding: 0;
}
/* Handle page breaks */
h1,
h2,
h3 {
page-break-after: avoid;
}
img,
table {
page-break-inside: avoid;
}
/* Show URLs for links */
a[href^="http"]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
}
}Preference Queries
/* Dark mode preference */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #f0f0f0;
}
}
/* Reduced motion preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* High contrast preference */
@media (prefers-contrast: high) {
:root {
--text: #000;
--bg: #fff;
--border: #000;
}
.button {
border: 2px solid currentColor;
}
}
/* Reduced data preference */
@media (prefers-reduced-data: reduce) {
.hero-video {
display: none;
}
.hero-image {
display: block;
}
}Testing Breakpoints
// Automated breakpoint testing
async function testBreakpoints(page, breakpoints) {
const results = [];
for (const [name, width] of Object.entries(breakpoints)) {
await page.setViewportSize({ width, height: 800 });
// Check for horizontal overflow
const hasOverflow = await page.evaluate(() => {
return (
document.documentElement.scrollWidth >
document.documentElement.clientWidth
);
});
// Check for elements going off-screen
const offscreenElements = await page.evaluate(() => {
const elements = document.querySelectorAll("*");
return Array.from(elements).filter((el) => {
const rect = el.getBoundingClientRect();
return rect.right > window.innerWidth || rect.left < 0;
}).length;
});
results.push({
breakpoint: name,
width,
hasOverflow,
offscreenElements,
});
}
return results;
}Resources
Container Queries Deep Dive
Overview
Container queries enable component-based responsive design by allowing elements to respond to their container's size rather than the viewport. This paradigm shift makes truly reusable components possible.
Browser Support
Container queries have excellent modern browser support (Chrome 105+, Firefox 110+, Safari 16+). For older browsers, provide graceful fallbacks.
Containment Basics
Container Types
/* Size containment - queries based on inline and block size */
.container {
container-type: size;
}
/* Inline-size containment - queries based on inline (width) size only */
/* Most common and recommended */
.container {
container-type: inline-size;
}
/* Normal - style queries only, no size queries */
.container {
container-type: normal;
}Named Containers
/* Named container for targeted queries */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Shorthand */
.card-wrapper {
container: card / inline-size;
}
/* Query specific container */
@container card (min-width: 400px) {
.card-content {
display: flex;
}
}Container Query Syntax
Width-Based Queries
.container {
container-type: inline-size;
}
/* Minimum width */
@container (min-width: 300px) {
.element {
/* styles */
}
}
/* Maximum width */
@container (max-width: 500px) {
.element {
/* styles */
}
}
/* Range syntax */
@container (300px <= width <= 600px) {
.element {
/* styles */
}
}
/* Exact width */
@container (width: 400px) {
.element {
/* styles */
}
}Combining Conditions
/* AND condition */
@container (min-width: 400px) and (max-width: 800px) {
.element {
/* styles */
}
}
/* OR condition */
@container (max-width: 300px) or (min-width: 800px) {
.element {
/* styles */
}
}
/* NOT condition */
@container not (min-width: 400px) {
.element {
/* styles */
}
}Named Container Queries
/* Multiple named containers */
.page-wrapper {
container: page / inline-size;
}
.sidebar-wrapper {
container: sidebar / inline-size;
}
/* Target specific containers */
@container page (min-width: 1024px) {
.main-content {
max-width: 800px;
}
}
@container sidebar (min-width: 300px) {
.sidebar-widget {
display: grid;
grid-template-columns: 1fr 1fr;
}
}Container Query Units
/* Container query length units */
.element {
/* Container query width - 1cqw = 1% of container width */
width: 50cqw;
/* Container query height - 1cqh = 1% of container height */
height: 50cqh;
/* Container query inline - 1cqi = 1% of container inline size */
padding-inline: 5cqi;
/* Container query block - 1cqb = 1% of container block size */
padding-block: 3cqb;
/* Container query min - smaller of cqi and cqb */
font-size: 5cqmin;
/* Container query max - larger of cqi and cqb */
margin: 2cqmax;
}
/* Practical example: fluid typography based on container */
.card-title {
font-size: clamp(1rem, 4cqi, 2rem);
}
.card-body {
padding: clamp(0.75rem, 4cqi, 1.5rem);
}Style Queries
Style queries allow querying CSS custom property values. Currently limited support.
/* Define a custom property */
.card {
--layout: stack;
}
/* Query the property value */
@container style(--layout: stack) {
.card-content {
display: flex;
flex-direction: column;
}
}
@container style(--layout: inline) {
.card-content {
display: flex;
flex-direction: row;
}
}
/* Toggle layout via custom property */
.card.horizontal {
--layout: inline;
}Practical Patterns
Responsive Card Component
.card-container {
container: card / inline-size;
}
.card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: clamp(1rem, 4cqi, 2rem);
}
.card-image {
aspect-ratio: 16/9;
width: 100%;
object-fit: cover;
border-radius: 0.5rem;
}
.card-title {
font-size: clamp(1rem, 4cqi, 1.5rem);
font-weight: 600;
}
/* Medium container: side-by-side layout */
@container card (min-width: 400px) {
.card {
flex-direction: row;
align-items: flex-start;
}
.card-image {
width: 40%;
aspect-ratio: 1;
}
.card-content {
flex: 1;
}
}
/* Large container: enhanced layout */
@container card (min-width: 600px) {
.card-image {
width: 250px;
}
.card-title {
font-size: 1.5rem;
}
.card-actions {
display: flex;
gap: 0.5rem;
}
}Responsive Grid Items
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.grid-item {
container-type: inline-size;
}
.item-content {
padding: 1rem;
}
/* Item adapts to its own size, not the viewport */
@container (min-width: 350px) {
.item-content {
padding: 1.5rem;
}
.item-title {
font-size: 1.25rem;
}
}
@container (min-width: 500px) {
.item-content {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
}
}Dashboard Widget
.widget-container {
container: widget / inline-size;
}
.widget {
--chart-height: 150px;
padding: 1rem;
}
.widget-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.widget-chart {
height: var(--chart-height);
}
.widget-stats {
display: grid;
grid-template-columns: 1fr;
gap: 0.5rem;
}
@container widget (min-width: 300px) {
.widget {
--chart-height: 200px;
}
.widget-stats {
grid-template-columns: repeat(2, 1fr);
}
}
@container widget (min-width: 500px) {
.widget {
--chart-height: 250px;
padding: 1.5rem;
}
.widget-stats {
grid-template-columns: repeat(4, 1fr);
}
.widget-actions {
display: flex;
gap: 0.5rem;
}
}Navigation Component
.nav-container {
container: nav / inline-size;
}
.nav {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.nav-link {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
}
.nav-link-text {
display: none;
}
.nav-link-icon {
width: 1.5rem;
height: 1.5rem;
}
/* Show text when container is wide enough */
@container nav (min-width: 200px) {
.nav-link-text {
display: block;
}
}
/* Horizontal layout for wider containers */
@container nav (min-width: 600px) {
.nav {
flex-direction: row;
}
.nav-link {
padding: 0.5rem 1rem;
}
}Tailwind CSS Integration
// Tailwind v3.2+ supports container queries
// tailwind.config.js
module.exports = {
plugins: [require("@tailwindcss/container-queries")],
};
// Component usage
function Card({ title, image, description }) {
return (
// @container creates containment context
<div className="@container">
<article className="flex flex-col @md:flex-row @md:gap-4">
<img
src={image}
alt=""
className="w-full @md:w-48 @lg:w-64 aspect-video @md:aspect-square object-cover rounded-lg"
/>
<div className="p-4 @md:p-0">
<h2 className="text-lg @md:text-xl @lg:text-2xl font-semibold">
{title}
</h2>
<p className="mt-2 text-muted-foreground @lg:text-lg">
{description}
</p>
</div>
</article>
</div>
);
}
// Named containers
function Dashboard() {
return (
<div className="@container/main">
<aside className="@container/sidebar">
<nav className="flex flex-col @lg/sidebar:flex-row">{/* ... */}</nav>
</aside>
<main className="@lg/main:grid @lg/main:grid-cols-2">{/* ... */}</main>
</div>
);
}Fallback Strategies
/* Provide fallbacks for browsers without support */
.card {
/* Default (fallback) styles */
display: flex;
flex-direction: column;
}
/* Feature query for container support */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
/* Alternative: media query fallback */
.card {
display: flex;
flex-direction: column;
}
/* Viewport-based fallback */
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
/* Enhanced with container queries when supported */
@supports (container-type: inline-size) {
@media (min-width: 768px) {
.card {
flex-direction: column; /* Reset */
}
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}Performance Considerations
/* Avoid over-nesting containers */
/* Bad: Too many nested containers */
.level-1 {
container-type: inline-size;
}
.level-2 {
container-type: inline-size;
}
.level-3 {
container-type: inline-size;
}
.level-4 {
container-type: inline-size;
}
/* Good: Strategic container placement */
.component-wrapper {
container-type: inline-size;
}
/* Use inline-size instead of size when possible */
/* size containment is more expensive */
.container {
container-type: inline-size; /* Preferred */
/* container-type: size; */ /* Only when needed */
}Testing Container Queries
// Test container query support
const supportsContainerQueries = CSS.supports("container-type", "inline-size");
// Resize observer for testing
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
console.log("Container width:", entry.contentRect.width);
}
});
observer.observe(document.querySelector(".container"));Resources
responsive-design — detailed patterns and worked examples
Core Capabilities
1. Container Queries
- Component-level responsiveness independent of viewport
- Container query units (cqi, cqw, cqh)
- Style queries for conditional styling
- Fallbacks for browser support
2. Fluid Typography & Spacing
- CSS clamp() for fluid scaling
- Viewport-relative units (vw, vh, dvh)
- Fluid type scales with min/max bounds
- Responsive spacing systems
3. Layout Patterns
- CSS Grid for 2D layouts
- Flexbox for 1D distribution
- Intrinsic layouts (content-based sizing)
- Subgrid for nested grid alignment
4. Breakpoint Strategy
- Mobile-first media queries
- Content-based breakpoints
- Design token integration
- Feature queries (@supports)
Quick Reference
Modern Breakpoint Scale
/* Mobile-first breakpoints */
/* Base: Mobile (< 640px) */
@media (min-width: 640px) {
/* sm: Landscape phones, small tablets */
}
@media (min-width: 768px) {
/* md: Tablets */
}
@media (min-width: 1024px) {
/* lg: Laptops, small desktops */
}
@media (min-width: 1280px) {
/* xl: Desktops */
}
@media (min-width: 1536px) {
/* 2xl: Large desktops */
}
/* Tailwind CSS equivalent */
/* sm: @media (min-width: 640px) */
/* md: @media (min-width: 768px) */
/* lg: @media (min-width: 1024px) */
/* xl: @media (min-width: 1280px) */
/* 2xl: @media (min-width: 1536px) */Key Patterns
Pattern 1: Container Queries
/* Define a containment context */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Query the container, not the viewport */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1rem;
}
.card-image {
aspect-ratio: 1;
}
}
@container card (min-width: 600px) {
.card {
grid-template-columns: 250px 1fr;
}
.card-title {
font-size: 1.5rem;
}
}
/* Container query units */
.card-title {
/* 5% of container width, clamped between 1rem and 2rem */
font-size: clamp(1rem, 5cqi, 2rem);
}// React component with container queries
function ResponsiveCard({ title, image, description }) {
return (
<div className="@container">
<article className="flex flex-col @md:flex-row @md:gap-4">
<img
src={image}
alt=""
className="w-full @md:w-48 @lg:w-64 aspect-video @md:aspect-square object-cover"
/>
<div className="p-4 @md:p-0">
<h2 className="text-lg @md:text-xl @lg:text-2xl font-semibold">
{title}
</h2>
<p className="mt-2 text-muted-foreground @md:line-clamp-3">
{description}
</p>
</div>
</article>
</div>
);
}Pattern 2: Fluid Typography
/* Fluid type scale using clamp() */
:root {
/* Min size, preferred (fluid), max size */
--text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
--text-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
--text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem);
--text-xl: clamp(1.25rem, 1rem + 1.25vw, 1.5rem);
--text-2xl: clamp(1.5rem, 1.25rem + 1.25vw, 2rem);
--text-3xl: clamp(1.875rem, 1.5rem + 1.875vw, 2.5rem);
--text-4xl: clamp(2.25rem, 1.75rem + 2.5vw, 3.5rem);
}
/* Usage */
h1 {
font-size: var(--text-4xl);
}
h2 {
font-size: var(--text-3xl);
}
h3 {
font-size: var(--text-2xl);
}
p {
font-size: var(--text-base);
}
/* Fluid spacing scale */
:root {
--space-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem);
--space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem);
--space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
--space-xl: clamp(2rem, 1.5rem + 2.5vw, 4rem);
}// Utility function for fluid values
function fluidValue(
minSize: number,
maxSize: number,
minWidth = 320,
maxWidth = 1280,
) {
const slope = (maxSize - minSize) / (maxWidth - minWidth);
const yAxisIntersection = -minWidth * slope + minSize;
return `clamp(${minSize}rem, ${yAxisIntersection.toFixed(4)}rem + ${(slope * 100).toFixed(4)}vw, ${maxSize}rem)`;
}
// Generate fluid type scale
const fluidTypeScale = {
sm: fluidValue(0.875, 1),
base: fluidValue(1, 1.125),
lg: fluidValue(1.25, 1.5),
xl: fluidValue(1.5, 2),
"2xl": fluidValue(2, 3),
};Pattern 3: CSS Grid Responsive Layout
/* Auto-fit grid - items wrap automatically */
.grid-auto {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: 1.5rem;
}
/* Auto-fill grid - maintains empty columns */
.grid-auto-fill {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
/* Responsive grid with named areas */
.page-layout {
display: grid;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
gap: 1rem;
}
@media (min-width: 768px) {
.page-layout {
grid-template-columns: 1fr 300px;
grid-template-areas:
"header header"
"main sidebar"
"footer footer";
}
}
@media (min-width: 1024px) {
.page-layout {
grid-template-columns: 250px 1fr 300px;
grid-template-areas:
"header header header"
"nav main sidebar"
"footer footer footer";
}
}
.header {
grid-area: header;
}
.main {
grid-area: main;
}
.sidebar {
grid-area: sidebar;
}
.footer {
grid-area: footer;
}// Responsive grid component
function ResponsiveGrid({ children, minItemWidth = "250px", gap = "1.5rem" }) {
return (
<div
className="grid"
style={{
gridTemplateColumns: `repeat(auto-fit, minmax(min(${minItemWidth}, 100%), 1fr))`,
gap,
}}
>
{children}
</div>
);
}
// Usage with Tailwind
function ProductGrid({ products }) {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}Pattern 4: Responsive Navigation
function ResponsiveNav({ items }) {
const [isOpen, setIsOpen] = useState(false);
return (
<nav className="relative">
{/* Mobile menu button */}
<button
className="lg:hidden p-2"
onClick={() => setIsOpen(!isOpen)}
aria-expanded={isOpen}
aria-controls="nav-menu"
>
<span className="sr-only">Toggle navigation</span>
{isOpen ? <X /> : <Menu />}
</button>
{/* Navigation links */}
<ul
id="nav-menu"
className={cn(
// Base: hidden on mobile
"absolute top-full left-0 right-0 bg-background border-b",
"flex flex-col",
// Mobile: slide down
isOpen ? "flex" : "hidden",
// Desktop: always visible, horizontal
"lg:static lg:flex lg:flex-row lg:border-0 lg:bg-transparent",
)}
>
{items.map((item) => (
<li key={item.href}>
<a
href={item.href}
className={cn(
"block px-4 py-3",
"lg:px-3 lg:py-2",
"hover:bg-muted lg:hover:bg-transparent lg:hover:text-primary",
)}
>
{item.label}
</a>
</li>
))}
</ul>
</nav>
);
}Pattern 5: Responsive Images
// Responsive image with art direction
function ResponsiveHero() {
return (
<picture>
{/* Art direction: different crops for different screens */}
<source
media="(min-width: 1024px)"
srcSet="/hero-wide.webp"
type="image/webp"
/>
<source
media="(min-width: 768px)"
srcSet="/hero-medium.webp"
type="image/webp"
/>
<source srcSet="/hero-mobile.webp" type="image/webp" />
{/* Fallback */}
<img
src="/hero-mobile.jpg"
alt="Hero image description"
className="w-full h-auto"
loading="eager"
fetchpriority="high"
/>
</picture>
);
}
// Responsive image with srcset for resolution switching
function ProductImage({ product }) {
return (
<img
src={product.image}
srcSet={`
${product.image}?w=400 400w,
${product.image}?w=800 800w,
${product.image}?w=1200 1200w
`}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt={product.name}
className="w-full h-auto object-cover"
loading="lazy"
/>
);
}Pattern 6: Responsive Tables
// Responsive table with horizontal scroll
function ResponsiveTable({ data, columns }) {
return (
<div className="w-full overflow-x-auto">
<table className="w-full min-w-[600px]">
<thead>
<tr>
{columns.map((col) => (
<th key={col.key} className="text-left p-3">
{col.label}
</th>
))}
</tr>
</thead>
<tbody>
{data.map((row, i) => (
<tr key={i} className="border-t">
{columns.map((col) => (
<td key={col.key} className="p-3">
{row[col.key]}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
);
}
// Card-based table for mobile
function ResponsiveDataTable({ data, columns }) {
return (
<>
{/* Desktop table */}
<table className="hidden md:table w-full">
{/* ... standard table */}
</table>
{/* Mobile cards */}
<div className="md:hidden space-y-4">
{data.map((row, i) => (
<div key={i} className="border rounded-lg p-4 space-y-2">
{columns.map((col) => (
<div key={col.key} className="flex justify-between">
<span className="font-medium text-muted-foreground">
{col.label}
</span>
<span>{row[col.key]}</span>
</div>
))}
</div>
))}
</div>
</>
);
}Viewport Units
/* Standard viewport units */
.full-height {
height: 100vh; /* May cause issues on mobile */
}
/* Dynamic viewport units (recommended for mobile) */
.full-height-dynamic {
height: 100dvh; /* Accounts for mobile browser UI */
}
/* Small viewport (minimum) */
.min-full-height {
min-height: 100svh;
}
/* Large viewport (maximum) */
.max-full-height {
max-height: 100lvh;
}
/* Viewport-relative font sizing */
.hero-title {
/* 5vw with min/max bounds */
font-size: clamp(2rem, 5vw, 4rem);
}Fluid Layouts and Typography
Overview
Fluid design creates smooth scaling experiences by using relative units and mathematical functions instead of fixed breakpoints. This approach reduces the need for media queries and creates more natural-feeling interfaces.
Fluid Typography
The clamp() Function
/* clamp(minimum, preferred, maximum) */
.heading {
/* Never smaller than 1.5rem, never larger than 3rem */
/* Scales at 5vw between those values */
font-size: clamp(1.5rem, 5vw, 3rem);
}Calculating Fluid Values
The preferred value in clamp() typically combines a base size with a viewport-relative portion:
/* Formula: clamp(min, base + scale * vw, max) */
/* For text that scales from 16px (320px viewport) to 24px (1200px viewport): */
/* slope = (24 - 16) / (1200 - 320) = 8 / 880 = 0.00909 */
/* y-intercept = 16 - 0.00909 * 320 = 13.09px = 0.818rem */
.text {
font-size: clamp(1rem, 0.818rem + 0.909vw, 1.5rem);
}Type Scale Generator
// Generate a fluid type scale
function fluidType({
minFontSize,
maxFontSize,
minViewport = 320,
maxViewport = 1200,
}) {
const minFontRem = minFontSize / 16;
const maxFontRem = maxFontSize / 16;
const minViewportRem = minViewport / 16;
const maxViewportRem = maxViewport / 16;
const slope = (maxFontRem - minFontRem) / (maxViewportRem - minViewportRem);
const yAxisIntersection = minFontRem - slope * minViewportRem;
return `clamp(${minFontRem}rem, ${yAxisIntersection.toFixed(4)}rem + ${(slope * 100).toFixed(4)}vw, ${maxFontRem}rem)`;
}
// Usage
const typeScale = {
xs: fluidType({ minFontSize: 12, maxFontSize: 14 }),
sm: fluidType({ minFontSize: 14, maxFontSize: 16 }),
base: fluidType({ minFontSize: 16, maxFontSize: 18 }),
lg: fluidType({ minFontSize: 18, maxFontSize: 20 }),
xl: fluidType({ minFontSize: 20, maxFontSize: 24 }),
"2xl": fluidType({ minFontSize: 24, maxFontSize: 32 }),
"3xl": fluidType({ minFontSize: 30, maxFontSize: 48 }),
"4xl": fluidType({ minFontSize: 36, maxFontSize: 60 }),
};Complete Type Scale
:root {
/* Base: 16-18px */
--text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
/* Smaller sizes */
--text-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
--text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
/* Larger sizes */
--text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem);
--text-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
--text-2xl: clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
--text-3xl: clamp(1.875rem, 1.4rem + 2.375vw, 2.5rem);
--text-4xl: clamp(2.25rem, 1.5rem + 3.75vw, 3.5rem);
--text-5xl: clamp(3rem, 1.8rem + 6vw, 5rem);
/* Line heights scale inversely */
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.75;
}
/* Apply to elements */
body {
font-size: var(--text-base);
line-height: var(--leading-normal);
}
h1 {
font-size: var(--text-4xl);
line-height: var(--leading-tight);
}
h2 {
font-size: var(--text-3xl);
line-height: var(--leading-tight);
}
h3 {
font-size: var(--text-2xl);
line-height: var(--leading-tight);
}
h4 {
font-size: var(--text-xl);
line-height: var(--leading-normal);
}
h5 {
font-size: var(--text-lg);
line-height: var(--leading-normal);
}
h6 {
font-size: var(--text-base);
line-height: var(--leading-normal);
}
small {
font-size: var(--text-sm);
}Fluid Spacing
Spacing Scale
:root {
/* Spacing tokens that scale with viewport */
--space-3xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.375rem);
--space-2xs: clamp(0.375rem, 0.3rem + 0.375vw, 0.5rem);
--space-xs: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
--space-sm: clamp(0.75rem, 0.6rem + 0.75vw, 1rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem);
--space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
--space-xl: clamp(2rem, 1.5rem + 2.5vw, 3rem);
--space-2xl: clamp(3rem, 2rem + 5vw, 5rem);
--space-3xl: clamp(4rem, 2.5rem + 7.5vw, 8rem);
/* One-up pairs (for asymmetric spacing) */
--space-xs-sm: clamp(0.5rem, 0.3rem + 1vw, 1rem);
--space-sm-md: clamp(0.75rem, 0.5rem + 1.25vw, 1.5rem);
--space-md-lg: clamp(1rem, 0.6rem + 2vw, 2rem);
--space-lg-xl: clamp(1.5rem, 1rem + 2.5vw, 3rem);
}
/* Usage examples */
.section {
padding-block: var(--space-xl);
padding-inline: var(--space-md);
}
.card {
padding: var(--space-md);
gap: var(--space-sm);
}
.stack > * + * {
margin-top: var(--space-md);
}Container Widths
:root {
/* Fluid max-widths */
--container-xs: min(100% - 2rem, 20rem);
--container-sm: min(100% - 2rem, 30rem);
--container-md: min(100% - 2rem, 45rem);
--container-lg: min(100% - 2rem, 65rem);
--container-xl: min(100% - 3rem, 80rem);
--container-2xl: min(100% - 4rem, 96rem);
}
.container {
width: var(--container-lg);
margin-inline: auto;
}
.prose {
max-width: var(--container-md);
}
.full-bleed {
width: 100vw;
margin-inline: calc(-50vw + 50%);
}CSS Grid Fluid Layouts
Auto-fit Grid
/* Grid that fills available space */
.auto-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
gap: var(--space-md);
}
/* With maximum columns */
.auto-grid-max-4 {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(100%, max(200px, calc((100% - 3 * var(--space-md)) / 4))), 1fr)
);
gap: var(--space-md);
}Responsive Grid Areas
.page-grid {
display: grid;
grid-template-columns:
1fr
min(var(--container-lg), 100%)
1fr;
grid-template-rows: auto 1fr auto;
}
.page-grid > * {
grid-column: 2;
}
.full-width {
grid-column: 1 / -1;
}
/* Content with sidebar */
.content-grid {
display: grid;
grid-template-columns: 1fr;
gap: var(--space-lg);
}
@media (min-width: 768px) {
.content-grid {
grid-template-columns: 1fr min(300px, 30%);
}
}Fluid Aspect Ratios
/* Maintain aspect ratio fluidly */
.aspect-video {
aspect-ratio: 16 / 9;
}
.aspect-square {
aspect-ratio: 1;
}
/* Fluid aspect ratio that changes */
.hero-image {
aspect-ratio: 1; /* Mobile: square */
}
@media (min-width: 640px) {
.hero-image {
aspect-ratio: 4 / 3;
}
}
@media (min-width: 1024px) {
.hero-image {
aspect-ratio: 16 / 9;
}
}Flexbox Fluid Patterns
Flexible Sidebar
/* Sidebar that collapses when too narrow */
.with-sidebar {
display: flex;
flex-wrap: wrap;
gap: var(--space-lg);
}
.with-sidebar > :first-child {
flex-basis: 300px;
flex-grow: 1;
}
.with-sidebar > :last-child {
flex-basis: 0;
flex-grow: 999;
min-width: 60%;
}Cluster Layout
/* Items cluster and wrap naturally */
.cluster {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm);
justify-content: flex-start;
align-items: center;
}
/* Center-aligned cluster */
.cluster-center {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm);
justify-content: center;
align-items: center;
}
/* Space-between cluster */
.cluster-spread {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm);
justify-content: space-between;
align-items: center;
}Switcher Layout
/* Switches from horizontal to vertical based on container */
.switcher {
display: flex;
flex-wrap: wrap;
gap: var(--space-md);
}
.switcher > * {
/* Items go vertical when container is narrower than threshold */
flex-grow: 1;
flex-basis: calc((30rem - 100%) * 999);
}
/* Limit columns */
.switcher > :nth-last-child(n + 4),
.switcher > :nth-last-child(n + 4) ~ * {
flex-basis: 100%;
}Intrinsic Sizing
Content-Based Widths
/* Size based on content */
.fit-content {
width: fit-content;
max-width: 100%;
}
/* Minimum content size */
.min-content {
width: min-content;
}
/* Maximum content size */
.max-content {
width: max-content;
}
/* Practical examples */
.button {
width: fit-content;
min-width: 8rem; /* Prevent too-narrow buttons */
padding-inline: var(--space-md);
}
.tag {
width: fit-content;
padding: var(--space-2xs) var(--space-xs);
}
.modal {
width: min(90vw, 600px);
max-height: min(90vh, 800px);
}min() and max() Functions
/* Responsive sizing without media queries */
.container {
/* 90% of viewport or 1200px, whichever is smaller */
width: min(90%, 1200px);
margin-inline: auto;
}
.hero-text {
/* At least 2rem, at most 4rem */
font-size: max(2rem, min(5vw, 4rem));
}
.sidebar {
/* At least 200px, at most 25% of parent */
width: max(200px, min(300px, 25%));
}
.card-grid {
/* Each card at least 200px, fill available space */
grid-template-columns: repeat(auto-fit, minmax(max(200px, 100%/4), 1fr));
}Viewport Units
Modern Viewport Units
/* Dynamic viewport height - accounts for mobile browser UI */
.full-height {
min-height: 100dvh;
}
/* Small viewport - minimum size when UI is visible */
.hero {
min-height: 100svh;
}
/* Large viewport - maximum size when UI is hidden */
.backdrop {
height: 100lvh;
}
/* Viewport-relative positioning */
.fixed-nav {
position: fixed;
inset-inline: 0;
top: 0;
height: max(60px, 8vh);
}
/* Safe area insets for notched devices */
.safe-area {
padding-top: env(safe-area-inset-top);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
}Combining Viewport and Container Units
/* Responsive based on both viewport and container */
.component {
container-type: inline-size;
}
.component-text {
/* Uses viewport when small, container when in container */
font-size: clamp(1rem, 2vw + 0.5rem, 1.5rem);
}
@container (min-width: 400px) {
.component-text {
font-size: clamp(1rem, 4cqi, 1.5rem);
}
}Utility Classes
/* Tailwind-style fluid utilities */
.text-fluid-sm {
font-size: var(--text-sm);
}
.text-fluid-base {
font-size: var(--text-base);
}
.text-fluid-lg {
font-size: var(--text-lg);
}
.text-fluid-xl {
font-size: var(--text-xl);
}
.text-fluid-2xl {
font-size: var(--text-2xl);
}
.text-fluid-3xl {
font-size: var(--text-3xl);
}
.text-fluid-4xl {
font-size: var(--text-4xl);
}
.p-fluid-sm {
padding: var(--space-sm);
}
.p-fluid-md {
padding: var(--space-md);
}
.p-fluid-lg {
padding: var(--space-lg);
}
.gap-fluid-sm {
gap: var(--space-sm);
}
.gap-fluid-md {
gap: var(--space-md);
}
.gap-fluid-lg {
gap: var(--space-lg);
}Resources
Related skills
How it compares
Use responsive-design for opinionated mobile-first CSS philosophy when framework-specific component skills do not cover layout strategy.
FAQ
What breakpoint approach does responsive-design recommend?
responsive-design favors fewer, content-driven breakpoints with a mobile-first base and min-width media queries. Example CSS enhances flex direction and padding at 640px and 1024px rather than targeting specific phone or tablet models.
Why mobile-first in responsive-design?
responsive-design starts with smallest-screen base styles so default CSS stays minimal for performance, then progressively enhances layout for wider viewports. That reduces unused rules shipped to narrow screens.
Is Responsive Design safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.