tailwindcss-responsive-darkmode

Tailwind CSS responsive design and dark mode implementation patterns for 2025/2026

INSTALLATION
npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill tailwindcss-responsive-darkmode
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

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 (&#x26;: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') &#x26;&#x26;

     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' &#x26;&#x26; 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 (&#x26;: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>
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card