Seven years of device churn distilled into the rules that survive it: real
viewport sizes, breakpoints, touch targets, safe areas, viewport units,
performance budgets, and a testing matrix — with the Skynet UI feature
that implements each one. Agents can pull this same guide with the MCP
get_best_practices tool.
sm 640 / md 768 / lg 1024 / xl 1280 / 2xl 1536.viewport-fit=cover + env(safe-area-inset-*) for notches, Dynamic Island, home indicators.100svh or 100dvh — never bare 100vh on mobile.srcset/sizes, AVIF/WebP, explicit aspect-ratio, lazy-load below the fold, fetchpriority="high" on the LCP image.prefers-color-scheme, prefers-reduced-motion, prefers-contrast.What actually changed, year by year. The through-line: design for ranges, not devices.
prefers-color-scheme ships and dark mode becomes a requirement; the first Galaxy Fold (768px inner) arrives; 360×640 is the world's top mobile viewport.loading="lazy" lands; mobile passes half of global traffic for good.aspect-ratio ships and kills the padding-hack era of layout-shift prevention.svh/lvh/dvh ship (Safari 15.4, Chrome 108); the Dynamic Island makes viewport-fit=cover + safe-area insets mainstream; 390–393px becomes the iPhone norm.:has() goes cross-browser; WCAG 2.2 lands with the 24×24px AA target-size floor.svh/dvh reach Baseline Widely Available; 360×800, 390×844 and 393×852 come to dominate mobile traffic.CSS pixels (what media queries see), not hardware pixels. Phones render at 2–3× DPR — serve 2× imagery and SVG icons; never read screen.width for layout.
| Width | Devices | Notes |
|---|---|---|
320 | iPhone SE (1st gen), small/older Androids | The floor. Everything must work here. |
360 | Samsung Galaxy A/S, most budget Androids | Single most common mobile width worldwide. |
375 | iPhone SE 2/3, 6–8, X, 11 Pro, 12/13 mini | Still huge in the installed base. |
390 | iPhone 12–14, 15 | Top-three worldwide. |
393 | Pixel 5–9, iPhone 14 Pro–16 | Top-three worldwide. |
402–412 | iPhone 16 Pro (402); Pixel XL & many Androids (412) | |
414 | iPhone 6–8 Plus, XR, 11 | |
428–440 | iPhone Pro Max / Plus models | Largest mainstream phones. |
| State | Width range | Examples |
|---|---|---|
| Folded (cover) | ~320–430 | Z Flip, Z Fold cover screens |
| Unfolded (inner) | ~717–928 | Galaxy Fold 768 → Fold2 884 → recent Folds 900+ |
| Width | Devices |
|---|---|
744 | iPad mini (portrait) |
768 | Classic iPad (portrait) — still the canonical tablet test width |
810–834 | iPad 10.2/10.9, Air, Pro 11 (portrait) |
1024 | iPad Pro 12.9 portrait; classic iPad landscape |
1080–1366 | Tablet landscape widths |
| Width | Context |
|---|---|
1280 | Small laptops, split-screen desktop windows |
1366 | Budget laptops (long-time #2 desktop size) |
1440 | MacBook Pro default scaled resolution |
1536 | 1920 hardware at Windows 125% scaling — extremely common |
1920 | Majority desktop size in 2026 |
2560+ | Growing fast; cap content width so lines stay readable |
min-width onlyStyle the 320px layout as the default and add complexity upward. Overriding desktop styles back down is how bugs breed.
The five-stop scale tracks real clusters: phones <640, large phones 640, tablets/foldables-open 768, laptops 1024, desktops 1280, large 1536.
Percentages, minmax(), flex-wrap, clamp() — a layout should never depend on landing exactly on a breakpoint.
A card in a sidebar and the same card full-width need @container, not @media — cross-browser since 2023.
<!-- The whole responsive-grid story in one line -->
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">…</div>
<!-- Zero-media-query card reflow -->
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
Skynet UI's variants map to 640 / 768 / 1024 / 1280 / 1536; .sk-container caps and centers content; .hide-mobile / .hide-desktop split at 768.
| Standard | Minimum | Status |
|---|---|---|
| WCAG 2.2 — 2.5.8 Target Size | 24×24px (or equivalent spacing) | Level AA — the legal floor |
| WCAG — 2.5.5 Target Size (Enhanced) | 44×44px | Level AAA |
| Apple Human Interface Guidelines | 44×44pt | Platform guidance |
| Material Design | 48×48dp | Platform guidance |
:hover needs a tap/focus path; style :focus-visible as deliberately as hover.@media (pointer: coarse) to widen spacing for touch without penalizing mouse users..sk-btn meets 44px out of the box; keep .sk-btn-sm for mouse-dense toolbars, not primary mobile actions.clamp() scales headings smoothly from 320 to 1920 with zero breakpoints.maximum-scale=1 or user-scalable=no — pinch zoom is an accessibility right.h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }
p { max-width: 65ch; line-height: 1.5; }
repeat(auto-fit, minmax(16rem, 1fr)) is the most useful responsive pattern of the era: cards reflow at every width with no media queries.overflow-wrap: anywhere on user content), media without max-width: 100%.min-width: auto trap: flex/grid children refuse to shrink below content size until you set min-width: 0 — the #1 cause of mystery overflow.overflow-x: auto wrapper (here: .sk-table-wrap); the page never scrolls sideways.margin-inline, padding-block) make layouts survive RTL locales for free.<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
viewport-fit=cover lets the page extend into notch / Dynamic Island territory; pad fixed chrome with env(safe-area-inset-top/bottom/left/right).100vh lies on mobile — it measures the largest viewport, so content hides behind collapsing toolbars. Use 100svh for stable shells (never overflows) or 100dvh to track the chrome; Baseline since 2025.position: sticky over bottom-fixed for actions that must stay visible while typing..app-shell { min-height: 100vh; min-height: 100svh; } /* fallback + fix */
.sk-navbar { padding-top: env(safe-area-inset-top); }
.footer-bar { padding-bottom: env(safe-area-inset-bottom); }
srcset + sizes so a 360px phone doesn't download a 1920px hero; <picture> for AVIF/WebP with fallback.width/height attributes or CSS aspect-ratio on every image and embed — unreserved media is the top cause of layout shift.loading="lazy" below the fold; fetchpriority="high" on the LCP hero; never lazy-load the LCP image.1em in currentColor.playsinline, and gated on prefers-reduced-motion.Core Web Vitals, measured at the 75th percentile on mobile.
font-display: swap and preload.prefers-color-scheme; persist manual toggles and set color-scheme so form controls match. Dark gray surfaces over pure black, desaturated accents, AA contrast maintained — Skynet UI's 9 themes (including contrast) follow this, and the MCP make_theme tool auto-derives AA-guarded custom themes.prefers-reduced-motion: reduce gates parallax, autoplaying carousels, scroll-triggered animation, large view transitions.prefers-contrast: more raises border/text contrast, or switch to a high-contrast theme.<meta name="theme-color"> per scheme so browser chrome matches the page.| Width | Represents |
|---|---|
320 | Absolute floor (iPhone SE 1, small Androids) |
360 | Most common Android |
390–393 | Modern iPhone / Pixel |
412 | Large Android |
768 | Tablet portrait / foldable inner (small) |
820–900 | iPad Air portrait / foldable inner (large) |
1024 | Tablet landscape / small laptop |
1280 | Laptop |
1440 | Desktop |
1920 | Majority desktop |
Beyond widths: both orientations on phone and tablet; a keyboard-only pass (tab order, visible focus, no traps); 200% browser zoom (WCAG 1.4.10 reflow); coarse-pointer emulation; dark and light schemes; reduced motion on; and a real mid-range Android over cellular at least once per release.
| Practice | Skynet UI feature |
|---|---|
| Mobile-first breakpoints | sm: 640 / md: 768 / lg: 1024 / xl: 1280 / 2xl: 1536 variants |
| Capped content width | .sk-container (fluid: .sk-container-fluid) |
| Responsive grids | grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 + gap-* |
| Nav that fits every width | .sk-navbar — burger <768px, one-row scroll for overflow above |
| Wide tables | .sk-table-wrap wrapper |
| Touch targets | .sk-btn ≥ 44px; :focus-visible styled everywhere |
| Show/hide by device | .hide-mobile / .hide-desktop (768px boundary) |
| Dark mode & themes | 9 themes, data-theme, dark-first tokens; MCP make_theme |
| Contrast checks | MCP check_contrast (WCAG ratios) |
| Icons at any DPR | 640 inline SVG icons, 1em, currentColor |
| Validate generated markup | MCP validate_markup / npx skynet-ui check |
The full guide is available from the Skynet UI MCP server at https://skynetui.com/mcp:
get_best_practices — the whole guide, or one section via the optional section argument (e.g. "touch", "viewport", "testing").skynetui://best-practices — the same document as markdown.curl -s https://skynetui.com/mcp -X POST -H 'Content-Type: application/json' -d '{
"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"get_best_practices","arguments":{"section":"touch"}}
}'
Stats from StatCounter global data and platform documentation (Apple HIG, Material Design, W3C WCAG 2.2, web.dev Core Web Vitals), current as of 2026. Markdown source: /best-practices.md.