# 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
```css
/* 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
```css
/* 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);
}
```
```tsx
// React component with container queries
function ResponsiveCard({ title, image, description }) {
return (
);
}
```
### Pattern 2: Fluid Typography
```css
/* 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);
}
```
```tsx
// 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
```css
/* 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;
}
```
```tsx
// Responsive grid component
function ResponsiveGrid({ children, minItemWidth = "250px", gap = "1.5rem" }) {
return (
{children}
);
}
// Usage with Tailwind
function ProductGrid({ products }) {
return (
{products.map((product) => (
))}
);
}
```
### Pattern 4: Responsive Navigation
```tsx
function ResponsiveNav({ items }) {
const [isOpen, setIsOpen] = useState(false);
return (
);
}
```
### Pattern 5: Responsive Images
```tsx
// Responsive image with art direction
function ResponsiveHero() {
return (
{/* Art direction: different crops for different screens */}
{/* Fallback */}
);
}
// Responsive image with srcset for resolution switching
function ProductImage({ product }) {
return (
);
}
```
### Pattern 6: Responsive Tables
```tsx
// Responsive table with horizontal scroll
function ResponsiveTable({ data, columns }) {
return (
{columns.map((col) => (
|
{col.label}
|
))}
{data.map((row, i) => (
{columns.map((col) => (
|
{row[col.key]}
|
))}
))}
);
}
// Card-based table for mobile
function ResponsiveDataTable({ data, columns }) {
return (
<>
{/* Desktop table */}
{/* ... standard table */}
{/* Mobile cards */}
{data.map((row, i) => (
{columns.map((col) => (
{col.label}
{row[col.key]}
))}
))}
>
);
}
```
## Viewport Units
```css
/* 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);
}
```