# Patterns — Responsive Layouts ## Contents - Auto-responsive card grid (no breakpoints) - Page shell (header/content/footer) - Sidebar + content (collapses on narrow) - Container-query card - Media object - Fluid space and size - Overflow-proofing checklist - Gotchas ## Auto-responsive card grid (no breakpoints) ```css .cards { display: grid; /* min(16rem, 100%) prevents overflow when the container is narrower than 16rem */ grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); gap: 1rem; } ``` ## Page shell (header/content/footer, footer pinned) ```css body { min-height: 100dvh; /* dvh avoids mobile URL-bar jump; fallback: 100vh */ display: grid; grid-template-rows: auto 1fr auto; } ``` ## Sidebar + content (collapses on narrow) ```css /* Flexbox version — wraps naturally, no media query */ .with-sidebar { display: flex; flex-wrap: wrap; gap: 1.5rem; } .with-sidebar > .sidebar { flex: 1 1 15rem; } /* basis = collapse point */ .with-sidebar > .content { flex: 999 1 60%; min-width: 0; } ``` ```css /* Grid version — explicit breakpoint */ .shell { display: grid; gap: 1.5rem; } @media (min-width: 48rem) { .shell { grid-template-columns: 15rem minmax(0, 1fr); } } ``` ## Container-query card ```css .card-slot { container-type: inline-size; } .card { display: flex; flex-direction: column; gap: 0.75rem; } @container (min-width: 24rem) { .card { flex-direction: row; align-items: center; } .card img { max-width: 40%; } } ``` ## Media object (avatar + text) ```css .media { display: flex; gap: 0.75rem; align-items: flex-start; } .media > img { flex-shrink: 0; width: 3rem; height: 3rem; border-radius: 50%; } .media > .body { min-width: 0; } /* lets long words/URLs shrink instead of overflow */ ``` ## Fluid space and size ```css :root { /* 1rem at 320px viewport → 2.5rem at 1280px, linear between */ --space-section: clamp(1rem, 0.5rem + 2.5vw, 2.5rem); --content-max: 72rem; } .section { padding-block: var(--space-section); } .container { width: min(100% - 2rem, var(--content-max)); margin-inline: auto; } ``` ## Overflow-proofing checklist ```css img, video, canvas, svg { max-width: 100%; height: auto; } .flex-child, .grid-child { min-width: 0; } /* on anything that must shrink */ .prose { overflow-wrap: break-word; } /* long URLs, tokens */ table { display: block; overflow-x: auto; } /* wide data tables scroll alone */ ``` Test sequence: 360px → 768px → 1280px → one odd width (e.g. 913px). Look for: horizontal scrollbar, clipped text, stretched images, wrap orphans. ## Gotchas - **`minmax(16rem, 1fr)` overflows below 16rem** — always wrap the minimum in `min(16rem, 100%)`. - **`auto-fit` vs `auto-fill`:** auto-fit collapses empty tracks (items stretch); auto-fill keeps them (items stay small). One word, opposite layouts. - **Flex `min-width: auto` default** means text children refuse to shrink — the single most common "why is this overflowing" cause. - **Percentage heights need a sized ancestor**; prefer grid rows (`1fr`) or `min-height` chains. - **`gap` on flexbox** is fine everywhere modern; if supporting old Safari (<14.1), fall back to margins. - **Nested containers:** `@container` resolves against the *nearest* ancestor with `container-type` — name containers (`container-name: card-slot`) when nesting. - **`100dvh` vs `100vh`:** dvh tracks the dynamic mobile toolbar; vh can leave a gap or cause scroll. Provide `100vh` first as fallback on the previous line.