Skip to main content

Tailwind CSS

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

View as Markdown

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:

pnpm add moraine
pnpm add -D tailwindcss @tailwindcss/vite
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:

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 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.

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:
    @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:

: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.

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.

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 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:
<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.

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 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.

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.