# How taste-skill Enforces Dark Mode by Default with Dual-Theme Support

> Discover how taste-skill enforces dark mode by default using a three-layer architecture ensuring dual-theme support for all components and strict theming protocols.

- Repository: [Leon Lin/taste-skill](https://github.com/Leonxlnx/taste-skill)
- Tags: how-to-guide
- Published: 2026-05-31

---

**taste-skill enforces dark mode by default through a three-layer architecture that locks the entire page to a single theme, mandates dual-theme support for every component, and provides a strict protocol for implementing Tailwind or CSS-variable-based theming.**

The taste-skill design system treats dark mode as a first-class constraint rather than an optional feature. Defined in [`skills/taste-skill/SKILL.md`](https://github.com/Leonxlnx/taste-skill/blob/main/skills/taste-skill/SKILL.md), the specification requires every consumer-facing page to support both light and dark appearances while strictly preventing mid-page theme switches. This approach ensures brand consistency, accessibility, and predictable visual hierarchy across the entire user interface.

## The Three-Layer Dark Mode Architecture

According to the taste-skill source code, dark mode enforcement operates through three architectural layers codified in [`SKILL.md`](https://github.com/Leonxlnx/taste-skill/blob/main/SKILL.md):

### Page-Level Theme Lock (Lines 341–349)

The **Page Theme Lock** guarantees that an entire page uses exactly one theme—light, dark, or auto—and explicitly forbids mid-page theme transitions. The system detects the user's preference via `prefers-color-scheme`, locks that choice for the duration of the session, and prohibits any child component from overriding it. This rule prevents jarring visual shifts when users scroll or navigate between sections.

### Mandatory Dark-Mode Support (Lines 31–36)

Every consumer-facing page must be built for **both** light and dark modes. The specification forces designers to adopt either Tailwind’s `dark:` utilities or CSS custom-property theming. Components must respect the system preference unless the brand explicitly overrides it, ensuring no single-theme interfaces exist in production.

### Dual-Mode Protocol (Lines 574–585)

The **Dual-Mode Protocol** declares the default implementation strategy and quality standards. It mandates choosing between Tailwind’s `dark:` variant (the default) or a CSS-variables approach, while enforcing contrast parity, visual hierarchy preservation, brand-color fidelity, and the prohibition of pure black (`#000000`) or pure white (`#FFFFFF`) values.

## Implementing the Dual-Theme Strategy

taste-skill supports two mutually exclusive implementation paths, both of which must respect the single-theme lock:

### Tailwind dark: Variant (Default)

The default approach uses Tailwind’s `dark:` modifier to pair every color utility with its dark counterpart. This requires detecting the system preference and applying the `dark` class to the root `html` element once at load time.

```typescript
// layout.tsx - Theme detection and lock
"use client";
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = prefersDark ? 'dark' : 'light'; // Auto-detect and lock

```

Components use the `dark:` prefix for conditional styling without managing state:

```tsx
// Hero component with Tailwind dual-theme support
export function Hero() {
  return (
    <header className="min-h-[100dvh] flex items-center justify-center bg-white dark:bg-zinc-950">
      <h1 className="text-5xl font-bold text-gray-900 dark:text-gray-100">
        Your Product
      </h1>
      <p className="mt-4 text-gray-600 dark:text-gray-400">
        Sub-headline that works in both light and dark.
      </p>
    </header>
  );
}

```

### CSS-Variable Theming

For design systems like `shadcn/ui` or `Radix Themes`, taste-skill permits a semantic token approach using CSS custom properties. The variables swap values under a `[data-theme="dark"]` selector or media query.

```css
/* Global theme tokens in globals.css */
:root {
  --surface: #f5f5f5;
  --text-primary: #111827;
}

[data-theme="dark"] {
  --surface: #111827;
  --text-primary: #f5f5f5;
}

```

JSX components reference these tokens directly, ensuring the theme change propagates from the root:

```tsx
// Card component using CSS variables
export function Card() {
  return (
    <article className="rounded-lg p-6 bg-[var(--surface)] text-[var(--text-primary)] shadow-md">
      <h2 className="text-2xl font-semibold">Feature</h2>
      <p className="mt-2">Description that stays readable in light & dark.</p>
    </article>
  );
}

```

## Enforcing the Single-Theme Lock

To comply with taste-skill’s Page Theme Lock, the theme must be set exactly once at the layout level and never modified by child components. The root `html` element receives the theme class or data attribute, and all descendants inherit this value.

```tsx
// layout.tsx - Theme lock implementation
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html
      lang="en"
      className={theme}               // "light" | "dark"
      data-theme={theme}              // For CSS-variable approach
    >
      <body>{children}</body>
    </html>
  );
}

```

Child components are strictly forbidden from re-assigning `class="dark"` or `data-theme="dark"`. If a brand requires a manual toggle, the switch must affect the entire page by updating the root element, not individual sections:

```typescript
const toggleTheme = () => {
  const newTheme = theme === 'dark' ? 'light' : 'dark';
  document.documentElement.className = newTheme;
};

```

## Summary

- taste-skill enforces dark mode through a **three-layer architecture** defined in [`skills/taste-skill/SKILL.md`](https://github.com/Leonxlnx/taste-skill/blob/main/skills/taste-skill/SKILL.md): Page Theme Lock, Mandatory Dark-Mode Support, and Dual-Mode Protocol.
- The **Page Theme Lock** (lines 341–349) prevents mid-page theme switches by locking the entire document to a single theme determined at load time based on `prefers-color-scheme`.
- Developers must choose between **Tailwind `dark:` utilities** (default) or **CSS-variable theming**, but cannot mix strategies within a single project.
- The specification forbids pure black and white values while requiring contrast parity and brand-color fidelity across both themes.
- Theme overrides and manual toggles must operate at the **root level only**, ensuring no child components violate the single-theme constraint.

## Frequently Asked Questions

### How does taste-skill prevent mid-page theme switching?

The **Page Theme Lock** rule in [`SKILL.md`](https://github.com/Leonxlnx/taste-skill/blob/main/SKILL.md) (lines 341–349) explicitly forbids mid-page theme flips by requiring the theme to be set once at the root `html` element. Child components cannot reassign theme classes or attributes, ensuring visual consistency throughout the user session and preventing layout shifts during navigation.

### What are the two approved theming strategies in taste-skill?

taste-skill supports **Tailwind’s `dark:` variant** as the default approach, where utilities are paired with dark counterparts using the `class` strategy, and a **CSS-variables approach** using semantic tokens that swap values under `[data-theme="dark"]`. Both methods must respect the single-theme lock and detect the system preference via `prefers-color-scheme` unless explicitly overridden by brand requirements.

### Why does taste-skill prohibit pure black and white color values?

The **Dual-Mode Protocol** (lines 574–585) forbids pure black (`#000000`) and pure white (`#FFFFFF`) to maintain brand-color fidelity and reduce eye strain in dark environments. The specification requires using off-black and off-white shades (such as `zinc-950` or `#111827`) that preserve visual hierarchy while meeting WCAG contrast accessibility standards across both themes.

### Can brands override the automatic system preference detection?

Yes, brands may explicitly override the `prefers-color-scheme` detection to force a fixed light or dark mode, but they must still adhere to the **Mandatory Dark-Mode Support** rule (lines 31–36). This means the interface must be architected for both themes even if only one is displayed, and the chosen theme must remain locked at the page level without per-section variations.