Expressive

Getting started

Installation

Set up Expressive in a React project that uses Tailwind CSS v4. It takes one command, plus one more per component.

Requirements

  • React 19 and TypeScript.
  • Tailwind CSS v4, with a CSS file that contains @import "tailwindcss".
  • Next.js, Vite, React Router / Remix, TanStack Start or Astro (with React islands).

1. Initialize

Run this in your project root:

npx @grenishrai/expressive init

It asks for six things, all changeable later:

  • Seed color: the hex color your whole scheme is generated from.
  • Color scheme: how the seed is interpreted, from expressive (the default) to tonal-spot or monochrome.
  • Font: any font family. Variable fonts work best.
  • Motion scheme: expressive, standard or fluid (soft and gooey; needs Motion or GSAP).
  • Motion engine: CSS (no dependency), Motion or GSAP.
  • Corner scale: Material's default, softer, or sharper.

Then it:

  • writes expressive.json with your choices and where files go;
  • copies the tokens and helpers into lib/expressive/;
  • adds expressive.css next to your global CSS and imports it after Tailwind;
  • generates your theme's CSS variables into the global CSS file;
  • installs the npm packages those files use.

2. Add components

npx @grenishrai/expressive add button dialog switch

Components land in components/expressive/, with imports rewritten to your path aliases (or to relative paths if you don't use aliases). Anything a component depends on is added too.

app/page.tsx
import { Button } from "@/components/expressive/button";

export default function Page() {
  return <Button>Get started</Button>;
}

3. Load your font

The theme refers to your font by name; loading it is up to you. In Next.js, use next/font and pass its CSS variable as the font:

app/layout.tsx
import { Geist } from "next/font/google";

const geist = Geist({ subsets: ["latin"], variable: "--font-geist" });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={geist.variable}>
      <body>{children}</body>
    </html>
  );
}
npx @grenishrai/expressive theme --font "var(--font-geist)"

Dark mode

The dark scheme applies under a .dark class on any ancestor, usually <html>. Toggle it however you like, for example with next-themes set to attribute="class".

Run from source

Until the CLI is on npm:

git clone https://github.com/Grenish/expressive.git
cd expressive
pnpm install
pnpm --filter @expressive/registry build   # builds the registry JSON
pnpm --filter @grenishrai/expressive build             # builds the CLI

# in your project (uses the hosted registry):
node /path/to/expressive/packages/cli/dist/index.js init

To work against your local checkout instead, add --registry /path/to/expressive/apps/www/public/r. The registry location is saved in expressive.json, so later add commands find it on their own.

Troubleshooting

  • “Could not find a CSS file with @import "tailwindcss"”: set up Tailwind v4 first, or point at your CSS file with --css src/styles.css.
  • Styles look unthemed: make sure expressive.css is imported after Tailwind, and that starter styles (like a body background from a project template) aren't overriding the theme.