plugins/frontend-toolkit/skills/responsive-design/SKILL.md
Build mobile-first responsive layouts — breakpoint strategy, fluid type/space with clamp(), container queries, mobile viewport units (dvh) + overflow robustness, touch-target testing, responsive images. Use when adding a layout, when mobile bugs appear, or before shipping a public page. Not for codifying breakpoint/fluid scales as design tokens (use design-system-construction) or auditing touch-target size and zoom against WCAG (use accessibility-audit).
npx skillsauth add jaykim88/claude-ai-engineering responsive-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Layouts adapt from 320px phones to wide desktops without horizontal scroll, overlap, or unreadable text. Mobile-first: base styles target the smallest screen; breakpoints add complexity upward, never strip it down.
Universal — mobile-first ordering, breakpoint-as-content-decision, fluid type/space, container queries, and touch-target minimums are CSS / web-platform concerns identical across frameworks.
Start mobile-first
min-width queriesmax-width overrides) — it leaks desktop assumptions into mobile and is harder to reason aboutSet breakpoints by content, not by device
design-system-construction); don't scatter arbitrary @media (768px) valuesPrefer intrinsic layout over breakpoints where possible
wrap + min()/max()/clamp() and Grid auto-fit / minmax adapt without explicit breakpointsmargin-inline, inset-block) over left/right so layouts also work in RTL (coordinate with i18n-localization)Use container queries for reusable components
Fluid type and spacing
clamp(min, preferred-vw, max) for headings / section spacing — smooth scaling instead of stepped jumpsResponsive images
srcset / sizes (or the framework's image component) so phones don't download desktop-sized images<picture> for art direction (different crop/aspect on mobile vs desktop) and modern formats (AVIF/WebP with fallback); a wrong sizes value silently defeats srcsetaspect-ratio to prevent CLS (coordinate with rendering-performance)Touch targets and zoom
user-scalable=no / maximum-scale=1 — blocks pinch-zoom (WCAG 1.4.4 failure)<meta name="viewport" content="width=device-width, initial-scale=1"> present@media (hover: hover) / pointer: coarse and give a tap equivalent7b. Guard against overflow and mobile viewport quirks
min-width: 0 on flex/grid children — the #1 hidden cause of horizontal scroll: flex items default to min-width: auto and refuse to shrink below their content (a long string or <pre> blows out the layout). Set min-width: 0 (min-w-0) on the shrinking childoverflow-x: auto; overflow-wrap: anywhere / break-words for long URLs and code100vh is wrong on mobile — browser chrome makes vh taller than the visible area, so content is cut off or jumps when the bar hides. Use dvh / svh / lvh (dynamic / small / large viewport)viewport-fit=cover + env(safe-area-inset-*) padding100vh issue), or performanceDesktop-first override vs. mobile-first base
/* ❌ Desktop-first — mobile is an afterthought, overrides pile up */
.grid { display: grid; grid-template-columns: repeat(3, 1fr); }
@media (max-width: 768px) { .grid { grid-template-columns: 1fr; } }
/* ✅ Mobile-first — simplest case is the base, complexity added upward */
.grid { display: grid; grid-template-columns: 1fr; }
@media (min-width: 768px) { .grid { grid-template-columns: repeat(3, 1fr); } }
/* ✅ Better — intrinsic, no breakpoint needed */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
| ❌ Anti-pattern | ✅ Correct |
|---|---|
| Desktop-first max-width overrides | Mobile-first min-width, complexity added upward |
| @media (768px) scattered with arbitrary values | Breakpoint tokens placed where the layout actually breaks |
| Viewport media query inside a reused component | Container query (@container) responding to its parent |
| user-scalable=no to "fix" layout | Allow zoom; fix the layout instead (WCAG 1.4.4) |
| Stepped font-size per breakpoint | clamp() for fluid scaling |
| One image size for all devices | srcset / sizes or framework image component |
| height: 100vh on mobile (content cut off) | 100dvh / svh (accounts for browser chrome) |
| Flex child overflowing on long content | min-width: 0 on the shrinking child |
| Hover-only menu / tooltip on touch | @media (hover: hover) + a tap equivalent |
| Tier | Examples | Action SLA |
|---|---|---|
| Critical | Horizontal scroll, content overlap, or unreadable text at a common viewport (320 / 375 / 768px); content cut off by 100vh on mobile (use dvh); zoom blocked via user-scalable=no / maximum-scale=1 (WCAG 1.4.4 failure) | Block release; fix immediately |
| Major | Touch targets < 44×44px or too closely spaced; hover-only affordance unusable on touch; desktop-first max-width overrides; oversized images served to mobile (no srcset) causing slow load or CLS | Fix this sprint |
| Minor | Stepped font-size per breakpoint instead of clamp(); arbitrary scattered @media values not tokenized; line length exceeding ~75ch; missing safe-area padding on notched devices | Schedule within 2 sprints |
min-width:0 / overflow-x / break-words)min-width queries, no desktop-first overrides)dvh/svh, not 100vh (no mobile cutoff)user-scalable=no)srcset / sizes; no CLS from imagesclamp() fluid scales, container queries for reusable componentsfix(responsive): <component> at <breakpoint> / feat(responsive): mobile-first <layout>sm/md/lg/xl/2xl (mobile-first by default — unprefixed = base, md: = ≥ 768px)clamp() or theme.fontSize tokens; container queries via @tailwindcss/container-queries (@container, @md:)next/image with sizes prop (e.g., sizes="(max-width: 768px) 100vw, 50vw"); <picture> for art directionlayout.tsx (Next.js sets a sensible default — verify no user-scalable=no)h-dvh / min-h-dvh, min-w-0 on flex children, break-words / overflow-x-auto for long contentenv(safe-area-inset-*) (arbitrary pt-[env(safe-area-inset-top)] or a plugin) with viewport-fit=cover[@media(hover:hover)]: so they don't strand touch users<NuxtImg sizes> for responsive images; useMediaQuery (VueUse) for JS-side breakpoint logic@sveltejs/enhanced-img for responsive images; CSS container queries nativeBreakpointObserver; NgOptimizedImage with sizesmin-width, CSS container queries (@container), clamp(), srcset / sizes, and the 44px touch-target minimum are all web-platform standards — framework-independentdesign-system-construction — breakpoints + fluid scales belong in design tokensrendering-performance — responsive images affect LCP / CLSaccessibility-audit — touch-target size + zoom are WCAG requirementsi18n-localization — logical properties + RTL layout mirroringclamp, Grid auto-fit, container queries) so there are fewer explicit states to maintain and test. Mobile-first min-width ordering keeps the simplest styles as the base and adds complexity upward. The most common real-world mobile bugs aren't breakpoints: 100vh cuts off content (use dvh), flex children overflow without min-width: 0, and hover-only UI strands touch users — guard all three.development
Audit and optimize third-party scripts — analytics, tag managers, chat widgets, embeds — with the right loading strategy, performance budget, facades, and CSP/consent controls. Use when adding a script, when TBT/INP regress, when a GDPR/CCPA consent requirement arises, or before shipping. Not for first-party bundle size (use bundle-optimization) or broad Core Web Vitals diagnosis (use rendering-performance).
development
Apply the Testing Trophy (mostly integration tests with RTL + MSW, sparing E2E with Playwright) and set coverage thresholds. Use before new feature work, after bug fixes, when CI coverage falls below target, or when tests are flaky or break on every refactor. Not for wiring coverage gates + Playwright into the GitHub Actions matrix (use cicd-pipeline) or auditing WCAG a11y compliance (use accessibility-audit).
development
Inventory and prioritize technical debt — TODO/FIXME/HACK, any usage, deprecated APIs, untested logic — with impact × effort matrix. Use at quarter start, before a refactoring sprint, when a new teammate joins, or when feature velocity slows. Not for actually paying down debt (use code-refactoring) or recording a migration approach (use decision-records) — this only inventories and prioritizes.
development
Decision framework for choosing the right state location — URL, server cache, local component, or shared/global store. Use when state-sync bugs appear, prop drilling gets deep (3+ levels), filters/tabs lose state on reload, or quarterly review. Not for form state specifically (use form-ux) or when the state is actually server data (use api-caching-optimization).