> ## Documentation Index
> Fetch the complete documentation index at: https://docs.subframe.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dark mode

> Add dark mode to your project using Subframe's built-in theme support

Subframe has built-in dark mode support. Enable it in your theme to define light and dark values side by side, then sync to get a fully configured Tailwind setup.

## Enable dark mode

1. Open **Theme** under **Design System** in the left sidebar
2. At the top of the theme page, click **Add dark mode**
3. Each token now shows light and dark values — edit the dark values to define your dark palette
4. Preview your components and pages in both light and dark mode using the sun/moon toggle in the editor toolbar

<Tip>
  Dark mode colors typically invert the scale: light mode's lightest shade becomes dark mode's darkest, and vice versa.
</Tip>

To remove dark mode, click **⋯** in the theme header and select **Remove dark mode**. This deletes all dark overrides. You can undo this using version history.

## How the generated code works

When dark mode is enabled, Subframe generates theme tokens as CSS variables so light and dark values can switch at runtime.

<Tabs>
  <Tab title="Tailwind v3">
    The CLI syncs two files:

    * **`tailwind.config.js`** — references CSS variables instead of hardcoded values, with `darkMode: 'selector'` enabled
    * **`theme.css`** — defines `:root` variables for light mode and `.dark` overrides for dark mode

    ```js tailwind.config.js theme={null}
    module.exports = {
      darkMode: 'selector',
      theme: {
        extend: {
          colors: {
            "brand-primary": "var(--color-brand-primary)",
            // ... all your color tokens
          },
        },
      },
    }
    ```

    ```css theme.css theme={null}
    :root {
      --color-brand-primary: rgb(26 26 26);
      --color-default-background: rgb(252 252 252);
      /* ... light mode values */
    }

    .dark {
      --color-brand-primary: rgb(212 212 212);
      --color-default-background: rgb(10 10 10);
      /* ... dark mode overrides */
    }
    ```

    Import `theme.css` in your global stylesheet or entry point:

    ```css globals.css theme={null}
    @import "./subframe/theme.css";
    ```
  </Tab>

  <Tab title="Tailwind v4">
    The generated `theme.css` includes a `@custom-variant` for dark mode and a `.dark` block with overrides:

    ```css theme.css theme={null}
    @custom-variant dark (&:where(.dark, .dark *));

    @theme {
      --color-brand-primary: rgb(26 26 26);
      --color-default-background: rgb(252 252 252);
      /* ... light mode values */
    }

    .dark {
      --color-brand-primary: rgb(212 212 212);
      --color-default-background: rgb(10 10 10);
      /* ... dark mode overrides */
    }
    ```
  </Tab>
</Tabs>

## Sync to code

Run the CLI to sync your theme (including dark mode) to your codebase:

<CodeGroup>
  ```bash npm theme={null} theme={null} theme={null} theme={null} theme={null}
  npx @subframe/cli@latest sync --all
  ```

  ```bash yarn theme={null} theme={null} theme={null} theme={null} theme={null}
  yarn dlx @subframe/cli@latest sync --all
  ```

  ```bash pnpm theme={null} theme={null} theme={null} theme={null} theme={null}
  pnpx @subframe/cli@latest sync --all
  ```

  ```bash bun theme={null} theme={null} theme={null} theme={null} theme={null}
  bunx @subframe/cli@latest sync --all
  ```
</CodeGroup>

## Enable dark mode in your app

To activate dark mode, set the `dark` class on the `<html>` element. Here are a few ways to accomplish that:

### Next.js with next-themes

```bash theme={null}
npm install next-themes
```

```tsx app/layout.tsx theme={null}
import { ThemeProvider } from "next-themes"

export default function RootLayout({ children }) {
  return (
    <html suppressHydrationWarning>
      <body>
        <ThemeProvider attribute="class" defaultTheme="system">
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}
```

### React with context

```tsx ThemeProvider.tsx theme={null}
import { createContext, useContext, useEffect, useState } from "react"

const ThemeContext = createContext({ theme: "light", toggleTheme: () => {} })

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light")

  useEffect(() => {
    const root = window.document.documentElement
    root.classList.remove("light", "dark")
    root.classList.add(theme)
  }, [theme])

  const toggleTheme = () => setTheme(theme === "light" ? "dark" : "light")

  return <ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>
}

export const useTheme = () => useContext(ThemeContext)
```

### Theme toggle button

```tsx theme={null}
import { useTheme } from "next-themes"

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

  return <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>{theme === "dark" ? "☀️" : "🌙"}</button>
}
```

## Best practices

<AccordionGroup>
  <Accordion title="Test both modes">
    Always test your application in both light and dark modes. Check for:

    * Sufficient contrast ratios (use browser DevTools)
    * Readability of all text
    * Visibility of borders and dividers
    * Proper styling of interactive states
  </Accordion>

  <Accordion title="Respect system preferences">
    Use the user's system preference as the default:

    ```tsx theme={null}
    <ThemeProvider attribute="class" defaultTheme="system">
    ```
  </Accordion>
</AccordionGroup>
