Getting Started

Essentials

Jade Garden at a quick glance.

Jade Garden

If you worked plenty with Class Variance Authority or Tailwind Variants, you can skip most of the documentation. What is covered on this page are the essential enhancements Jade Garden has made in order to make UI development a breeze.

Class Variance Authority

Jade Garden has an equivalent cva function to the beta version of Class Variance Authority. The function accepts a single object as input.

import { cva } from "jade-garden/cva";

const h1 = cva({
  base: "font-bold text-xl text-blue-200"
});

h1(); // "font-bold text-xl text-blue-200"

Slots Variance Authority

The equivalent to Tailwind Variants is the sva function. Unlike Tailwind Variants, sva does not ship with Tailwind Merge. Under the hood, class names are merged with clsx. It is more fair to compare sva with the recently released version of Tailwind Variants lite.

In addition, the base property has been renamed to slots.

import { sva } from "jade-garden/sva";

const menu = sva({
  slots: {
    root: "flex flex-wrap",
    submenu: ["absolute", "flex", "overflow-visible"]
  },
  variants: {
    size: {
      xs: {},
      sm: {}
    }
  },
  compoundSlots: [
    {
      slots: ["root"],
      size: ["xs", "sm"],
      class: "w-7 h-7 text-xs"
    }
  ]
});

root({ size: "sm" }); // "flex flex-wrap w-7 h-7 text-xs"
submenu() // "absolute flex overflow-visible"

create

For the sake of convenience and performance, cva and sva functions use clsx to merge class names by default. However, if you need an escape hatch for class conflicts with a library like Tailwind Merge, or need a more performant merge algorithm like clsx/lite, you can create an instance of cva and sva with the createCVA and createSVA functions.

cva.ts
import { cn, createCVA } from "jade-garden";

export const cva = createCVA(cn);

const component = cva({
  base: "base-class",
  variants: {
    size: {
      small: "size-small",
      medium: "size-medium"
    },
    variant: {
      primary: "variant-primary",
      secondary: "variant-secondary"
    }
  },
  compoundVariants: [
    {
      size: "small",
      variant: "primary",
      class: "compound-small-primary"
    }
  ]
});

component({ size: "small", variant: "primary" }); // "base-class size-small variant-primary compound-small-primary"

class-utils

Plugin Functions require unplugin-jade-garden to work properly with Tailwind CSS.

In addition to cva and sva functions, there are utility functions that support modifying class names:

  • cm: Conditional class merging with include and exclude options.
  • cn: Alias for clsx/lite.
  • cx: Alias for clsx.
  • Plugin Functions:
    • getClasses: A function that generates class names based on a cva or sva configuration.
    • prefixClasses: A utility to simplify the maintenance of prefixed CSS classes.
    • traits: Generates CSS class names and data attributes.
class-utils/cm.ts
import { cm } from "jade-garden/class-utils";

// cm (Class Manipulator)
const specialClasses = cm(
  "button-primary focus:outline-none",
  {
    exclude: "button-primary",
    include: "custom-animation"
  }
);
// "focus:outline-none custom-animation" - removed "button-primary" and added "custom-animation"

define

Jade Garden provides extensibility and portability of your cva and sva configurations with defineCVA and defineSVA.

These helper function provide better TypeScript support and IntelliSense that makes it possible to reuse your configurations beyond any project.

cva.ts
import { cva, defineCVA } from "jade-garden/cva";

export const buttonConfig = defineCVA({
  name: "button",
  base: "rounded-full",
  variants: {
    intent: {
      primary: [
        "bg-blue-500",
        "text-white",
        "border-transparent",
        "hover:bg-blue-600"
      ],
      secondary: [
        "bg-white",
        "text-gray-800",
        "border-gray-400",
        "hover:bg-gray-100"
      ]
    },
    size: {
      small: [
        "text-sm",
        "py-1",
        "px-2"
      ],
      medium: [
        "text-base",
        "py-2",
        "px-4"
      ]
    }
  },
  compoundVariants: [
    {
      intent: "primary",
      size: "medium",
      class: "uppercase"
    }
  ],
  defaultVariants: {
    intent: "primary",
    size: "medium"
  }
});

const button = cva(buttonConfig);

TypeScript

Similar to Class Variance Authority and Tailwind Variants, Jade Garden ships VariantProps. In addition, there are other utility types that help build upon your design system.

  • ClassStrings: Represents the minimum structure to work with class names.
  • ClassValue: Represents the values that clsx can process to generate a class name string.
  • MergeFn: Represents a function that merges class names.
  • Traits: Helps define the structure for configuring and using traits.
  • VariantProps: Extracts the variant-related props from a component's overall props type.
button.ts
import type { VariantProps } from "jade-garden";
import { cva } from "./cva";

const button = cva({
  name: "button",
  base: "rounded-full",
  variants: {
    intent: {
      primary: [
        "bg-blue-500",
        "text-white",
        "border-transparent",
        "hover:bg-blue-600"
      ],
      secondary: [
        "bg-white",
        "text-gray-800",
        "border-gray-400",
        "hover:bg-gray-100"
      ]
    },
    size: {
      small: [
        "text-sm",
        "py-1",
        "px-2"
      ],
      medium: [
        "text-base",
        "py-2",
        "px-4"
      ]
    }
  },
  compoundVariants: [
    {
      intent: "primary",
      size: "medium",
      class: "uppercase"
    }
  ],
  defaultVariants: {
    intent: "primary",
    size: "medium"
  }
});

/**
 * {
 *  intent?: "primary" | "secondary";
 *  size?: "small" | "medium";
 * }
 */
type ButtonProps = VariantProps<typeof button>;

Unplugin Jade Garden

If you want to create an extensible design system with Tailwind CSS and Jade Garden, use unplugin-jade-garden to generate CSS that Tailwind can compile.

You would utilize the Plugin Functions from class-utils and define functions to generate CSS with the plugin.

See Advanced for more helpful suggestions.
vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import jadeGarden from "unplugin-jade-garden/vite";

import { alertConfig, buttonConfig } from "./src";

export default defineConfig({
  build: {
    lib: {
      entry: "./src/index.ts"
    }
  },
  plugins: [
    jadeGarden({
      styleConfigs: {
        components: [alertConfig, buttonConfig]
      },
      entry: "./styles/app.css"
    }),
    react(),
    tailwindcss()
  ]
});