Claude Cursor Skill

responsive-design

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.

LLM Mart · 0 points · 11 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download wshobson-agents-plugins_ui-design_skills_responsive-design-554237f.zip · 14 KB
Part of wshobson/agents — 170 skills

Install

skills CLI npx skills add https://github.com/wshobson/agents/tree/main/plugins/ui-design/skills/responsive-design
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wshobson-agents@llmmart
Git git clone https://github.com/wshobson/agents.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wshobson/agents collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Responsive Design

Master modern responsive design techniques to create interfaces that adapt seamlessly across all screen sizes and device contexts.

When to Use This Skill

  • Implementing mobile-first responsive layouts
  • Using container queries for component-based responsiveness
  • Creating fluid typography and spacing scales
  • Building complex layouts with CSS Grid and Flexbox
  • Designing breakpoint strategies for design systems
  • Implementing responsive images and media
  • Creating adaptive navigation patterns
  • Building responsive tables and data displays

Detailed patterns and worked examples

Detailed pattern documentation lives in references/details.md. Read that file when the navigation tier above is insufficient.

Best Practices

  1. Mobile-First: Start with mobile styles, enhance for larger screens
  2. Content Breakpoints: Set breakpoints based on content, not devices
  3. Fluid Over Fixed: Use fluid values for typography and spacing
  4. Container Queries: Use for component-level responsiveness
  5. Test Real Devices: Simulators don't catch all issues
  6. Performance: Optimize images, lazy load off-screen content
  7. Touch Targets: Maintain 44x44px minimum on mobile
  8. Logical Properties: Use inline/block for internationalization

Common Issues

  • Horizontal Overflow: Content breaking out of viewport
  • Fixed Widths: Using px instead of relative units
  • Viewport Height: 100vh issues on mobile browsers
  • Font Size: Text too small on mobile
  • Touch Targets: Buttons too small to tap accurately
  • Aspect Ratio: Images squishing or stretching
  • Z-Index Stacking: Overlays breaking on different screens
Files (agents)
  • references
    • breakpoint-strategies.md 10.4 KB
      # Breakpoint Strategies
      
      ## Overview
      
      Effective breakpoint strategies focus on content needs rather than device sizes. Modern responsive design uses fewer, content-driven breakpoints combined with fluid techniques.
      
      ## Mobile-First Approach
      
      ### Core Philosophy
      
      Start with the smallest screen, then progressively enhance for larger screens.
      
      ```css
      /* Base styles (mobile first) */
      .component {
        display: flex;
        flex-direction: column;
        padding: 1rem;
      }
      
      /* Enhance for larger screens */
      @media (min-width: 640px) {
        .component {
          flex-direction: row;
          padding: 1.5rem;
        }
      }
      
      @media (min-width: 1024px) {
        .component {
          padding: 2rem;
        }
      }
      ```
      
      ### Benefits
      
      1. **Performance**: Mobile devices load only necessary CSS
      2. **Progressive Enhancement**: Features add rather than subtract
      3. **Content Priority**: Forces focus on essential content first
      4. **Simplicity**: Easier to reason about cascading styles
      
      ## Common Breakpoint Scales
      
      ### Tailwind CSS Default
      
      ```css
      /* Tailwind breakpoints */
      /* sm: 640px  - Landscape phones */
      /* md: 768px  - Tablets */
      /* lg: 1024px - Laptops */
      /* xl: 1280px - Desktops */
      /* 2xl: 1536px - Large desktops */
      
      @media (min-width: 640px) {
        /* sm */
      }
      @media (min-width: 768px) {
        /* md */
      }
      @media (min-width: 1024px) {
        /* lg */
      }
      @media (min-width: 1280px) {
        /* xl */
      }
      @media (min-width: 1536px) {
        /* 2xl */
      }
      ```
      
      ### Bootstrap 5
      
      ```css
      /* Bootstrap breakpoints */
      /* sm: 576px */
      /* md: 768px */
      /* lg: 992px */
      /* xl: 1200px */
      /* xxl: 1400px */
      
      @media (min-width: 576px) {
        /* sm */
      }
      @media (min-width: 768px) {
        /* md */
      }
      @media (min-width: 992px) {
        /* lg */
      }
      @media (min-width: 1200px) {
        /* xl */
      }
      @media (min-width: 1400px) {
        /* xxl */
      }
      ```
      
      ### Minimalist Scale
      
      ```css
      /* Simplified 3-breakpoint system */
      /* Base: Mobile (< 600px) */
      /* Medium: Tablets and small laptops (600px - 1024px) */
      /* Large: Desktops (> 1024px) */
      
      :root {
        --bp-md: 600px;
        --bp-lg: 1024px;
      }
      
      @media (min-width: 600px) {
        /* Medium */
      }
      @media (min-width: 1024px) {
        /* Large */
      }
      ```
      
      ## Content-Based Breakpoints
      
      ### Finding Natural Breakpoints
      
      Instead of using device-based breakpoints, identify where your content naturally needs to change.
      
      ```css
      /* Bad: Device-based thinking */
      @media (min-width: 768px) {
        /* iPad breakpoint */
      }
      
      /* Good: Content-based thinking */
      /* Breakpoint where sidebar fits comfortably next to content */
      @media (min-width: 50rem) {
        .layout {
          display: grid;
          grid-template-columns: 1fr 300px;
        }
      }
      
      /* Breakpoint where cards can show 3 across without crowding */
      @media (min-width: 65rem) {
        .card-grid {
          grid-template-columns: repeat(3, 1fr);
        }
      }
      ```
      
      ### Testing Content Breakpoints
      
      ```javascript
      // Find where content breaks
      function findBreakpoints(selector) {
        const element = document.querySelector(selector);
        const breakpoints = [];
      
        for (let width = 320; width <= 1920; width += 10) {
          element.style.width = `${width}px`;
      
          // Check for overflow, wrapping, or layout issues
          if (element.scrollWidth > element.clientWidth) {
            breakpoints.push({ width, issue: "overflow" });
          }
        }
      
        return breakpoints;
      }
      ```
      
      ## Design Token Integration
      
      ### Breakpoint Tokens
      
      ```css
      :root {
        /* Breakpoint values */
        --breakpoint-sm: 640px;
        --breakpoint-md: 768px;
        --breakpoint-lg: 1024px;
        --breakpoint-xl: 1280px;
        --breakpoint-2xl: 1536px;
      
        /* Container widths for each breakpoint */
        --container-sm: 640px;
        --container-md: 768px;
        --container-lg: 1024px;
        --container-xl: 1280px;
        --container-2xl: 1536px;
      }
      
      .container {
        width: 100%;
        max-width: var(--container-lg);
        margin-inline: auto;
        padding-inline: var(--space-4);
      }
      ```
      
      ### JavaScript Integration
      
      ```typescript
      // Breakpoint constants
      export const breakpoints = {
        sm: 640,
        md: 768,
        lg: 1024,
        xl: 1280,
        "2xl": 1536,
      } as const;
      
      // Media query hook
      function useMediaQuery(query: string): boolean {
        const [matches, setMatches] = useState(false);
      
        useEffect(() => {
          const media = window.matchMedia(query);
          setMatches(media.matches);
      
          const listener = () => setMatches(media.matches);
          media.addEventListener("change", listener);
          return () => media.removeEventListener("change", listener);
        }, [query]);
      
        return matches;
      }
      
      // Breakpoint hook
      function useBreakpoint() {
        const isSmall = useMediaQuery(`(min-width: ${breakpoints.sm}px)`);
        const isMedium = useMediaQuery(`(min-width: ${breakpoints.md}px)`);
        const isLarge = useMediaQuery(`(min-width: ${breakpoints.lg}px)`);
        const isXLarge = useMediaQuery(`(min-width: ${breakpoints.xl}px)`);
      
        return {
          isMobile: !isSmall,
          isTablet: isSmall && !isLarge,
          isDesktop: isLarge,
          current: isXLarge
            ? "xl"
            : isLarge
              ? "lg"
              : isMedium
                ? "md"
                : isSmall
                  ? "sm"
                  : "base",
        };
      }
      ```
      
      ## Feature Queries
      
      ### @supports for Progressive Enhancement
      
      ```css
      /* Feature detection instead of browser detection */
      @supports (display: grid) {
        .layout {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
        }
      }
      
      @supports (container-type: inline-size) {
        .card-container {
          container-type: inline-size;
        }
      
        @container (min-width: 400px) {
          .card {
            flex-direction: row;
          }
        }
      }
      
      @supports (aspect-ratio: 16/9) {
        .video-container {
          aspect-ratio: 16/9;
        }
      }
      
      /* Fallback for older browsers */
      @supports not (gap: 1rem) {
        .flex-container > * + * {
          margin-left: 1rem;
        }
      }
      ```
      
      ### Combining Feature and Size Queries
      
      ```css
      /* Only apply grid layout if supported and screen is large enough */
      @supports (display: grid) {
        @media (min-width: 768px) {
          .layout {
            display: grid;
            grid-template-columns: 250px 1fr;
          }
        }
      }
      ```
      
      ## Responsive Patterns by Component
      
      ### Navigation
      
      ```css
      .nav {
        /* Mobile: vertical stack */
        display: flex;
        flex-direction: column;
      }
      
      @media (min-width: 768px) {
        .nav {
          /* Tablet+: horizontal */
          flex-direction: row;
          align-items: center;
        }
      }
      
      /* Or with container queries */
      .nav-container {
        container-type: inline-size;
      }
      
      @container (min-width: 600px) {
        .nav {
          flex-direction: row;
        }
      }
      ```
      
      ### Cards Grid
      
      ```css
      .cards {
        display: grid;
        gap: 1.5rem;
        grid-template-columns: 1fr;
      }
      
      @media (min-width: 640px) {
        .cards {
          grid-template-columns: repeat(2, 1fr);
        }
      }
      
      @media (min-width: 1024px) {
        .cards {
          grid-template-columns: repeat(3, 1fr);
        }
      }
      
      @media (min-width: 1280px) {
        .cards {
          grid-template-columns: repeat(4, 1fr);
        }
      }
      
      /* Better: auto-fit with minimum size */
      .cards-auto {
        display: grid;
        gap: 1.5rem;
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
      }
      ```
      
      ### Hero Section
      
      ```css
      .hero {
        min-height: 50vh;
        padding: var(--space-lg) var(--space-md);
        text-align: center;
      }
      
      .hero-title {
        font-size: clamp(2rem, 5vw + 1rem, 4rem);
      }
      
      .hero-subtitle {
        font-size: clamp(1rem, 2vw + 0.5rem, 1.5rem);
      }
      
      @media (min-width: 768px) {
        .hero {
          min-height: 70vh;
          display: flex;
          align-items: center;
          text-align: left;
        }
      
        .hero-content {
          max-width: 60%;
        }
      }
      
      @media (min-width: 1024px) {
        .hero {
          min-height: 80vh;
        }
      
        .hero-content {
          max-width: 50%;
        }
      }
      ```
      
      ### Tables
      
      ```css
      /* Mobile: cards or horizontal scroll */
      .table-container {
        overflow-x: auto;
      }
      
      .responsive-table {
        min-width: 600px;
      }
      
      /* Alternative: transform to cards on mobile */
      @media (max-width: 639px) {
        .responsive-table {
          min-width: 0;
        }
      
        .responsive-table thead {
          display: none;
        }
      
        .responsive-table tr {
          display: block;
          margin-bottom: 1rem;
          border: 1px solid var(--border);
          border-radius: 0.5rem;
          padding: 1rem;
        }
      
        .responsive-table td {
          display: flex;
          justify-content: space-between;
          padding: 0.5rem 0;
          border: none;
        }
      
        .responsive-table td::before {
          content: attr(data-label);
          font-weight: 600;
        }
      }
      ```
      
      ## Print Styles
      
      ```css
      @media print {
        /* Remove non-essential elements */
        .nav,
        .sidebar,
        .footer,
        .ads {
          display: none;
        }
      
        /* Reset colors and backgrounds */
        * {
          background: white !important;
          color: black !important;
          box-shadow: none !important;
        }
      
        /* Ensure content fits on page */
        .container {
          max-width: 100%;
          padding: 0;
        }
      
        /* Handle page breaks */
        h1,
        h2,
        h3 {
          page-break-after: avoid;
        }
      
        img,
        table {
          page-break-inside: avoid;
        }
      
        /* Show URLs for links */
        a[href^="http"]::after {
          content: " (" attr(href) ")";
          font-size: 0.8em;
        }
      }
      ```
      
      ## Preference Queries
      
      ```css
      /* Dark mode preference */
      @media (prefers-color-scheme: dark) {
        :root {
          --bg: #1a1a1a;
          --text: #f0f0f0;
        }
      }
      
      /* Reduced motion preference */
      @media (prefers-reduced-motion: reduce) {
        *,
        *::before,
        *::after {
          animation-duration: 0.01ms !important;
          animation-iteration-count: 1 !important;
          transition-duration: 0.01ms !important;
        }
      }
      
      /* High contrast preference */
      @media (prefers-contrast: high) {
        :root {
          --text: #000;
          --bg: #fff;
          --border: #000;
        }
      
        .button {
          border: 2px solid currentColor;
        }
      }
      
      /* Reduced data preference */
      @media (prefers-reduced-data: reduce) {
        .hero-video {
          display: none;
        }
      
        .hero-image {
          display: block;
        }
      }
      ```
      
      ## Testing Breakpoints
      
      ```javascript
      // Automated breakpoint testing
      async function testBreakpoints(page, breakpoints) {
        const results = [];
      
        for (const [name, width] of Object.entries(breakpoints)) {
          await page.setViewportSize({ width, height: 800 });
      
          // Check for horizontal overflow
          const hasOverflow = await page.evaluate(() => {
            return (
              document.documentElement.scrollWidth >
              document.documentElement.clientWidth
            );
          });
      
          // Check for elements going off-screen
          const offscreenElements = await page.evaluate(() => {
            const elements = document.querySelectorAll("*");
            return Array.from(elements).filter((el) => {
              const rect = el.getBoundingClientRect();
              return rect.right > window.innerWidth || rect.left < 0;
            }).length;
          });
      
          results.push({
            breakpoint: name,
            width,
            hasOverflow,
            offscreenElements,
          });
        }
      
        return results;
      }
      ```
      
      ## Resources
      
      - [Tailwind CSS Breakpoints](https://tailwindcss.com/docs/responsive-design)
      - [The 100% Correct Way to Do CSS Breakpoints](https://www.freecodecamp.org/news/the-100-correct-way-to-do-css-breakpoints-88d6a5ba1862/)
      - [Modern CSS Solutions](https://moderncss.dev/)
      - [Defensive CSS](https://defensivecss.dev/)
      
    • container-queries.md 9.7 KB
      # Container Queries Deep Dive
      
      ## Overview
      
      Container queries enable component-based responsive design by allowing elements to respond to their container's size rather than the viewport. This paradigm shift makes truly reusable components possible.
      
      ## Browser Support
      
      Container queries have excellent modern browser support (Chrome 105+, Firefox 110+, Safari 16+). For older browsers, provide graceful fallbacks.
      
      ## Containment Basics
      
      ### Container Types
      
      ```css
      /* Size containment - queries based on inline and block size */
      .container {
        container-type: size;
      }
      
      /* Inline-size containment - queries based on inline (width) size only */
      /* Most common and recommended */
      .container {
        container-type: inline-size;
      }
      
      /* Normal - style queries only, no size queries */
      .container {
        container-type: normal;
      }
      ```
      
      ### Named Containers
      
      ```css
      /* Named container for targeted queries */
      .card-wrapper {
        container-type: inline-size;
        container-name: card;
      }
      
      /* Shorthand */
      .card-wrapper {
        container: card / inline-size;
      }
      
      /* Query specific container */
      @container card (min-width: 400px) {
        .card-content {
          display: flex;
        }
      }
      ```
      
      ## Container Query Syntax
      
      ### Width-Based Queries
      
      ```css
      .container {
        container-type: inline-size;
      }
      
      /* Minimum width */
      @container (min-width: 300px) {
        .element {
          /* styles */
        }
      }
      
      /* Maximum width */
      @container (max-width: 500px) {
        .element {
          /* styles */
        }
      }
      
      /* Range syntax */
      @container (300px <= width <= 600px) {
        .element {
          /* styles */
        }
      }
      
      /* Exact width */
      @container (width: 400px) {
        .element {
          /* styles */
        }
      }
      ```
      
      ### Combining Conditions
      
      ```css
      /* AND condition */
      @container (min-width: 400px) and (max-width: 800px) {
        .element {
          /* styles */
        }
      }
      
      /* OR condition */
      @container (max-width: 300px) or (min-width: 800px) {
        .element {
          /* styles */
        }
      }
      
      /* NOT condition */
      @container not (min-width: 400px) {
        .element {
          /* styles */
        }
      }
      ```
      
      ### Named Container Queries
      
      ```css
      /* Multiple named containers */
      .page-wrapper {
        container: page / inline-size;
      }
      
      .sidebar-wrapper {
        container: sidebar / inline-size;
      }
      
      /* Target specific containers */
      @container page (min-width: 1024px) {
        .main-content {
          max-width: 800px;
        }
      }
      
      @container sidebar (min-width: 300px) {
        .sidebar-widget {
          display: grid;
          grid-template-columns: 1fr 1fr;
        }
      }
      ```
      
      ## Container Query Units
      
      ```css
      /* Container query length units */
      .element {
        /* Container query width - 1cqw = 1% of container width */
        width: 50cqw;
      
        /* Container query height - 1cqh = 1% of container height */
        height: 50cqh;
      
        /* Container query inline - 1cqi = 1% of container inline size */
        padding-inline: 5cqi;
      
        /* Container query block - 1cqb = 1% of container block size */
        padding-block: 3cqb;
      
        /* Container query min - smaller of cqi and cqb */
        font-size: 5cqmin;
      
        /* Container query max - larger of cqi and cqb */
        margin: 2cqmax;
      }
      
      /* Practical example: fluid typography based on container */
      .card-title {
        font-size: clamp(1rem, 4cqi, 2rem);
      }
      
      .card-body {
        padding: clamp(0.75rem, 4cqi, 1.5rem);
      }
      ```
      
      ## Style Queries
      
      Style queries allow querying CSS custom property values. Currently limited support.
      
      ```css
      /* Define a custom property */
      .card {
        --layout: stack;
      }
      
      /* Query the property value */
      @container style(--layout: stack) {
        .card-content {
          display: flex;
          flex-direction: column;
        }
      }
      
      @container style(--layout: inline) {
        .card-content {
          display: flex;
          flex-direction: row;
        }
      }
      
      /* Toggle layout via custom property */
      .card.horizontal {
        --layout: inline;
      }
      ```
      
      ## Practical Patterns
      
      ### Responsive Card Component
      
      ```css
      .card-container {
        container: card / inline-size;
      }
      
      .card {
        display: flex;
        flex-direction: column;
        gap: 1rem;
        padding: clamp(1rem, 4cqi, 2rem);
      }
      
      .card-image {
        aspect-ratio: 16/9;
        width: 100%;
        object-fit: cover;
        border-radius: 0.5rem;
      }
      
      .card-title {
        font-size: clamp(1rem, 4cqi, 1.5rem);
        font-weight: 600;
      }
      
      /* Medium container: side-by-side layout */
      @container card (min-width: 400px) {
        .card {
          flex-direction: row;
          align-items: flex-start;
        }
      
        .card-image {
          width: 40%;
          aspect-ratio: 1;
        }
      
        .card-content {
          flex: 1;
        }
      }
      
      /* Large container: enhanced layout */
      @container card (min-width: 600px) {
        .card-image {
          width: 250px;
        }
      
        .card-title {
          font-size: 1.5rem;
        }
      
        .card-actions {
          display: flex;
          gap: 0.5rem;
        }
      }
      ```
      
      ### Responsive Grid Items
      
      ```css
      .grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
        gap: 1.5rem;
      }
      
      .grid-item {
        container-type: inline-size;
      }
      
      .item-content {
        padding: 1rem;
      }
      
      /* Item adapts to its own size, not the viewport */
      @container (min-width: 350px) {
        .item-content {
          padding: 1.5rem;
        }
      
        .item-title {
          font-size: 1.25rem;
        }
      }
      
      @container (min-width: 500px) {
        .item-content {
          display: grid;
          grid-template-columns: auto 1fr;
          gap: 1rem;
        }
      }
      ```
      
      ### Dashboard Widget
      
      ```css
      .widget-container {
        container: widget / inline-size;
      }
      
      .widget {
        --chart-height: 150px;
        padding: 1rem;
      }
      
      .widget-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 1rem;
      }
      
      .widget-chart {
        height: var(--chart-height);
      }
      
      .widget-stats {
        display: grid;
        grid-template-columns: 1fr;
        gap: 0.5rem;
      }
      
      @container widget (min-width: 300px) {
        .widget {
          --chart-height: 200px;
        }
      
        .widget-stats {
          grid-template-columns: repeat(2, 1fr);
        }
      }
      
      @container widget (min-width: 500px) {
        .widget {
          --chart-height: 250px;
          padding: 1.5rem;
        }
      
        .widget-stats {
          grid-template-columns: repeat(4, 1fr);
        }
      
        .widget-actions {
          display: flex;
          gap: 0.5rem;
        }
      }
      ```
      
      ### Navigation Component
      
      ```css
      .nav-container {
        container: nav / inline-size;
      }
      
      .nav {
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
      }
      
      .nav-link {
        display: flex;
        align-items: center;
        gap: 0.75rem;
        padding: 0.75rem 1rem;
        border-radius: 0.5rem;
      }
      
      .nav-link-text {
        display: none;
      }
      
      .nav-link-icon {
        width: 1.5rem;
        height: 1.5rem;
      }
      
      /* Show text when container is wide enough */
      @container nav (min-width: 200px) {
        .nav-link-text {
          display: block;
        }
      }
      
      /* Horizontal layout for wider containers */
      @container nav (min-width: 600px) {
        .nav {
          flex-direction: row;
        }
      
        .nav-link {
          padding: 0.5rem 1rem;
        }
      }
      ```
      
      ## Tailwind CSS Integration
      
      ```tsx
      // Tailwind v3.2+ supports container queries
      // tailwind.config.js
      module.exports = {
        plugins: [require("@tailwindcss/container-queries")],
      };
      
      // Component usage
      function Card({ title, image, description }) {
        return (
          // @container creates containment context
          <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 rounded-lg"
              />
              <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 @lg:text-lg">
                  {description}
                </p>
              </div>
            </article>
          </div>
        );
      }
      
      // Named containers
      function Dashboard() {
        return (
          <div className="@container/main">
            <aside className="@container/sidebar">
              <nav className="flex flex-col @lg/sidebar:flex-row">{/* ... */}</nav>
            </aside>
            <main className="@lg/main:grid @lg/main:grid-cols-2">{/* ... */}</main>
          </div>
        );
      }
      ```
      
      ## Fallback Strategies
      
      ```css
      /* Provide fallbacks for browsers without support */
      .card {
        /* Default (fallback) styles */
        display: flex;
        flex-direction: column;
      }
      
      /* Feature query for container support */
      @supports (container-type: inline-size) {
        .card-container {
          container-type: inline-size;
        }
      
        @container (min-width: 400px) {
          .card {
            flex-direction: row;
          }
        }
      }
      
      /* Alternative: media query fallback */
      .card {
        display: flex;
        flex-direction: column;
      }
      
      /* Viewport-based fallback */
      @media (min-width: 768px) {
        .card {
          flex-direction: row;
        }
      }
      
      /* Enhanced with container queries when supported */
      @supports (container-type: inline-size) {
        @media (min-width: 768px) {
          .card {
            flex-direction: column; /* Reset */
          }
        }
      
        @container (min-width: 400px) {
          .card {
            flex-direction: row;
          }
        }
      }
      ```
      
      ## Performance Considerations
      
      ```css
      /* Avoid over-nesting containers */
      /* Bad: Too many nested containers */
      .level-1 {
        container-type: inline-size;
      }
      .level-2 {
        container-type: inline-size;
      }
      .level-3 {
        container-type: inline-size;
      }
      .level-4 {
        container-type: inline-size;
      }
      
      /* Good: Strategic container placement */
      .component-wrapper {
        container-type: inline-size;
      }
      
      /* Use inline-size instead of size when possible */
      /* size containment is more expensive */
      .container {
        container-type: inline-size; /* Preferred */
        /* container-type: size; */ /* Only when needed */
      }
      ```
      
      ## Testing Container Queries
      
      ```javascript
      // Test container query support
      const supportsContainerQueries = CSS.supports("container-type", "inline-size");
      
      // Resize observer for testing
      const observer = new ResizeObserver((entries) => {
        for (const entry of entries) {
          console.log("Container width:", entry.contentRect.width);
        }
      });
      
      observer.observe(document.querySelector(".container"));
      ```
      
      ## Resources
      
      - [MDN Container Queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_container_queries)
      - [CSS Container Queries Spec](https://www.w3.org/TR/css-contain-3/)
      - [Una Kravets: Container Queries](https://web.dev/cq-stable/)
      - [Ahmad Shadeed: Container Queries Guide](https://ishadeed.com/article/container-queries-are-finally-here/)
      
    • details.md 10.4 KB
      # responsive-design — detailed patterns and worked examples
      
      ## Core Capabilities
      
      ### 1. Container Queries
      
      - Component-level responsiveness independent of viewport
      - Container query units (cqi, cqw, cqh)
      - Style queries for conditional styling
      - Fallbacks for browser support
      
      ### 2. Fluid Typography & Spacing
      
      - CSS clamp() for fluid scaling
      - Viewport-relative units (vw, vh, dvh)
      - Fluid type scales with min/max bounds
      - Responsive spacing systems
      
      ### 3. Layout Patterns
      
      - CSS Grid for 2D layouts
      - Flexbox for 1D distribution
      - Intrinsic layouts (content-based sizing)
      - Subgrid for nested grid alignment
      
      ### 4. Breakpoint Strategy
      
      - Mobile-first media queries
      - Content-based breakpoints
      - Design token integration
      - Feature queries (@supports)
      
      ## Quick Reference
      
      ### Modern Breakpoint Scale
      
      ```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) */
      ```
      
      ## Key Patterns
      
      ### Pattern 1: Container Queries
      
      ```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>
        );
      }
      ```
      
      ### Pattern 2: Fluid Typography
      
      ```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),
      };
      ```
      
      ### Pattern 3: CSS Grid Responsive Layout
      
      ```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>
        );
      }
      ```
      
      ### Pattern 4: Responsive Navigation
      
      ```tsx
      function 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>
        );
      }
      ```
      
      ### Pattern 5: Responsive Images
      
      ```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"
          />
        );
      }
      ```
      
      ### Pattern 6: Responsive Tables
      
      ```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>
          </>
        );
      }
      ```
      
      ## Viewport Units
      
      ```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);
      }
      ```
      
    • fluid-layouts.md 10.5 KB
      # Fluid Layouts and Typography
      
      ## Overview
      
      Fluid design creates smooth scaling experiences by using relative units and mathematical functions instead of fixed breakpoints. This approach reduces the need for media queries and creates more natural-feeling interfaces.
      
      ## Fluid Typography
      
      ### The clamp() Function
      
      ```css
      /* clamp(minimum, preferred, maximum) */
      .heading {
        /* Never smaller than 1.5rem, never larger than 3rem */
        /* Scales at 5vw between those values */
        font-size: clamp(1.5rem, 5vw, 3rem);
      }
      ```
      
      ### Calculating Fluid Values
      
      The preferred value in `clamp()` typically combines a base size with a viewport-relative portion:
      
      ```css
      /* Formula: clamp(min, base + scale * vw, max) */
      
      /* For text that scales from 16px (320px viewport) to 24px (1200px viewport): */
      /* slope = (24 - 16) / (1200 - 320) = 8 / 880 = 0.00909 */
      /* y-intercept = 16 - 0.00909 * 320 = 13.09px = 0.818rem */
      
      .text {
        font-size: clamp(1rem, 0.818rem + 0.909vw, 1.5rem);
      }
      ```
      
      ### Type Scale Generator
      
      ```javascript
      // Generate a fluid type scale
      function fluidType({
        minFontSize,
        maxFontSize,
        minViewport = 320,
        maxViewport = 1200,
      }) {
        const minFontRem = minFontSize / 16;
        const maxFontRem = maxFontSize / 16;
        const minViewportRem = minViewport / 16;
        const maxViewportRem = maxViewport / 16;
      
        const slope = (maxFontRem - minFontRem) / (maxViewportRem - minViewportRem);
        const yAxisIntersection = minFontRem - slope * minViewportRem;
      
        return `clamp(${minFontRem}rem, ${yAxisIntersection.toFixed(4)}rem + ${(slope * 100).toFixed(4)}vw, ${maxFontRem}rem)`;
      }
      
      // Usage
      const typeScale = {
        xs: fluidType({ minFontSize: 12, maxFontSize: 14 }),
        sm: fluidType({ minFontSize: 14, maxFontSize: 16 }),
        base: fluidType({ minFontSize: 16, maxFontSize: 18 }),
        lg: fluidType({ minFontSize: 18, maxFontSize: 20 }),
        xl: fluidType({ minFontSize: 20, maxFontSize: 24 }),
        "2xl": fluidType({ minFontSize: 24, maxFontSize: 32 }),
        "3xl": fluidType({ minFontSize: 30, maxFontSize: 48 }),
        "4xl": fluidType({ minFontSize: 36, maxFontSize: 60 }),
      };
      ```
      
      ### Complete Type Scale
      
      ```css
      :root {
        /* Base: 16-18px */
        --text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
      
        /* Smaller sizes */
        --text-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
        --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
      
        /* Larger sizes */
        --text-lg: clamp(1.125rem, 1rem + 0.625vw, 1.25rem);
        --text-xl: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
        --text-2xl: clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
        --text-3xl: clamp(1.875rem, 1.4rem + 2.375vw, 2.5rem);
        --text-4xl: clamp(2.25rem, 1.5rem + 3.75vw, 3.5rem);
        --text-5xl: clamp(3rem, 1.8rem + 6vw, 5rem);
      
        /* Line heights scale inversely */
        --leading-tight: 1.25;
        --leading-normal: 1.5;
        --leading-relaxed: 1.75;
      }
      
      /* Apply to elements */
      body {
        font-size: var(--text-base);
        line-height: var(--leading-normal);
      }
      
      h1 {
        font-size: var(--text-4xl);
        line-height: var(--leading-tight);
      }
      h2 {
        font-size: var(--text-3xl);
        line-height: var(--leading-tight);
      }
      h3 {
        font-size: var(--text-2xl);
        line-height: var(--leading-tight);
      }
      h4 {
        font-size: var(--text-xl);
        line-height: var(--leading-normal);
      }
      h5 {
        font-size: var(--text-lg);
        line-height: var(--leading-normal);
      }
      h6 {
        font-size: var(--text-base);
        line-height: var(--leading-normal);
      }
      
      small {
        font-size: var(--text-sm);
      }
      ```
      
      ## Fluid Spacing
      
      ### Spacing Scale
      
      ```css
      :root {
        /* Spacing tokens that scale with viewport */
        --space-3xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.375rem);
        --space-2xs: clamp(0.375rem, 0.3rem + 0.375vw, 0.5rem);
        --space-xs: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
        --space-sm: clamp(0.75rem, 0.6rem + 0.75vw, 1rem);
        --space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem);
        --space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
        --space-xl: clamp(2rem, 1.5rem + 2.5vw, 3rem);
        --space-2xl: clamp(3rem, 2rem + 5vw, 5rem);
        --space-3xl: clamp(4rem, 2.5rem + 7.5vw, 8rem);
      
        /* One-up pairs (for asymmetric spacing) */
        --space-xs-sm: clamp(0.5rem, 0.3rem + 1vw, 1rem);
        --space-sm-md: clamp(0.75rem, 0.5rem + 1.25vw, 1.5rem);
        --space-md-lg: clamp(1rem, 0.6rem + 2vw, 2rem);
        --space-lg-xl: clamp(1.5rem, 1rem + 2.5vw, 3rem);
      }
      
      /* Usage examples */
      .section {
        padding-block: var(--space-xl);
        padding-inline: var(--space-md);
      }
      
      .card {
        padding: var(--space-md);
        gap: var(--space-sm);
      }
      
      .stack > * + * {
        margin-top: var(--space-md);
      }
      ```
      
      ### Container Widths
      
      ```css
      :root {
        /* Fluid max-widths */
        --container-xs: min(100% - 2rem, 20rem);
        --container-sm: min(100% - 2rem, 30rem);
        --container-md: min(100% - 2rem, 45rem);
        --container-lg: min(100% - 2rem, 65rem);
        --container-xl: min(100% - 3rem, 80rem);
        --container-2xl: min(100% - 4rem, 96rem);
      }
      
      .container {
        width: var(--container-lg);
        margin-inline: auto;
      }
      
      .prose {
        max-width: var(--container-md);
      }
      
      .full-bleed {
        width: 100vw;
        margin-inline: calc(-50vw + 50%);
      }
      ```
      
      ## CSS Grid Fluid Layouts
      
      ### Auto-fit Grid
      
      ```css
      /* Grid that fills available space */
      .auto-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
        gap: var(--space-md);
      }
      
      /* With maximum columns */
      .auto-grid-max-4 {
        display: grid;
        grid-template-columns: repeat(
          auto-fit,
          minmax(min(100%, max(200px, calc((100% - 3 * var(--space-md)) / 4))), 1fr)
        );
        gap: var(--space-md);
      }
      ```
      
      ### Responsive Grid Areas
      
      ```css
      .page-grid {
        display: grid;
        grid-template-columns:
          1fr
          min(var(--container-lg), 100%)
          1fr;
        grid-template-rows: auto 1fr auto;
      }
      
      .page-grid > * {
        grid-column: 2;
      }
      
      .full-width {
        grid-column: 1 / -1;
      }
      
      /* Content with sidebar */
      .content-grid {
        display: grid;
        grid-template-columns: 1fr;
        gap: var(--space-lg);
      }
      
      @media (min-width: 768px) {
        .content-grid {
          grid-template-columns: 1fr min(300px, 30%);
        }
      }
      ```
      
      ### Fluid Aspect Ratios
      
      ```css
      /* Maintain aspect ratio fluidly */
      .aspect-video {
        aspect-ratio: 16 / 9;
      }
      
      .aspect-square {
        aspect-ratio: 1;
      }
      
      /* Fluid aspect ratio that changes */
      .hero-image {
        aspect-ratio: 1; /* Mobile: square */
      }
      
      @media (min-width: 640px) {
        .hero-image {
          aspect-ratio: 4 / 3;
        }
      }
      
      @media (min-width: 1024px) {
        .hero-image {
          aspect-ratio: 16 / 9;
        }
      }
      ```
      
      ## Flexbox Fluid Patterns
      
      ### Flexible Sidebar
      
      ```css
      /* Sidebar that collapses when too narrow */
      .with-sidebar {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-lg);
      }
      
      .with-sidebar > :first-child {
        flex-basis: 300px;
        flex-grow: 1;
      }
      
      .with-sidebar > :last-child {
        flex-basis: 0;
        flex-grow: 999;
        min-width: 60%;
      }
      ```
      
      ### Cluster Layout
      
      ```css
      /* Items cluster and wrap naturally */
      .cluster {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-sm);
        justify-content: flex-start;
        align-items: center;
      }
      
      /* Center-aligned cluster */
      .cluster-center {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-sm);
        justify-content: center;
        align-items: center;
      }
      
      /* Space-between cluster */
      .cluster-spread {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-sm);
        justify-content: space-between;
        align-items: center;
      }
      ```
      
      ### Switcher Layout
      
      ```css
      /* Switches from horizontal to vertical based on container */
      .switcher {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-md);
      }
      
      .switcher > * {
        /* Items go vertical when container is narrower than threshold */
        flex-grow: 1;
        flex-basis: calc((30rem - 100%) * 999);
      }
      
      /* Limit columns */
      .switcher > :nth-last-child(n + 4),
      .switcher > :nth-last-child(n + 4) ~ * {
        flex-basis: 100%;
      }
      ```
      
      ## Intrinsic Sizing
      
      ### Content-Based Widths
      
      ```css
      /* Size based on content */
      .fit-content {
        width: fit-content;
        max-width: 100%;
      }
      
      /* Minimum content size */
      .min-content {
        width: min-content;
      }
      
      /* Maximum content size */
      .max-content {
        width: max-content;
      }
      
      /* Practical examples */
      .button {
        width: fit-content;
        min-width: 8rem; /* Prevent too-narrow buttons */
        padding-inline: var(--space-md);
      }
      
      .tag {
        width: fit-content;
        padding: var(--space-2xs) var(--space-xs);
      }
      
      .modal {
        width: min(90vw, 600px);
        max-height: min(90vh, 800px);
      }
      ```
      
      ### min() and max() Functions
      
      ```css
      /* Responsive sizing without media queries */
      .container {
        /* 90% of viewport or 1200px, whichever is smaller */
        width: min(90%, 1200px);
        margin-inline: auto;
      }
      
      .hero-text {
        /* At least 2rem, at most 4rem */
        font-size: max(2rem, min(5vw, 4rem));
      }
      
      .sidebar {
        /* At least 200px, at most 25% of parent */
        width: max(200px, min(300px, 25%));
      }
      
      .card-grid {
        /* Each card at least 200px, fill available space */
        grid-template-columns: repeat(auto-fit, minmax(max(200px, 100%/4), 1fr));
      }
      ```
      
      ## Viewport Units
      
      ### Modern Viewport Units
      
      ```css
      /* Dynamic viewport height - accounts for mobile browser UI */
      .full-height {
        min-height: 100dvh;
      }
      
      /* Small viewport - minimum size when UI is visible */
      .hero {
        min-height: 100svh;
      }
      
      /* Large viewport - maximum size when UI is hidden */
      .backdrop {
        height: 100lvh;
      }
      
      /* Viewport-relative positioning */
      .fixed-nav {
        position: fixed;
        inset-inline: 0;
        top: 0;
        height: max(60px, 8vh);
      }
      
      /* Safe area insets for notched devices */
      .safe-area {
        padding-top: env(safe-area-inset-top);
        padding-right: env(safe-area-inset-right);
        padding-bottom: env(safe-area-inset-bottom);
        padding-left: env(safe-area-inset-left);
      }
      ```
      
      ### Combining Viewport and Container Units
      
      ```css
      /* Responsive based on both viewport and container */
      .component {
        container-type: inline-size;
      }
      
      .component-text {
        /* Uses viewport when small, container when in container */
        font-size: clamp(1rem, 2vw + 0.5rem, 1.5rem);
      }
      
      @container (min-width: 400px) {
        .component-text {
          font-size: clamp(1rem, 4cqi, 1.5rem);
        }
      }
      ```
      
      ## Utility Classes
      
      ```css
      /* Tailwind-style fluid utilities */
      .text-fluid-sm {
        font-size: var(--text-sm);
      }
      .text-fluid-base {
        font-size: var(--text-base);
      }
      .text-fluid-lg {
        font-size: var(--text-lg);
      }
      .text-fluid-xl {
        font-size: var(--text-xl);
      }
      .text-fluid-2xl {
        font-size: var(--text-2xl);
      }
      .text-fluid-3xl {
        font-size: var(--text-3xl);
      }
      .text-fluid-4xl {
        font-size: var(--text-4xl);
      }
      
      .p-fluid-sm {
        padding: var(--space-sm);
      }
      .p-fluid-md {
        padding: var(--space-md);
      }
      .p-fluid-lg {
        padding: var(--space-lg);
      }
      
      .gap-fluid-sm {
        gap: var(--space-sm);
      }
      .gap-fluid-md {
        gap: var(--space-md);
      }
      .gap-fluid-lg {
        gap: var(--space-lg);
      }
      ```
      
      ## Resources
      
      - [Utopia Fluid Type Calculator](https://utopia.fyi/)
      - [Modern Fluid Typography](https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/)
      - [Every Layout](https://every-layout.dev/)
      - [CSS min(), max(), and clamp()](https://web.dev/min-max-clamp/)
      
  • SKILL.md 1.9 KB
    ---
    name: responsive-design
    description: 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.
    ---
    
    # Responsive Design
    
    Master modern responsive design techniques to create interfaces that adapt seamlessly across all screen sizes and device contexts.
    
    ## When to Use This Skill
    
    - Implementing mobile-first responsive layouts
    - Using container queries for component-based responsiveness
    - Creating fluid typography and spacing scales
    - Building complex layouts with CSS Grid and Flexbox
    - Designing breakpoint strategies for design systems
    - Implementing responsive images and media
    - Creating adaptive navigation patterns
    - Building responsive tables and data displays
    
    ## Detailed patterns and worked examples
    
    Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
    
    ## Best Practices
    
    1. **Mobile-First**: Start with mobile styles, enhance for larger screens
    2. **Content Breakpoints**: Set breakpoints based on content, not devices
    3. **Fluid Over Fixed**: Use fluid values for typography and spacing
    4. **Container Queries**: Use for component-level responsiveness
    5. **Test Real Devices**: Simulators don't catch all issues
    6. **Performance**: Optimize images, lazy load off-screen content
    7. **Touch Targets**: Maintain 44x44px minimum on mobile
    8. **Logical Properties**: Use inline/block for internationalization
    
    ## Common Issues
    
    - **Horizontal Overflow**: Content breaking out of viewport
    - **Fixed Widths**: Using px instead of relative units
    - **Viewport Height**: 100vh issues on mobile browsers
    - **Font Size**: Text too small on mobile
    - **Touch Targets**: Buttons too small to tap accurately
    - **Aspect Ratio**: Images squishing or stretching
    - **Z-Index Stacking**: Overlays breaking on different screens
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related