Skip to content

Pro pre-sale is open. Installation opens January 2027. See Pro

Docs

Using components

Components are source files in your project, not a package you update. This page covers how to use them and how to style the code around them so it matches.

Import a component

After npx werewolf-ui add card button field input, the files sit under your UI alias, by default @/components/ui. Import them like any other file in your app.

import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

export function ProjectCard() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Project</CardTitle>
        <CardDescription>Last edited 2 hours ago.</CardDescription>
      </CardHeader>
      <CardContent>
        <Field>
          <FieldLabel>Name</FieldLabel>
          <FieldControl>
            <Input placeholder="My project" />
          </FieldControl>
        </Field>
      </CardContent>
      <CardFooter>
        <Button variant="secondary">Cancel</Button>
        <Button>Save</Button>
      </CardFooter>
    </Card>
  )
}

The code is yours

Change a component file when you need to. Running add again skips identical files and reports a conflict for any file that differs, so your change is never lost. To take the new version, review the difference, then pass that exact path to --overwrite.

npx werewolf-ui add button --overwrite src/components/ui/button.tsx

Pick one size per region

Controls take size: sm, md (the default), lg or xl. Choose the size for a region, not per control, and never put two neighboring sizes in one row. On touch screens md, lg and xl grow on their own, so do not change sizes at a breakpoint.

SizeMouseTouchUse for
sm28px28px, 44px hit areaToolbars, tables, dense menus
md32px36pxEverywhere by default
lg36px44pxRoomy forms and settings
xl44px48pxThe primary action, touch-first screens
<div className="flex gap-sm">
  <Button size="sm" variant="ghost">Filter</Button>
  <Button size="sm" variant="ghost">Sort</Button>
</div>

<Button size="xl" fullWidth>Continue</Button>

Style with token classes

For your own layout, use the Tailwind classes that the token stylesheet adds. A color class is the utility plus the token name without color-: bg-background-surface, text-text-secondary, border-border. Spacing is p-md, gap-sm, mt-xs; corners are rounded-element and rounded-container; type is text-sm or text-heading-3.

<section className="rounded-container border border-border bg-background-surface p-lg">
  <h2 className="text-heading-3 text-text-primary">Billing</h2>
  <p className="mt-xs text-sm text-text-secondary">Your plan renews on May 1.</p>
</section>

Tailwind's own palette, radius, shadow and font weight scales are cleared, so palette classes (a hue with a number), rounded-lg and font-bold produce no CSS. Never hardcode a color, as a hex value or a color function. If no token fits, report it instead of inventing one. The Tokens page lists every name.

Text on colored fills

  • On the accent fill, text and icons use only text-text-on-accent and text-icon-on-accent. With the default accent, text-text-primary on bg-accent is invisible in dark mode.
  • Badges, tags and banners use a soft fill with its matching text: bg-background-green text-text-green.
  • Hover and pressed states are overlays on the normal fill (hover:overlay-hover active:overlay-pressed with transition-control), not new colors.
  • Make text quieter with text-text-secondary or text-text-tertiary, never with opacity.

Dark and light mode

Dark is the default. Set data-theme="light" on <html> for a light page, or on any element for one region. Tokens switch on their own, so never write dark: variants.

<html lang="en" data-theme="light">

<div data-theme="dark">Always dark, even on a light page.</div>

Add classes to a component

Every component accepts className. The utils item that add installs provides cn, which merges classes and knows the token class names, so your class wins over the component's default.

import { cn } from "@/lib/utils"

<Button className={cn("w-full", isPrimary && "shadow-med")}>Save</Button>

Loading states

Never blank content while the next content loads. Keep it on screen with the pending prop (Table, TabsContent), which dims it and marks it busy. Use a Skeleton only for a first load, shaped like the content, through useSkeletonDelay so it shows after about 150ms and for at least 300ms.

Icons

Components and docs use Tabler Icons, but any icon set works. Always pass a size: 16px in sm, md and lg controls, 20px in xl controls and navigation. See Iconography.

Check your code

Run lint after you or your agent change UI. It flags hardcoded colors, cleared Tailwind classes, dark: variants, unknown tokens and arbitrary spacing, and --fix applies the edits it can prove.

npx werewolf-ui lint src --fix