Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/dicklesworthstone-responsive-design/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✓→✓ | = Same ✓ | 135% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 587% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 323% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 173% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 169% | 0% |
Master modern responsive design techniques to create interfaces that adapt seamlessly across all screen sizes and device contexts.
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) */
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> ); }
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), };
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> ); }
tsxfunction 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> ); }
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" /> ); }
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> </> ); }
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); }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 7,335 | 11,322 | +54% | 1 | 1 | 0% | 1,220 | 5,245 | +330% | 0 | 0 | — |
case-02 | pass→pass | 14,684 | 16,603 | +13% | 1 | 1 | 0% | 3,214 | 7,566 | +135% | 0 | 0 | — |
case-03 | pass→pass | 4,368 | 4,734 | +8% | 1 | 1 | 0% | 721 | 4,951 | +587% | 0 | 0 | — |
case-04 | pass→pass | 6,253 | 5,569 | -11% | 1 | 1 | 0% | 1,219 | 5,153 | +323% | 0 | 0 | — |
case-05 | pass→pass | 11,730 | 9,986 | -15% | 1 | 1 | 0% | 2,246 | 6,132 | +173% | 0 | 0 | — |
case-06 | pass→pass | 10,357 | 9,265 | -11% | 1 | 1 | 0% | 2,161 | 5,822 | +169% | 0 | 0 | — |
case-07 | pass→pass | 6,543 | 5,912 | -10% | 1 | 1 | 0% | 1,090 | 5,186 | +376% | 0 | 0 | — |
case-08 | fail→fail | 22,884 | 18,417 | -20% | 1 | 1 | 0% | 3,754 | 8,002 | +113% | 0 | 0 | — |
case-09 | pass→pass | 7,543 | 6,449 | -15% | 1 | 1 | 0% | 1,316 | 5,148 | +291% | 0 | 0 | — |
case-10 | pass→pass | 5,693 | 3,700 | -35% | 1 | 1 | 0% | 1,073 | 4,670 | +335% | 0 | 0 | — |
case-11 | pass→pass | 3,024 | 3,338 | +10% | 1 | 1 | 0% | 628 | 4,743 | +655% | 0 | 0 | — |
case-12 | pass→pass | 7,135 | 4,847 | -32% | 1 | 1 | 0% | 1,494 | 5,092 | +241% | 0 | 0 | — |
case-13 | pass→pass | 6,842 | 6,448 | -6% | 1 | 1 | 0% | 1,278 | 5,167 | +304% | 0 | 0 | — |
case-14 | pass→pass | 14,812 | 15,425 | +4% | 1 | 1 | 0% | 2,496 | 6,753 | +171% | 0 | 0 | — |
case-15 | pass→pass | 2,537 | 2,358 | -7% | 1 | 1 | 0% | 491 | 4,508 | +818% | 0 | 0 | — |
case-16 | pass→pass | 4,530 | 5,326 | +18% | 1 | 1 | 0% | 882 | 4,968 | +463% | 0 | 0 | — |
case-17 | pass→pass | 8,751 | 5,924 | -32% | 1 | 1 | 0% | 1,551 | 5,200 | +235% | 0 | 0 | — |
case-18 | pass→pass | 18,430 | 17,329 | -6% | 1 | 1 | 0% | 2,717 | 6,679 | +146% | 0 | 0 | — |
case-19 | pass→pass | 3,655 | 4,562 | +25% | 1 | 1 | 0% | 727 | 4,961 | +582% | 0 | 0 | — |
case-20 | pass→pass | 7,402 | 6,055 | -18% | 1 | 1 | 0% | 1,461 | 5,297 | +263% | 0 | 0 | — |
case-21 | pass→pass | 2,913 | 2,934 | +1% | 1 | 1 | 0% | 421 | 4,625 | +999% | 0 | 0 | — |
case-22 | fail→fail | 2,944 | 3,029 | +3% | 1 | 1 | 0% | 464 | 4,523 | +875% | 0 | 0 | — |
case-23 | pass→pass | 4,700 | 4,720 | +0% | 1 | 1 | 0% | 810 | 4,956 | +512% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 23 comparable cases.
Other measured skills in the registry, with their headline benchmark lift.