Advanced

Unplugin Jade Garden

Learn about the unplugin-jade-garden plugin and how it is used to generate CSS with Tailwind CSS.

The unplugin-jade-garden plugin is a build tool plugin designed to bridge the gap between your jade-garden configurations and your final CSS output, especially when using Tailwind CSS. It works by processing your cva and sva configurations at build time and automatically generates CSS stylesheets containing Tailwind's @apply directives.

This automation eliminates the need for manual class matching, prop handling, or complex PostCSS setups, ensuring that your jade-garden definitions directly translate into optimized CSS. The plugin is required for class-utils like getClasses, prefixClasses, and traits, as it generates the class names that Tailwind needs to scan and process to create the final CSS bundle.

By handling this pre-computation, unplugin-jade-garden ensures your design system is both fully customizable and portable.

How it Works

The unplugin-jade-garden process is designed to be a seamless part of your build pipeline. Here is a step-by-step breakdown of how it converts your jade-garden configurations into a final CSS file:

  1. Configuration Intake: The plugin receives cva and sva configurations.
  2. Parses Definitions: For each configuration, it intelligently parses the class names defined in the base, variants, compoundVariants, slots, and compoundSlots properties.
  3. Generates CSS: It then generates corresponding CSS rules for each component and slot, using Tailwind's @apply directives.
  4. Writes to File: This generated CSS is written to a directory where you specify the entry file (e.g., styles/app.css). The plugin will create this file if it does not already exist.
  5. Tailwind Processing: When your main Tailwind CSS file is processed by PostCSS, the @apply directives generated by the plugin are expanded into plain CSS, ready for production.

Class Name Anatomy

For your cva and sva configurations, it is a requirement that you include a name property. This is responsible for generating class selectors for your components.

A generated class name will have the following anatomy:

"prefix:componentName--componentSlot__variantName--variantType"

Example Generated Output

For a component with a cva configuration, the plugin might generate a CSS file similar to this:

/* src/styles/cva/button.css (output by the plugin) */

.button {
  @apply inline-flex items-center justify-center rounded-md font-medium;
}

.button.button__variant--primary {
  @apply bg-blue-600 text-white hover:bg-blue-700;
}

.button.button__variant--secondary {
  @apply bg-gray-200 text-gray-800 hover:bg-gray-300;
}

For a component with an sva configuration, it generates classes for each slot:

/* src/styles/sva/card.css (output by the plugin) */

.card--root {
  @apply relative rounded-lg shadow-md;
}

.card--header {
  @apply p-4 border-b;
}

.card--body {
  @apply p-4;
}

/* ... and their variants, e.g., */
.card--root.card--root__flat--true {
  @apply shadow-none border border-gray-200;
}

Configuration

The plugin accepts an Options object to customize its behavior:

export type Options = {
  /**
   * The build options object used for the internals of the plugin.
   *
   * @default
   * ```ts
   * {
   *   cache: true,
   *   clean: false,
   *   maxDepth: 5,
   *   outDir: "jade-garden"
   * }
   * ```
   */
  build?: {
    /**
     * Utilizes [`Map`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map)
     * to cache configs for faster rebuilds.
     *
     * @default true
     */
    cache?: boolean;

    /**
     * Will empty the output directory on every build.
     *
     * @default false
     */
    clean?: boolean;

    /**
     * Specify the max depth of nested objects to walk through directory.
     *
     * @default 5
     */
    maxDepth?: number;

    /**
     * Specify the output directory (relative to **`entry`**).
     *
     * @default "jade-garden"
     *
     * @example
     * ```ts
     * {
     *   outDir: "themes/my-theme"
     * }
     * ```
     */
    outDir?: string;
  };

  /**
   * Nested objects containing arrays of your `jade-garden` CVA and SVA configurations.
   *
   * The plugin will process these configurations to generate the corresponding CSS.
   *
   * Object keys will generate directories in root of `outDir`.
   *
   * @example
   * ```ts
   * {
   *   entry: "./styles/main.css",
   *   styleConfigs: {
   *     path: {
   *       to: {
   *         output: [alertSVA, buttonCVA, cardSVA]
   *       }
   *     }
   *   }
   * }
   * // jade-garden/path/to/output -> alert.css button.css card.css index.css
   * ```
   */
  styleConfigs: StyleConfigs;

  /**
   * The main TailwindCSS file (relative to **project root**) where the generated CSS files will output.
   *
   * It is **recommended** that the main TailwindCSS file live in a dedicated directory
   * (e.g., `assets`, `css`, `styles`, etc.).
   *
   * @example
   * ```ts
   * {
   *   entry: "./styles/main.css"
   * }
   * ```
   */
  entry: string;

  /**
   * The shared options object for overriding generated classes.
   *
   * Used in conjunction with `jade-garden`'s `getClasses` function.
   *
   * @default {}
   *
   * @example
   * ```ts
   * {
   *   classNameConfig: {
   *     jgPrefix: "jg",
   *     mergeFn: twMerge as (...inputs: ClassValue[]) => string;,
   *     twPrefix: "tw"
   *   }
   * }
   * ```
   */
  classNameConfig?: ClassNameConfig;
};