Agent Skills

tailwindcss-responsive-darkmode

Tailwind CSS responsive design and dark mode patterns (2025-2026). PROACTIVELY activate for: (1) dark mode setup (class-based vs media-query, dark: variant), (2) toggling dark mode at runtime (data-theme, html.dark), (3) prefers-color-scheme handling, (4) per-component dark variants, (5) responsive design with dark mode (combining sm:dark:bg-*), (6) system / light / dark / high-contrast theming, (7) CSS color-scheme property, (8) avoiding flash of unstyled content (FOUC) in dark mode, (9) dark-m

Install

npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill tailwindcss-responsive-darkmode
SKILL.md

Tailwind CSS Responsive Design & Dark Mode (2025/2026)

Responsive Design

Mobile-First Approach (Industry Standard 2025/2026)

Tailwind uses a mobile-first breakpoint system. With over 60% of global web traffic from mobile devices and Google's mobile-first indexing, this approach is essential.

Key Principle: Unprefixed utilities apply to ALL screen sizes. Breakpoint prefixes apply at that size AND ABOVE.

<!-- CORRECT: Mobile-first (progressive enhancement) -->
<div class="text-sm md:text-base lg:text-lg">...</div>

<!-- INCORRECT: Desktop-first thinking -->
<div class="lg:text-lg md:text-base text-sm">...</div>

Default Breakpoints

Prefix Min Width Typical Devices CSS Media Query
(none) 0px All mobile phones All sizes
sm: 640px (40rem) Large phones, small tablets @media (min-width: 640px)
md: 768px (48rem) Tablets (portrait) @media (min-width: 768px)
lg: 1024px (64rem) Tablets (landscape), laptops @media (min-width: 1024px)
xl: 1280px (80rem) Desktops @media (min-width: 1280px)
2xl: 1536px (96rem) Large desktops @media (min-width: 1536px)

2025/2026 Device Coverage

Common device sizes to test:

  • 320px: Older iPhones, smallest supported
  • 375px: Modern iPhone base (~17% of mobile)
  • 390-430px: Modern large phones (~35% of mobile)
  • 768px: iPad portrait
  • 1024px: iPad landscape, laptops
  • 1280px: Standard laptops/desktops
  • 1440px: Large desktops
  • 1920px: Full HD displays

Custom Breakpoints

@theme {
  /* Add custom breakpoints for specific content needs */
  --breakpoint-xs: 20rem;   /* 320px - very small devices */
  --breakpoint-3xl: 100rem; /* 1600px */
  --breakpoint-4xl: 120rem; /* 1920px - full HD */

  /* Override existing breakpoints based on YOUR content */
  --breakpoint-sm: 36rem;   /* 576px - when content needs space */
  --breakpoint-lg: 62rem;   /* 992px - common content width */
}

Usage:

<div class="grid xs:grid-cols-2 3xl:grid-cols-6">
  <!-- Custom breakpoints work like built-in ones -->
</div>

Content-Driven Breakpoints (2025 Best Practice)

Instead of targeting devices, let your content determine breakpoints:

@theme {
  /* Based on content needs, not device specs */
  --breakpoint-prose: 65ch;  /* Optimal reading width */
  --breakpoint-content: 75rem; /* Main content max */
}

Test your design at various widths and add breakpoints where layout breaks.

Responsive Examples

Responsive Grid

<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
  <div>Item 4</div>
</div>

Responsive Typography

<h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold">
  Responsive Heading
</h1>

<p class="text-sm md:text-base lg:text-lg leading-relaxed">
  Responsive paragraph text
</p>

Responsive Spacing

<section class="py-8 md:py-12 lg:py-16 px-4 md:px-8 lg:px-12">
  <div class="max-w-4xl mx-auto">
    Content with responsive padding
  </div>
</section>

Responsive Navigation

<nav class="flex flex-col md:flex-row items-center justify-between">
  <div class="hidden md:flex gap-4">
    <!-- Desktop navigation -->
  </div>
  <button class="md:hidden">
    <!-- Mobile menu button -->
  </button>
</nav>

Show/Hide Based on Screen Size

<!-- Hidden on mobile, visible on desktop -->
<div class="hidden md:block">Desktop only</div>

<!-- Visible on mobile, hidden on desktop -->
<div class="block md:hidden">Mobile only</div>

<!-- Different content per breakpoint -->
<span class="sm:hidden">XS</span>
<span class="hidden sm:inline md:hidden">SM</span>
<span class="hidden md:inline lg:hidden">MD</span>
<span class="hidden lg:inline xl:hidden">LG</span>
<span class="hidden xl:inline 2xl:hidden">XL</span>
<span class="hidden 2xl:inline">2XL</span>

Container Queries (v4) - 2025 Game-Changer

Container queries enable component-level responsiveness, independent of viewport size. This is essential for reusable components in 2025.

@plugin "@tailwindcss/container-queries";
<!-- Mark parent as a query container -->
<div class="@container">
  <div class="flex flex-col @md:flex-row @lg:gap-8">
    <!-- Responds to container size, not viewport -->
  </div>
</div>

<!-- Named containers for multiple contexts -->
<div class="@container/card">
  <div class="@lg/card:grid-cols-2 grid grid-cols-1">
    <!-- Responds specifically to 'card' container -->
  </div>
</div>

Container Query Breakpoints

Class Min-width Use Case
@xs 20rem (320px) Small widgets
@sm 24rem (384px) Compact cards
@md 28rem (448px) Standard cards
@lg 32rem (512px) Wide cards
@xl 36rem (576px) Full-width components
@2xl 42rem (672px) Large containers
@3xl 48rem (768px) Page sections

When to Use Container vs Viewport Queries

Container Queries Viewport Queries
Reusable components Page-level layouts
Cards in various contexts Navigation bars
Sidebar widgets Hero sections
CMS/embedded content Full-width sections

Max-Width Breakpoints

Target screens below a certain size:

<!-- Only on screens smaller than md (< 768px) -->
<div class="md:hidden">Small screens only</div>

<!-- Custom max-width media query -->
<div class="[@media(max-width:600px)]:text-sm">
  Custom max-width
</div>

Dark Mode

Strategy: Media (Default)

Dark mode follows the user's operating system preference using prefers-color-scheme:

@import "tailwindcss";
/* No additional configuration needed */
<div class="bg-white dark:bg-gray-900">
  <h1 class="text-gray-900 dark:text-white">Title</h1>
  <p class="text-gray-600 dark:text-gray-300">Content</p>
</div>

Strategy: Selector (Manual Toggle)

Control dark mode with a CSS class:

@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));
<!-- Add .dark class to html or body to enable dark mode -->
<html class="dark">
  <body>
    <div class="bg-white dark:bg-gray-900">
      Content
    </div>
  </body>
</html>

JavaScript Toggle

// Simple toggle
function toggleDarkMode() {
  document.documentElement.classList.toggle('dark');
}

// With localStorage persistence
function initDarkMode() {
  const isDark = localStorage.getItem('darkMode') === 'true' ||
    (!localStorage.getItem('darkMode') &&
     window.matchMedia('(prefers-color-scheme: dark)').matches);

  document.documentElement.classList.toggle('dark', isDark);
}

function toggleDarkMode() {
  const isDark = document.documentElement.classList.toggle('dark');
  localStorage.setItem('darkMode', isDark);
}

// Initialize on page load
initDarkMode();

Three-Way Toggle (Light/Dark/System)

const themes = ['light', 'dark', 'system'];

function setTheme(theme) {
  localStorage.setItem('theme', theme);
  applyTheme();
}

function applyTheme() {
  const theme = localStorage.getItem('theme') || 'system';
  const isDark = theme === 'dark' ||
    (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches);

  document.documentElement.classList.toggle('dark', isDark);
}

// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', () => {
    if (localStorage.getItem('theme') === 'system') {
      applyTheme();
    }
  });

applyTheme();

Data Attribute Strategy

@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
<html data-theme="dark">
  <body>
    <div class="bg-white dark:bg-gray-900">Content</div>
  </body>
</html>

Dark Mode with Next.js (next-themes)

npm install next-themes
// app/providers.tsx
'use client';

import { ThemeProvider } from 'next-themes';

export function Providers({ children }) {
  return (
    <ThemeProvider attribute="class" defaultTheme="system">
      {children}
    </ThemeProvider>
  );
}
// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
// components/ThemeToggle.tsx
'use client';

import { useTheme } from 'next-themes';

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  return (
    <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
      Toggle Theme
    </button>
  );
}

Dark Mode Color Palette

<!-- Text colors -->
<p class="text-gray-900 dark:text-gray-100">Primary text</p>
<p class="text-gray-600 dark:text-gray-400">Secondary text</p>
<p class="text-gray-400 dark:text-gray-500">Muted text</p>

<!-- Background colors -->
<div class="bg-white dark:bg-gray-900">Page background</div>
<div class="bg-gray-50 dark:bg-gray-800">Card background</div>
<div class="bg-gray-100 dark:bg-gray-700">Elevated background</div>

<!-- Border colors -->
<div class="border border-gray-200 dark:border-gray-700">Bordered element</div>

<!-- Interactive elements -->
<button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700">
  Button
</button>

Dark Mode with CSS Variables

@theme {
  /* Light mode colors (default) */
  --color-bg-primary: oklch(1 0 0);
  --color-bg-secondary: oklch(0.98 0 0);
  --color-text-primary: oklch(0.15 0 0);
  --color-text-secondary: oklch(0.4 0 0);
}

/* Dark mode overrides */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg-primary: oklch(0.15 0 0);
    --color-bg-secondary: oklch(0.2 0 0);
    --color-text-primary: oklch(0.95 0 0);
    --color-text-secondary: oklch(0.7 0 0);
  }
}
<div class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
  Semantic colors
</div>

Typography Plugin Dark Mode

<article class="prose dark:prose-invert">
  <!-- Markdown content automatically adapts to dark mode -->
</article>

Combining Responsive and Dark Mode

<!-- Different layouts AND colors based on screen size and theme -->
<div class="
  grid grid-cols-1 md:grid-cols-2
  bg-white dark:bg-gray-900
  p-4 md:p-8
  text-gray-900 dark:text-white
">
  <div class="hidden dark:md:block">
    Only visible on md+ screens in dark mode
  </div>
</div>

Best Practices (2025/2026)

1. Start Mobile, Then Enhance

<!-- CORRECT: Mobile-first progression -->
<div class="text-sm md:text-base lg:text-lg">

<!-- WRONG: Desktop-first thinking (more code, more bugs) -->
<div class="lg:text-lg md:text-base text-sm">

2. Touch-Friendly Interactive Elements

WCAG 2.2 requires 24x24px minimum, but 44x44px is recommended:

<!-- Touch-friendly button (44px minimum) -->
<button class="min-h-11 min-w-11 px-4 py-2.5">
  Click me
</button>

<!-- Touch-friendly navigation link -->
<a href="#" class="block py-3 px-4 min-h-11">
  Navigation Item
</a>

<!-- Adequate spacing between touch targets -->
<div class="flex gap-3">
  <button class="min-h-11 px-4 py-2">Button 1</button>
  <button class="min-h-11 px-4 py-2">Button 2</button>
</div>

3. Fluid Typography (Eliminates Breakpoint Jumps)

@theme {
  --text-fluid-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
  --text-fluid-lg: clamp(1.25rem, 1rem + 1.25vw, 2rem);
  --text-fluid-xl: clamp(1.5rem, 1rem + 2.5vw, 3rem);
}
<h1 class="text-fluid-xl font-bold">Smoothly Scaling Heading</h1>
<p class="text-fluid-base leading-relaxed">Smoothly scaling body text.</p>

2. Use Semantic Dark Mode Colors

@theme {
  /* Instead of raw colors, use semantic names */
  --color-surface: oklch(1 0 0);
  --color-surface-dark: oklch(0.15 0 0);
  --color-on-surface: oklch(0.1 0 0);
  --color-on-surface-dark: oklch(0.95 0 0);
}

3. Test All Breakpoints

Use the debug-screens plugin during development:

npm install -D @tailwindcss/debug-screens
@plugin "@tailwindcss/debug-screens";

4. Reduce Repetition with Components

/* components.css */
@layer components {
  .card {
    @apply bg-white dark:bg-gray-800 rounded-lg p-6 shadow-sm;
  }

  .section {
    @apply py-12 md:py-16 lg:py-24;
  }
}

5. Consider Color Contrast

Ensure sufficient contrast in both light and dark modes (WCAG 2.2):

  • Normal text: 4.5:1 contrast ratio minimum
  • Large text (18pt+): 3:1 contrast ratio minimum
  • Interactive elements: 3:1 against adjacent colors
<!-- Good contrast in both modes -->
<button class="
  bg-blue-600 text-white
  dark:bg-blue-500 dark:text-white
  hover:bg-blue-700 dark:hover:bg-blue-400
  /* Focus ring for accessibility */
  focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2
">
  Action
</button>

6. Reduced Motion Preference

Respect users who prefer reduced motion:

<div class="
  transition-transform duration-300
  hover:scale-105
  motion-reduce:transition-none
  motion-reduce:hover:scale-100
">
  Respects motion preferences
</div>

7. Performance-Optimized Responsive Images

<!-- Lazy load below-fold images -->
<img
  src="image.jpg"
  alt="Description"
  loading="lazy"
  class="w-full h-auto"
/>

<!-- Responsive srcset -->
<img
  src="medium.jpg"
  srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
  sizes="(min-width: 1024px) 50vw, 100vw"
  alt="Responsive image"
  loading="lazy"
  class="w-full h-auto"
/>

8. Safe Area Handling (Notched Devices)

@utility safe-area-pb {
  padding-bottom: env(safe-area-inset-bottom);
}
<!-- Bottom navigation respects device notch -->
<nav class="fixed bottom-0 inset-x-0 safe-area-pb bg-white border-t">
  Navigation
</nav>

Related skills

vercel-react-best-practicesvercel-labs751KReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.vercel-composition-patternsvercel-labs361KReact composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.vercel-react-view-transitionsvercel-labs135KGuide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions viewpick-ui-libraryemilkowalski124KPick the right library for a given frontend task from a curated, opinionated list — numbers, OTP inputs, charts, command menus, virtualization, drag and drop, toasts, state, styling, and more. Only runs when explicitly invoked; it does not trigger on its own.

Search skills and MCP servers

Fuzzy search across 23,137 skills and servers