diff --git a/log.md b/log.md index e44e51d..2def3c4 100644 --- a/log.md +++ b/log.md @@ -93,3 +93,5 @@ Append-only. Format: `## [YYYY-MM-DD] ` with at most a hairline border | Redrawn chrome is always wrong in detail and reads as invented UI | | Lottie or Three.js for a simple or non-interactive visual | Hand-built CSS/SVG; 3D must be user-manipulable to earn its bundle | Runtime plus hundreds of KB for what CSS does in zero bytes | diff --git a/wiki/frontend/design/responsive-layout.md b/wiki/frontend/design/responsive-layout.md new file mode 100644 index 0000000..f83f0f1 --- /dev/null +++ b/wiki/frontend/design/responsive-layout.md @@ -0,0 +1,87 @@ +--- +id: frontend-design-responsive-layout +domain: frontend +category: design +applies_to: [css, html, general] +confidence: verified +sources: + - https://web.dev/articles/responsive-web-design-basics + - https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Viewport_meta_element + - https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html + - https://www.w3.org/WAI/WCAG21/Understanding/target-size.html + - https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html + - https://www.w3.org/WAI/WCAG21/Understanding/reflow.html + - https://web.dev/articles/min-max-clamp + - https://web.dev/patterns/layout/repeat-auto-minmax + - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_container_queries + - https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img + - https://developer.mozilla.org/en-US/docs/Web/CSS/min-width + - https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_viewport + - https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover + - https://webkit.org/blog/7929/designing-websites-for-iphone-x/ +last_verified: 2026-08-21 +related: [frontend-design-anti-slop-visual-design, frontend-accessibility-interactive-elements, frontend-performance-bundle-and-assets] +--- + +# Making One Layout Work From 320px Phones to Desktop + +## When this applies + +Building or reviewing web UI that must render across viewport sizes (phone → +desktop); choosing breakpoints, touch-target sizes, fluid type, or responsive +images; a layout overflows horizontally, breaks on mobile, or fails a zoom/reflow +accessibility check. + +## Do this + +Work through these in order — each later item assumes the earlier ones hold: + +| Case | Do | +|------|----| +| Any page intended for mobile | Ship exactly ``. Without it, mobile browsers render into a ~980px virtual viewport and scale down, so width-based media queries never trigger | +| Same meta tag, zoom settings | Leave pinch-zoom enabled: `user-scalable=no` and `maximum-scale=1` are forbidden because low-vision users need ≥2× zoom (WCAG minimum; 5× is the documented best practice) and iOS ignores the restriction anyway — omit both attributes | +| Writing the stylesheet | Mobile-first: base styles target the smallest screen; layer wider layouts with `min-width` media queries. This minimizes overrides versus a desktop-first `max-width` cascade | +| Choosing breakpoint values | Place a breakpoint where THIS content's layout breaks (expand the window until it does), not at device-catalog widths — device-based breakpoints rot as hardware ships | +| A grid of cards/tiles must reflow by width | `grid-template-columns: repeat(auto-fit, minmax(, 1fr))` — zero media queries; `auto-fit` collapses empty tracks and stretches the rest, `auto-fill` keeps empty tracks | +| A component must respond to its container, not the viewport (sidebar vs main placement) | `@container` query with `container-type: inline-size` on the ancestor; keep an intrinsic grid/flex layout as the no-support fallback | +| Sizing interactive targets | ≥24×24 CSS px per WCAG 2.2 AA (SC 2.5.8); 44×44 meets AAA (SC 2.5.5). A smaller target is compliant only when a 24px-diameter circle centered on it intersects no other target's circle — see [frontend-accessibility-interactive-elements] for the rest of the interactive contract | +| Fluid type | `font-size: clamp(, , )`, then verify at 200% browser zoom before shipping — a clamp ceiling can stop text from reaching 200% of its original size, which fails WCAG 1.4.4 | +| Serving images | `srcset` + `sizes` so the browser picks the resource for the slot's layout width; explicit `width`/`height` attributes on every `` so space is reserved pre-load (prevents CLS; matters most on lazy-loaded images) | +| Final gate before shipping | Render at 320px CSS width: all content and functions present with no horizontal scrolling (WCAG 1.4.10 reflow — 320px equals a 1280px desktop at 400% zoom) | + +## Edge cases + +| Case | Then | +|------|------| +| A full-height section uses `100vh` and content hides under the mobile URL bar | `vh` sizes to the largest viewport (chrome retracted). Use `dvh` (tracks the current chrome state, may reflow during scroll) or `svh` (smallest viewport — stable, may leave a gap when chrome retracts) | +| UI is revealed only on hover | Gate it behind `@media (hover: hover)` and give touch users a tap-visible path — `hover: none` devices can only emulate hover via long-tap | +| Edge-to-edge layout on notched/rounded-corner phones | Add `viewport-fit=cover` to the viewport meta, then `padding: max(, env(safe-area-inset-left))` (and the other three insets) so content clears the sensor housing without losing its baseline padding | +| A grid/flex track overflows the viewport because of one long unbreakable child (URL, image, `
`) | Items default to `min-width: auto` ≈ their `min-content` size, so the track cannot shrink below the child. Use `minmax(0, 1fr)` for the track or `min-width: 0` on the item |
+| The layout passes but still "reads AI-generated" | Responsiveness is the floor, not the design — apply [frontend-design-anti-slop-visual-design] (its narrow-viewport row assumes this page's overflow fixes) |
+
+## Instead of
+
+| If you are about to | Do this instead | Why |
+|---------------------|-----------------|-----|
+| Copy breakpoints from a device list (375/768/1024/…) | Derive each breakpoint from where this content's layout fails | Device catalogs churn; content-driven breakpoints are fewer and don't rot |
+| Add `maximum-scale=1` to stop iOS input-focus zoom | Set the input's `font-size` to ≥16px so iOS has no reason to zoom | The attribute blocks low-vision zoom (WCAG ≥2×) and iOS ignores it since iOS 10 anyway |
+| Write one media query per column count for a card grid | `repeat(auto-fit, minmax(, 1fr))` | The intrinsic grid covers every width, including ones you didn't test |
+| Give a track a fixed-px minimum: `minmax(200px, 1fr)` on a container that can be <200px | `minmax(0, 1fr)` plus `min-width` on the content that truly needs it | The px floor forces horizontal overflow on viewports narrower than the sum of floors |
+| Fix mobile layout bugs desktop-first, per bug report | Run the 320px no-horizontal-scroll gate once and fix what it surfaces | The gate is the WCAG 1.4.10 reflow criterion — piecemeal fixes miss views nobody reported |
+
+## Sources
+
+- https://web.dev/articles/responsive-web-design-basics — content-driven (not device-based) breakpoints; small-screen-first workflow
+- https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Viewport_meta_element — ~980px virtual viewport without the meta tag; `user-scalable=no` harm; ≥2× zoom requirement, 5× best practice
+- https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html — SC 2.5.8: 24×24 CSS px AA minimum, 24px-circle spacing exception
+- https://www.w3.org/WAI/WCAG21/Understanding/target-size.html — SC 2.5.5: 44×44 CSS px AAA
+- https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html — 1.4.4: text must resize to 200% without loss
+- https://www.w3.org/WAI/WCAG21/Understanding/reflow.html — 1.4.10: no 2-D scrolling at 320 CSS px width
+- https://web.dev/articles/min-max-clamp — clamp() fluid type; clamp ceiling can fail 1.4.4
+- https://web.dev/patterns/layout/repeat-auto-minmax — auto-fit vs auto-fill semantics
+- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_container_queries — @container, container-type, fallback stance
+- https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img — srcset/sizes selection; width/height reserve space against layout shift
+- https://developer.mozilla.org/en-US/docs/Web/CSS/min-width — `min-width: auto` → min-content minimum on grid/flex items (the overflow mechanism)
+- https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_viewport — vh ≈ lvh; svh/dvh semantics
+- https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover — hover:none on touch (long-tap emulation only)
+- https://webkit.org/blog/7929/designing-websites-for-iphone-x/ — viewport-fit=cover + env(safe-area-inset-*) + max() pattern
diff --git a/wiki/frontend/index.md b/wiki/frontend/index.md
index 906ed86..c864fae 100644
--- a/wiki/frontend/index.md
+++ b/wiki/frontend/index.md
@@ -5,7 +5,8 @@ performance, component structure/composition, in-UI data fetching, async
 loading/error/empty UI states, bundle/asset load performance, form validation UX,
 XSS-safe output, client-side auth token handling, interactive-element accessibility,
 agent-facing tool surfaces (WebMCP tool registration), visual design decisions
-(color/typography/layout/motion styling, canvas effect layers).
+(color/typography/layout/motion styling, canvas effect layers, responsive
+layout across viewport sizes).
 
 Match your situation to a "load when" line; load only matching pages.
 
@@ -79,5 +80,6 @@ Match your situation to a "load when" line; load only matching pages.
 
 | Page | Load when |
 |------|-----------|
-| [anti-slop-visual-design](design/anti-slop-visual-design.md) | Styling or restyling web UI without a design spec; output looks "AI-generated" or template-like; choosing colors, fonts, page structure, or motion for new UI; reviewing a UI diff for template tells |
+| [anti-slop-visual-design](design/anti-slop-visual-design.md) | Styling or restyling web UI without a design spec; picking the theme/aesthetic direction for a new screen (the committed non-generic direction is the default, not an upgrade); output looks "AI-generated" or template-like; choosing colors, fonts, page structure, or motion for new UI; reviewing a UI diff for template tells; writing reusable design guidance for an LLM |
+| [responsive-layout](design/responsive-layout.md) | Building or reviewing UI that must work across viewport sizes (phone → desktop); choosing breakpoints, touch-target sizes, fluid type, or responsive images; a layout overflows horizontally or breaks on mobile; fixing a zoom/reflow accessibility failure (WCAG 1.4.4/1.4.10/2.5.8) |
 | [html-in-canvas](design/html-in-canvas.md) | Wanting shader/3D/canvas-composited effects on real interactive HTML (forms, buttons, sections); about to hand-draw UI widgets inside a canvas with manual hit-testing; adding a canvas effect layer to an existing page |
diff --git a/wiki/frontend/performance/bundle-and-assets.md b/wiki/frontend/performance/bundle-and-assets.md
index 6e321a0..a090cfb 100644
--- a/wiki/frontend/performance/bundle-and-assets.md
+++ b/wiki/frontend/performance/bundle-and-assets.md
@@ -11,7 +11,7 @@ sources:
   - https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Lazy_loading
   - https://web.dev/articles/font-best-practices
 last_verified: 2026-07-10
-related: [frontend-rendering-rerender-and-memoization]
+related: [frontend-rendering-rerender-and-memoization, frontend-design-responsive-layout]
 ---
 
 # Reducing First-Load Payload: Bundle, Images, Fonts