responsive-design ยท diff

git:20260307.47a5dbc to git:20260522.be57c0b

2 added, 474 removed. Audit A to A.

---
name: responsive-design
description: Implement modern responsive layouts using container queries, fluid typography, CSS Grid, and mobile-first breakpoint strategies. Use when building adaptive interfaces, implementing fluid layouts, or creating component-level responsive behavior.
---
# 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
- ## 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 (
- <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
-
- ```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 (
- <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
-
- ```tsx
- 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
-
- ```tsx
- // 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
-
- ```tsx
- // 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
-
- ```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;
- }
+ ## Detailed patterns and worked examples
- /* Viewport-relative font sizing */
- .hero-title {
- /* 5vw with min/max bounds */
- font-size: clamp(2rem, 5vw, 4rem);
- }
- ```
+ 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