---
title: Tailwind CSS
description: Configure the Moraine Tailwind CSS plugin for Tailwind CSS v4,
  scanner paths, and custom utilities.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# Tailwind CSS

> Configure the Moraine Tailwind CSS plugin for Tailwind CSS v4, scanner paths, and custom utilities.

Moraine includes a dedicated Tailwind CSS plugin exported as `moraine/tailwind`. It maps Moraine tokens to CSS variables and registers semantic z-index utilities, keyframe animations, parametric transform modifiers, and `data-*` / `aria-*` state variants.

## Tailwind CSS v4

In an existing Solid/Vite project, install Tailwind's Vite integration:

```shell
pnpm add moraine
pnpm add -D tailwindcss @tailwindcss/vite
```

```ts title="vite.config.ts"
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'

export default defineConfig({
  plugins: [tailwindcss(), solid()],
})
```

Register Moraine with the `@plugin` directive and scan its published distribution files with `@source`:

```css title="src/index.css"
@import 'tailwindcss';
@plugin 'moraine/tailwind';
@source '../node_modules/moraine/dist';
```

Import `./index.css` in `src/main.tsx` and define the theme variables described below. Keep the import in the application entry so the stylesheet is loaded when directly opening any route. See [Tailwind's Vite integration guide](https://tailwindcss.com/docs/installation/using-vite) for the build-tool setup.

### The `@source` path directive

Tailwind CSS v4 ignores `node_modules` during automatic source detection. The Moraine plugin registers utility rules and variants; it does not register component files as sources. Use `@source` even when the plugin is already configured. See [Tailwind's source detection guide](https://tailwindcss.com/docs/detecting-classes-in-source-files#explicitly-registering-sources).

Scan the full `moraine/dist` directory. Note the following requirements:

1. **Path resolution**: `@source` paths are filesystem paths relative to the stylesheet, not package imports. Use a path to the published `dist` folder (e.g. `'../node_modules/moraine/dist'` for `src/index.css`).
2. **Monorepos and workspaces**: If `moraine` is hoisted to a root `node_modules` directory in a pnpm or npm workspace, adjust the relative path to point to the repository root:
   ```css
   @source '../../../node_modules/moraine/dist';
   ```

Tailwind discovers static classes by scanning files, independently of whether a lazy route has been loaded. Ensure route directories are covered by automatic source detection or explicit `@source` entries. If you disable automatic detection with `source(none)`, also register your application sources. These paths are relative to the stylesheet; automatic detection uses the working directory unless a base path is configured.

### Theme variables

Theme variables and page background/text styles belong in your CSS. The plugin registers utilities and variants, but does not emit theme color values. You can reuse compatible variables from a shadcn/ui theme, but must add Moraine's independent `--control` surface in both themes and define its semantic shadows:

```css
:root {
  --control: rgb(255, 255, 255);
  --shadow-surface: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  --shadow-overlay: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-input: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.dark {
  --control: rgb(23, 23, 23);
}
```

Set the global `--sidebar-width` in `:root` to customize the desktop sidebar width. SidebarFrame uses `clamp(14rem, 25%, 20rem)` when unset.

`bg-control` supplies outline field, unchecked choice, and upload surfaces; `border-input` keeps the existing boundary role, including neutral tracks/separators elsewhere. `control` uses existing field text colors and has no dedicated foreground or state family. Alpha utilities such as `bg-control/30` remain supported. See the [form surface migration](https://moraine.subf.dev/docs/design.md#form-surface-migration).

Hover and active utilities prefer explicit variables such as `--primary-hover`; otherwise they mix the current scoped color in browsers that support `color-mix(in oklch, …)`. Older browsers use the base color unless you define a state variable.

`bg-backdrop` reads `--backdrop` with a black-at-10% fallback. Component shadows use `shadow-surface`, `shadow-overlay`, and `shadow-input`, which read `--shadow-surface`, `--shadow-overlay`, and `--shadow-input`. Define these role tokens in `:root` and override them in `.dark`; use `none` to remove a role's shadow. Native size utilities such as `shadow-xs` use Tailwind's own theme and remain independently configurable. See [Backdrop and Semantic Shadows](https://moraine.subf.dev/docs/design.md#backdrop-and-semantic-shadows).

## Injected utilities and variants

The plugin registers the following utilities and variants with Tailwind v4:

### Parametric transform utilities

Combine with `animate-mo-enter` and `animate-mo-exit` to customize motion:

- **Opacity**: `enter-opacity-<percent>`, `exit-opacity-<percent>`
- **Scale**: `enter-scale-<percent>`, `exit-scale-<percent>`
- **Translate**: `enter-translate-x-<num>`, `enter-translate-y-<num>`, `exit-translate-x-<num>`, `exit-translate-y-<num>`
- **Rotate**: `enter-rotate-<deg>`, `exit-rotate-<deg>`

See the [Animations guide](https://moraine.subf.dev/docs/animations.md) for details and interactive specimens.

### Semantic z-index utilities

- `z-base` (`1`)
- `z-raised` (`2`)
- `z-control` (`3`)
- `z-sticky` (`10`)
- `z-resize` (`20`)
- `z-overlay` (`40`)
- `z-floating` (`50`)

### State attribute variants

Target component DOM states cleanly using utility prefixes:

- **Data attributes**: `data-active:`, `data-checked:`, `data-disabled:`, `data-expanded:`, `data-focused:`, `data-hidden:`, `data-invalid:`, `data-loading:`, `data-open:`, `data-selected:`, `data-pressed:`, `data-submitting:`
- **ARIA attributes**: `aria-busy:`, `aria-checked:`, `aria-disabled:`, `aria-expanded:`, `aria-hidden:`, `aria-invalid:`, `aria-readonly:`, `aria-required:`, `aria-selected:`

```tsx
<button class="bg-muted text-muted-foreground data-active:bg-primary data-active:text-primary-foreground">
  Filter
</button>
```

## Design tokens and CSS variables

The plugin maps utility classes such as `bg-primary`, `text-foreground`, and `rounded-lg` to CSS variables. Define your color palette and dimensions in your application stylesheet as described in the [Design guide](https://moraine.subf.dev/docs/design.md#css-variables).

The default `text-xs` through `text-9xl` scale reads the local `--font-size`, and named radius utilities read `--radius`. Explicit Tailwind theme values can replace these mappings. See [Typography and spacing](https://moraine.subf.dev/docs/design.md#typography-and-spacing) for the differences between
Tailwind, UnoCSS Wind3, and UnoCSS Wind4.

## Troubleshooting

Check the installation button on a clean dev-server start, after restarting with Vite's cache retained, on a directly opened lazy-loaded route, and in a production preview.

- **No utility styles**: Confirm Tailwind's build integration runs and your application entry imports its stylesheet.
- **Application classes work, but Moraine classes are missing**: Confirm `@plugin 'moraine/tailwind'` is loaded and `@source` points to the installed distribution. Registering a plugin alone does not scan a dependency.
- **Rules exist, but colors, radius, or shadows are missing**: Check the referenced CSS variables and their scope. Unlike `presetMoraine()` with default options, the Tailwind plugin does not emit a complete default theme. `MoraineProvider` and the optional `moraine/icon.css` do not replace theme variables or scanning.
- **Classes from a lazy route are missing**: Check source coverage and keep the stylesheet import in the application entry. Correctly registered sources do not require navigating through the home page first.
- **Dynamically constructed classes are missing**: Use complete static class maps. Tailwind cannot infer classes such as `bg-${color}`; in v4, explicitly include additional classes with `@source inline()` on versions that support it. The legacy JavaScript `safelist` option is not supported in v4. See [Tailwind's safelisting guide](https://tailwindcss.com/docs/detecting-classes-in-source-files#safelisting-specific-utilities).

If clearing Vite's dependency cache changes the symptom, recheck the imported stylesheet, registered sources, and theme configuration, then verify a restart with the cache retained. Cache clearing does not replace `@source` and is not a normal setup requirement.

