# Plane's Tailwind CSS Configuration and Theming Strategies: The Complete Guide

> Explore Plane's Tailwind CSS configuration in its workspace package. Learn about powerful theming strategies using CSS custom properties and Tailwind 4 directives for instant runtime theme switching.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: deep-dive
- Published: 2026-06-18

---

**Plane centralizes its Tailwind CSS configuration in a dedicated workspace package and implements theming through CSS custom properties with Tailwind 4's `@custom-variant` and `@theme` directives, enabling instant runtime theme switching without rebuilds.**

Plane is an open-source project management platform built with a monorepo architecture that demands consistent styling across multiple packages. The project implements a sophisticated **Tailwind CSS configuration and theming strategy** that leverages CSS variables and Tailwind 4 features to support light, dark, and high-contrast modes from a single source of truth. This approach eliminates the need for separate theme builds while maintaining strict design token consistency across the entire UI layer.

## Centralized Tailwind Configuration as a Workspace Package

Plane manages its styling infrastructure through a dedicated package called `@plane/tailwind-config` located at `packages/tailwind-config`. This package acts as the single source of truth for all Tailwind-related configuration, ensuring that every consumer uses identical PostCSS plugins, base styles, and utility classes.

The package exports three critical assets through its [`package.json`](https://github.com/makeplane/plane/blob/main/package.json):

- [`tailwind.config.js`](https://github.com/makeplane/plane/blob/main/tailwind.config.js) – the main Tailwind configuration
- [`index.css`](https://github.com/makeplane/plane/blob/main/index.css) – the shared Tailwind base stylesheet
- [`postcss.config.js`](https://github.com/makeplane/plane/blob/main/postcss.config.js) – the PostCSS configuration

Other packages in the monorepo, such as the UI package at `packages/ui`, consume this configuration via a workspace alias:

```json
// packages/ui/package.json
{
  "dependencies": {
    "@plane/tailwind-config": "workspace:*"
  }
}

```

The UI layer imports the shared stylesheet directly, guaranteeing that every component builds with the same Tailwind core:

```css
/* packages/ui/styles/globals.css */
@import "@plane/tailwind-config/index.css";

```

PostCSS configuration is similarly centralized. The UI package forwards its PostCSS processing to the shared configuration:

```js
// packages/ui/postcss.config.js
import postcssConfig from "@plane/tailwind-config/postcss.config.js";
export default postcssConfig;

```

The shared PostCSS configuration at [`packages/tailwind-config/postcss.config.js`](https://github.com/makeplane/plane/blob/main/packages/tailwind-config/postcss.config.js) wires the Tailwind plugins:

```js
// packages/tailwind-config/postcss.config.js
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

```

## CSS-Variable-Driven Theming Strategy

Plane's theming system is **purely CSS-variable based**, eliminating the need for separate Tailwind builds per theme. All design tokens are defined in [`packages/tailwind-config/variables.css`](https://github.com/makeplane/plane/blob/main/packages/tailwind-config/variables.css) using Tailwind 4's experimental directives.

### Custom Variants for Theme Detection

The configuration defines custom variants that map `data-theme` attributes to CSS selectors. These variants enable Tailwind to generate utilities that only apply under specific theme conditions:

```css
/* packages/tailwind-config/variables.css */
@custom-variant dark (&:where([data-theme*="dark"], [data-theme*="dark"] *));
@custom-variant dark-high-contrast (&:where([data-theme="dark-contrast"], [data-theme="dark-contrast"] *));
@custom-variant light-high-contrast (&:where([data-theme="light-contrast"], [data-theme="light-contrast"] *));

```

These selectors target any ancestor element (typically `<html>` or `<body>`) carrying a `data-theme` attribute, allowing Tailwind to scope styles precisely.

### Token Architecture in variables.css

The file organizes tokens into layered blocks. First, the `@layer base` defines default light-mode values using OKLCH color space for perceptually uniform colors:

```css
/* Light mode (default) */
@layer base {
  :root, :host {
    --neutral-100: oklch(0.9848 0.0003 230.66);
    --brand-500: oklch(0.4511 0.087024 237.37);
    --bg-accent-primary: var(--brand-default);
    /* … additional tokens … */
  }
}

```

Dark mode and high-contrast variants override these values within `@variant` blocks:

```css
/* Dark mode overrides */
@variant dark {
  --neutral-100: oklch(0.1932 0.002 230.81);
  --brand-500: oklch(0.4511 0.087024 237.37);
  --bg-accent-primary: var(--brand-default);
  /* … dark palette values … */
}

```

### Semantic Design Token Mapping

After defining the raw color variables, a `@theme` block exposes semantic tokens that Tailwind utilities can reference directly:

```css
/* packages/tailwind-config/variables.css */
@theme inline {
  --background-color-accent-primary: var(--bg-accent-primary);
  --text-color-primary: var(--txt-primary);
  --border-color-subtle: var(--border-subtle);
  /* … semantic mappings … */
}

```

This architecture allows components to use intuitive class names like `bg-accent-primary`, `text-primary`, and `border-subtle`. Because these map to CSS variables rather than static values, the visual appearance updates instantly when the underlying variable values change.

## Runtime Theme Switching Without Rebuilds

Changing themes in Plane requires no JavaScript state management for styling logic. The system relies entirely on the `data-theme` attribute:

```html
<body data-theme="dark"> … </body>

```

For high-contrast accessibility modes:

```html
<body data-theme="dark-contrast"> … </body>

```

When the `data-theme` attribute updates, the `@custom-variant` selectors automatically match the new state, causing the CSS variables to recalculate. The UI repaints with the appropriate palette immediately, with zero runtime overhead or rebuild requirements.

## Implementation Examples

### Consuming the Shared Configuration

Application entry points import the centralized stylesheet to ensure Tailwind utilities are available:

```tsx
// src/main.tsx (UI entry point)
import "@plane/tailwind-config/index.css";
import "./styles/globals.css";   // pulls the same import again, safe

```

### Using Themed Utilities in Components

Components reference semantic tokens through standard Tailwind class names:

```tsx
// packages/ui/src/components/Button.tsx
export const Button = () => (
  <button className="px-4 py-2 rounded-md bg-accent-primary text-primary hover:bg-accent-primary-hover">
    Save
  </button>
);

```

The classes `bg-accent-primary` and `text-primary` resolve to the CSS variables defined in [`variables.css`](https://github.com/makeplane/plane/blob/main/variables.css), automatically adapting to the current theme.

### Implementing Theme Controls

A simple hook updates the theme by manipulating the `data-theme` attribute:

```tsx
// packages/ui/src/hooks/useTheme.ts
export const useTheme = () => {
  const setTheme = (theme: "light" | "dark" | "dark-contrast" | "light-contrast") => {
    document.documentElement.dataset.theme = theme;
  };
  return { setTheme };
};

// usage
const { setTheme } = useTheme();
<button onClick={() => setTheme("dark")}>Dark mode</button>

```

When `setTheme("dark")` executes, the `@custom-variant dark` selectors activate, and all components instantly adopt the dark palette through CSS variable recalculation.

## Summary

- **Centralized configuration** lives in `packages/tailwind-config` and is consumed via workspace aliases, ensuring every package uses identical Tailwind and PostCSS settings.
- **CSS-variable architecture** stores all design tokens in [`variables.css`](https://github.com/makeplane/plane/blob/main/variables.css) using OKLCH color values for consistent perceptual uniformity across themes.
- **Tailwind 4 directives** including `@custom-variant` and `@theme` enable semantic token mapping and variant-based theme detection without JavaScript overhead.
- **Runtime switching** occurs through the `data-theme` attribute, triggering immediate visual updates via CSS variable recalculation with no rebuild required.
- **Zero-runtime-cost theming** means theme changes happen entirely in the browser's CSS engine, eliminating flash-of-unstyled-content or JavaScript-based style injection.

## Frequently Asked Questions

### Where is Plane's Tailwind CSS configuration located?

Plane's Tailwind CSS configuration is centralized in `packages/tailwind-config` within the monorepo. This package exports [`tailwind.config.js`](https://github.com/makeplane/plane/blob/main/tailwind.config.js), [`index.css`](https://github.com/makeplane/plane/blob/main/index.css), and [`postcss.config.js`](https://github.com/makeplane/plane/blob/main/postcss.config.js), which are consumed by other packages like `packages/ui` through workspace dependencies (`workspace:*`). The configuration uses Tailwind 4's `@tailwindcss/postcss` plugin as defined in [`packages/tailwind-config/postcss.config.js`](https://github.com/makeplane/plane/blob/main/packages/tailwind-config/postcss.config.js).

### How does Plane handle dark mode and high-contrast themes?

Plane implements dark mode and high-contrast themes through CSS custom properties rather than separate Tailwind theme objects. The system uses `@custom-variant` directives in [`packages/tailwind-config/variables.css`](https://github.com/makeplane/plane/blob/main/packages/tailwind-config/variables.css) to define selectors that target `data-theme` attributes (such as `data-theme="dark"` or `data-theme="dark-contrast"`). Color values are defined in `@layer base` for light mode and overridden in `@variant dark` and `@variant dark-high-contrast` blocks, allowing the browser to switch themes instantly by updating CSS variables.

### What is the purpose of the `@theme` directive in Plane's setup?

The `@theme` directive in [`packages/tailwind-config/variables.css`](https://github.com/makeplane/plane/blob/main/packages/tailwind-config/variables.css) maps low-level CSS variables (like `--bg-accent-primary`) to semantic design tokens that Tailwind utilities can reference. This allows developers to use intuitive class names such as `bg-accent-primary` or `text-primary` while the underlying values remain dynamic CSS variables. When the `data-theme` attribute changes, these variables update automatically, causing all themed components to repaint with the correct colors without requiring class name changes or React re-renders for styling purposes.

### Can I use Plane's theming strategy in my own React project?

Yes, you can adapt Plane's theming strategy to any React project using Tailwind 4. The key requirements are: creating a CSS file with `@custom-variant` rules that target `data-theme` attributes, defining color tokens as CSS variables in `@layer base` with variant-specific overrides, and using `@theme` to expose semantic tokens to Tailwind's utility classes. Your theme switching logic only needs to update the `data-theme` attribute on a root element (like `<html>`), and Tailwind's generated selectors will handle the rest through CSS variable scoping.