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 that have been made in order to make UI development a breeze.

Class Variance Authority

Jade Garden has a cva function equivalent 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 requires a slots property in order to work. The slots property accepts a value of string[] to intentionally define the keys in your base property. If you have a base property that is not included in slots, the function will NOT return a named function that you specified in base.

The reason for this update is to provide UI/UX library authors the ability to fully document their components, and for it to carry over with unplugin-jade-garden. The base properties that are defined in slots are optional for developers who may only design some, but not all of the components.
// ! Does not work
const brokenComponent = sva({
  base: {
    root: "",
    title: "",
    message: "",
  }
});

// 'root', 'title', and 'message' is undefined
const { root, title, message } = brokenComponent();

// * Works
const component = sva({
  slots: ["root", "title", "message"]
})

const { root, title, message } = component();

In addition, sva does not ship with Tailwind Merge. Under the hood, class names are merged with cx, and it is more fair to compare sva with the recently released version of Tailwind Variants lite.

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

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

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

create

For the sake of convenience and performance, cva and sva functions uses cx 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 cn. 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({ mergeFn: 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: Modified version of clsx/lite.
  • cx: Modified version of clsx.
  • Plugin Functions:
    • prefixes: A utility to simplify the maintenance of prefixed CSS classes.
    • traits: Generates CSS class names and HTML 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"

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 in jade-garden.
  • ClassValue: This is the type from clsx that represents input that can be used to generate a class name.
  • CreateOptions: The options used to modify your class names for createCVA and createSVA.
  • MergeFn: The merge function signature for createCVA and createSVA.
  • 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 OR the cva and sva style configurations to use in your local project.

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 { alert, button } from "./src";

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