---
name: paubha
description: Use whenever building or modifying a React + Tailwind CSS UI and the project uses (or should use) Paubha, a shadcn-style, copy-paste component registry. Trigger on requests to add a button/input/modal/dialog/select/table/dropdown/toast/card/etc. to a React app, to theme or restyle a React+Tailwind project, to scaffold a design system, or when a repo already has a components.json pointing at paubha.tech. Covers installing components via the CLI, using Paubha's design tokens correctly, and following its component API conventions. Not for non-React frameworks or projects that have explicitly opted out of Paubha.
license: MIT
---

# Paubha

React components, styled with Tailwind, shipped shadcn-style: `npx paubha add <name>` copies real source into your project. No package to update, no version to fight, nothing you can't just open and change.

Docs: https://paubha.tech · Repo: https://github.com/dipendrashrest/paubha · License: MIT · CLI: `paubha@0.5.1` · Design source: the Paubha-UI Figma file (v1). Components, tokens and states match it.

## When to use this

You're about to hand-roll a button, input, modal, dropdown, table, toast, card. Stop and check the registry first. If the project's `components.json` already points `registry` at `.../r`, Paubha is the default source for UI here, full stop, not a suggestion.

## Install

```bash
npx paubha@latest init          # tokens.css, theme.css, lib/cn.ts, components.json, shared deps
npx paubha@latest add button    # copies one component
npx paubha@latest add field avatar tabs   # or a batch
```

`add` walks `registryDependencies` for you: `add field` also pulls `input` because `Field` composes it. Already have the file? `add` no-ops it unless you pass `--force`.

Resulting layout:

```
components.json          # registry URL, path aliases, which token files exist
components/ui/<name>/    # one folder per component, yours to edit
lib/cn.ts                # clsx + tailwind-merge helper every component imports
styles/tokens.css        # primitives + semantic tokens
styles/theme.css         # Tailwind v4 @theme mapping
```

Imports inside the copied files are plain relative paths (`../../../lib/cn`), computed by the CLI to match wherever your `aliases.components`/`aliases.lib` actually put things. Nothing to configure, nothing to break if you move folders around. The CLI recomputes depth on every `add` instead of baking in an assumption. You don't need a `@/*` tsconfig alias for this to work.

## What's actually in the registry

Don't trust a hardcoded list here or anywhere else, the catalog moves. Hit the index:

```
https://paubha.tech/r/registry.json
```

Every entry has `name` (what `add` takes), `title`, `description`, real `files`. Want to eyeball a component's actual source before installing it? `https://paubha.tech/r/<name>.json`.

Snapshot (registry v0.5.0, 74 items): base components (Button, Input, Field, Textarea, Checkbox, Radio Group, Switch, Select, Avatar, Badge, Alert, Toast, Modal, Dialog, Dropdown Menu, Popover, Tooltip, Tabs, Accordion, Card, Table, Pagination, Progress Bar, Progress Circle, Slider, Spinner, Skeleton, Divider, Breadcrumbs, Kbd, Tag Input, Toggle Group, Verification Code Input, Logo) plus application patterns built from them (Activity Feed, Announcement Bar, App Nav, Auth Card, Blog Card, Calendar, Card Header, Chart, CLI Snippet, Comment, Confirm Dialog, Cookie Banner, Data Toolbar, Date Picker, Empty State, FAQ, Feature List, File Upload, Filter, Icon List, Inline CTA, Logo Cloud, Marketing Hero, Metric, Newsletter, Page Header, Pricing Card, Progress Steps, Search Field, Section Header, Settings Row, Site Footer, Team Card, Testimonial, User Menu). Some base components ship a `-v2` sibling too (`select-v2`, `popover-v2`, `accordion-v2`, `pagination-v2`, `tag-input-v2`). These are real, separately maintained, restrained variants, not stale duplicates. Patterns compose base components: install one and the CLI pulls in what it needs.

## Design tokens: bind to these, never to primitives

Every component reaches for a semantic class, never a raw hex or an arbitrary value that "looks right." Do the same in anything you write around these components. This is the only reason dark mode and re-theming stay a token remap instead of a find-and-replace across every file.

**Backgrounds:** `bg-primary` `bg-secondary` `bg-tertiary` `bg-elevated` `bg-preview` `bg-overlay` `bg-disabled` `bg-secondary-hover` `bg-tertiary-hover` `bg-switch-off` · chips (badge-style fills): `bg-brand-chip` `bg-info-chip` `bg-success-chip` `bg-warning-chip` `bg-error-chip` · brand: `bg-brand-solid` `bg-brand-solid-hover` `bg-brand-solid-active` `bg-brand-subtle` · status (error/warning/success/info all follow `-solid` / `-solid-hover` / `-subtle`): `bg-error-solid` `bg-error-solid-hover` `bg-error-solid-active` `bg-error-subtle` `bg-warning-solid` `bg-warning-subtle` `bg-success-solid` `bg-success-subtle` `bg-info-solid` `bg-info-subtle`

**Foreground:** `fg-primary` `fg-secondary` `fg-tertiary` `fg-disabled` `fg-brand` · on-solid: `fg-on-brand` `fg-on-error` `fg-on-warning` `fg-on-success` `fg-on-info` · links: `fg-link` · status: `fg-error` `fg-warning` `fg-success` `fg-info`

**Borders:** `border-default` `border-strong` `border-disabled` `border-brand` `border-error` `border-warning` `border-success` `border-info`

**Focus:** `shadow-[var(--shadow-glow-focus)]` on every interactive component's focus-visible state: a 4px brand-tinted glow, not a hard outline. Destructive and invalid states use the error variant (`focus-ring-error`). Non-negotiable brand signature. Hand-rolling something interactive? Match it. Don't fall back to the browser default.

Light/dark mappings exist for all of the above already. You never write a `dark:` variant for color on top of a semantic token; the token itself already flips per mode.

**Also available the same way:** spacing (`space-0`…`space-10xl`, plus a `space-2xs` 2px micro tier), radius (`radius-none/xs/sm/md/lg/xl/full` = 0/4/8/12/16/20/9999px), type scale (`text-ui-*` `text-body-*` `text-display-*`, role-based, not generic `sm`/`md`/`lg`), plus layering (`--z-*`) and motion (`--duration-*`) variables.

## Component API: the shape you can rely on

These hold across the catalog, but **the installed file's own prop types are the actual source of truth, not this list.** Docs and reality have drifted here before and gotten caught. When in doubt, open the file `add` just wrote and read it.

- Native props extended, refs forwarded: `React.ComponentPropsWithRef<"button">` etc.
- CVA (`class-variance-authority`) + a shared `cn()` for variants. Your `className` always wins.
- `variant` is a lowercase string in code (`variant="primary"`). `size` is `sm`/`md`/`lg`/`xl`, and form-row components (Button, Input, Select) also take `2xl`. They share one height scale: 32 / 40 / 48 / 56 / 64px. Booleans are camelCase (`showIcon`, `dismissible`). Instance-swap slots are camelCase (`leadingIcon`, `avatar`).
- Multi-part components compose: `<Dialog><DialogTrigger/><DialogContent/></Dialog>`, not one component drowning in fifteen props.
- Controlled/uncontrolled both work where it makes sense: `value` / `defaultValue` / `onValueChange`.
- Overlay/interaction components (Select, Dialog, Dropdown Menu, Tooltip, Popover, Accordion, Switch, Checkbox, Slider, Toast, Tabs) sit on Radix primitives. Expect real Radix behavior (focus trapping, typeahead, positioning) even where the wrapper hides it.
- **`asChild` is not everywhere.** It's real on `Select` (comes from the underlying Radix primitive). `Button` has no `asChild` at all. Need a button-styled link? Import `buttonVariants()` and slap the classes on a real `<a>`/`<Link>`. Don't assume `asChild` exists just because you've seen it on other component libraries.
- **Things that changed in the v1 sync, so stale code may be wrong:** Button variants are `primary` / `secondary` / `tertiary` / `link` (`ghost` is gone) and `destructive` is a boolean prop, not a variant. Avatar uses `indicator` (not `status`) and a `type` prop. Badge's root is a plain `<span>` with no default `role="status"`. Card radius is 8px. Checkbox's checked state is outlined, not solid. Date Picker stages a picked day and only commits (and closes) on Apply. Logo takes a pixel `size`.
- Lucide (`lucide-react`) only. Matches the Figma file 1:1. Don't mix in a second icon set next to these components.

## Known gotchas: don't rediscover these

Real bugs that have shipped and gotten fixed in this registry. If you're extending or writing a component, check for the same shape:

- **Prop name collides with a native HTML attribute.** A component extends `React.ComponentPropsWithRef<"div">` (or `"figure"`, `"section"`, etc.) and also declares its own `title`/`role` prop with an incompatible type (e.g. `title?: React.ReactNode` vs. the native `title?: string`). TypeScript won't warn you about this on its own; it silently picks whichever wins. Fix: `Omit<React.ComponentPropsWithRef<"div">, "title" | "role">` before adding your own. Same applies to `size` on `<input>`/`<select>`, `color`, `translate`.
- **Relative import depth baked in instead of computed.** Registry source imports shared modules via a self-referencing package specifier (`@paubha/registry/lib/cn`), not a relative path. The *installed* file lives at a different folder depth than the *source* file, so a hardcoded `../../lib/cn` silently breaks the moment either side's folder structure changes. The CLI, not the source file, is what knows the real destination depth.
- **`registryDependencies` drift.** If component A imports component B (`../accordion/accordion`), A's registry entry must declare `"registryDependencies": ["accordion"]` or `add a` never pulls B in and the copied file 404s on import. This gets checked mechanically now, but if you're hand-editing `registry.json`, don't forget it.

## Accessibility

Every interactive component ships the correct ARIA role, documented keyboard behavior (WAI-ARIA APG), a real focus state, and a passing `vitest-axe` check with zero violations across its states. You don't need to bolt on ARIA yourself when using these as-is, but if you override rendering or inject custom children, preserve what's already there instead of clobbering it.

## Before you hand-build anything

1. Does Paubha already have this? Check `https://paubha.tech/r/registry.json`.
2. Yes → `npx paubha@latest add <name>`, then go read the file it wrote. That's the real API.
3. Styling something adjacent (a wrapper, a one-off layout)? Reach for the semantic tokens above, not a hex code.
4. Installed behavior doesn't match what you expected? Trust the file on disk over any cached assumption. This registry moves, and things do get corrected out from under stale docs.
