# Responsive Design

> 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. Use when this capability is needed.

- Skill: `tomevault-io/responsive-design-3` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/responsive-design-3`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/responsive-design-3/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/responsive-design-3

---


# 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;
}

/* Viewport-relative font sizing */
.hero-title {
    /* 5vw with min/max bounds */
    font-size: clamp(2rem, 5vw, 4rem);
}
```

## 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

## Resources

- [CSS Container Queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_container_queries)
- [Utopia Fluid Type Calculator](https://utopia.fyi/type/calculator/)
- [Every Layout](https://every-layout.dev/)
- [Responsive Images Guide](https://web.dev/responsive-images/)
- [CSS Grid Garden](https://cssgridgarden.com/)

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/rainnny7) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

