Imported from henriques4nti4go/fuse-ui (
AGENTS.md). Install upstream withnpx skills add henriques4nti4go/fuse-ui. Copyright stays with the author.
Fuse UI — Agent Guide
Style compiler + React component library. Utilities in className compile to hashed static CSS at build time (zero CSS runtime). Public npm package: fuse-ui.
Monorepo layout
packages/
fuse/ Public umbrella (re-exports) — consumers install this only
core/ Compiler (AST transform, UnoCSS engine, CSS generation)
theme/ fusion global tokens + defineConfig for fuse.config.ts
react/ React components, primitives, stories
vite/ Vite plugin (wires compiler into apps)
next/ Next.js / webpack plugin (withFuseUI wrapper)
apps/
playground/ Local demo (Vite)
storybook/ Component docs (port 6006)
Consumer setup
yarn add fuse-ui
// ── Vite / TanStack Start ──────────────────────────
// vite.config.ts
import { fuse } from 'fuse-ui/vite'
// main.tsx
import 'virtual:fuse-ui.css'
// ── Next.js ────────────────────────────────────────
// next.config.ts
import { withFuseUI } from 'fuse-ui/next'
export default withFuseUI({})
// app/layout.tsx (or pages/_app.tsx)
import 'fuse-ui/styles'
// fuse.config.ts (optional)
import { defineConfig } from 'fuse-ui/theme'
// App
import { Button, fusion } from 'fuse-ui'
import { Search } from 'fuse-ui/icons'
Scripts
| Command | Purpose |
|---|---|
yarn build:packages |
Build all packages (run before link/publish) |
yarn dev |
Playground |
yarn storybook |
Storybook on :6006 |
yarn test |
Compiler tests (packages/core) |
Component map
Each UI component lives in its own folder under packages/react/src/components/ or primitives/.
| Component | Path | Exports from fuse-ui |
Notes |
|---|---|---|---|
| Button | components/button/ |
Button, buttonVariants |
CVA variants: primary, secondary, outline, ghost; sizes: sm, md, lg. Radix Slot via asChild. |
| Input | components/input/ |
Input, inputVariants |
Sizes: sm, md. Optional icon + iconPosition (left | right). |
| Icon | components/icon/ |
Icon |
Wraps Lucide: <Icon icon={Search} />. Types: LucideIcon, LucideProps. |
| Dialog | components/dialog/ |
Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose, DialogOverlay, DialogPortal |
Radix Dialog. Styles are inline string literals in dialog.tsx. |
| Select | components/select/ |
Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, SelectSearch, SelectEmpty, selectTriggerVariants |
Radix Select. Search: add <SelectSearch /> inside SelectContent. Items show check on selected. Select/SelectGroup/SelectValue re-exported from Radix via index.ts. |
| Stack | primitives/stack/ |
Stack |
Layout primitive (flex column/row). |
| Surface | primitives/surface/ |
Surface |
Card-like container; uses global fusion.surface. |
Folder structure (per component)
components/button/
button.tsx Component
button.types.ts Props + variant types
button.variants.ts CVA definitions (inline Tailwind strings)
button.stories.tsx Storybook
index.ts Barrel export
Icons
- Lucide re-exported at
fuse-ui/icons(packages/react/src/icons/index.ts) - Internal components import from
../../icons/index.js - Do not use a name-based icon registry — pass
LucideIconto<Icon icon={...} />
Global design tokens (fusion)
File: packages/theme/src/fusion.ts
| Token | Use |
|---|---|
fusion.page |
Page background/layout |
fusion.surface |
Surface primitive |
fusion.heading |
Page headings |
fusion.subheading |
Muted subheadings |
Component-specific styles live co-located as string literals in *.variants.ts or *.tsx — not in fusion.ts.
Fusion style
Default palette uses zinc neutrals with black primary (primary = zinc-900).
| Token | Value |
|---|---|
primary |
#18181b (zinc-900) — buttons, focus ring, selected check |
primary-dark |
#09090b (zinc-950) — hover on primary |
| Page bg | zinc-50 |
| Text | zinc-950 / muted zinc-500 |
| Borders | zinc-200 |
| Fields hover | zinc-50 / zinc-100 |
Form fields: border-solid border-zinc-200 rounded-md focus-visible:ring-primary
Button primary: bg-primary text-zinc-50 (black button, white text)
Use zinc-* not gray-* for new components.
Styling rules (compiler)
The Fuse compiler (packages/core) only compiles:
- String literals in
className,cn(), andcva() fusion.*member expressions
Do not extract classes to const variables — the compiler won't resolve them. Group with cn():
cva(cn(
'flex w-full',
'rounded-md border border-solid border-zinc-200',
))
Variants (hover:, focus:, disabled:, responsive) are supported. Vite plugin must process .ts and .tsx files (including *.variants.ts).
Conventions for new components
- Create folder under
components/<name>/with*.tsx,*.types.ts,*.variants.ts(if variants),*.stories.tsx,index.ts - Use Radix primitives when accessibility matters (Dialog, Select pattern)
- Use CVA + inline Tailwind strings for variants
- Form fields:
border border-solid border-zinc-200,rounded-md,focus-visible:ring-primary - Export from
packages/react/src/index.ts(auto-available viafuse-ui) - Exclude
*.stories.tsxfromtsconfig.build.json - Fast Refresh:
.tsxfiles should export only React components (move Radix re-exports toindex.ts)
Internal packages (do not publish separately)
| Package | Role |
|---|---|
@fuse-ui/core |
transformSource, CSS generation, tests |
@fuse-ui/react |
Component implementation |
@fuse-ui/theme |
fusion, defineConfig, theme types |
@fuse-ui/vite |
fuse() plugin, virtual:fuse-ui.css, loads fuse.config.ts |
@fuse-ui/next |
withFuseUI() Next.js wrapper, webpack loader + plugin |
Local development link
# in fuse-ui monorepo
yarn build:packages && cd packages/fuse && yarn link
# in consumer project (package.json must list "fuse-ui": "*")
yarn link fuse-ui
Alternative: "fuse-ui": "file:../fuse-ui/packages/fuse" in consumer package.json.