Polymorphic React primitives. CVA variants, cn() class dedup, token bridge to vTheme. Semantic HTML defaults, ARIA inline, ref forwarding — no theme provider, no runtime config.
npm install @booga/vuiPeer deps: react ^18, react-dom ^18, tailwindcss ^3.4.
vUi's components emit Tailwind color-role classes (bg-primary, border-input, …). Those roles are defined by @booga/vtheme. Apply its preset so they resolve:
// tailwind.config.js
import vtheme from "@booga/vtheme/preset";
export default {
presets: [vtheme],
content: [
"./src/**/*.{ts,tsx}",
"./node_modules/@booga/vui/dist/**/*.js",
],
};Without the preset the role classes compile to nothing — components render unstyled.
import { Button, Card, CardContent, Input, cn } from "@booga/vui";
<Button variant="default" size="md">Save</Button>
<Button as="a" href="/docs">Docs</Button>
<Button asChild><a href="/docs">Docs</a></Button>
<Card>
<CardContent>
<Input defaultValue="hello" />
</CardContent>
</Card>
cn("p-2", "p-4") // → "p-4"Every component accepts as to change the rendered element. TypeScript narrows props to the target element type.
<Button as="a" href="/home">Link button</Button>
// → <a href="/home" class="...button classes...">Link button</a>asChild delegates rendering to the immediate child, merging classes and props.
<Button asChild>
<a href="/home">Link</a>
</Button>
// → <a href="/home" class="...button classes...">Link</a>variant: default | secondary | destructive | outline | ghost | link
size: sm | md | lg | icon
Button's default variant references --v-color-accent from vTheme CSS variables. Set tokens at :root via cssVars(tokens) from @booga/vtheme.
Box, Stack, Inline, Grid — flex/grid wrappers, polymorphic, extend via className.
Sheet, Dialog, Popover, Tooltip — compound components with ARIA defaults. Enhanced by vAria when installed.
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger>Open</SheetTrigger>
<SheetContent aria-label="Panel">...</SheetContent>
</Sheet>Contributions follow the Contributor Covenant 2.1.
MIT © 2026 bvasilenko