About
Use raft-ui
How to install raft-ui, set up its stylesheets, Tailwind CSS v4 and dark mode, render a first component, upgrade between versions, and where the tokens and the agent entry points are.
raft-ui is the React implementation of the Raft Design System, published as @botiverse/raft-ui. It contains the CSS foundations (the tokens as CSS variables), which work in plain HTML, and React components built on Base UI and Tailwind CSS v4. Product code uses raft-ui instead of its own copies of these components.
Install
raft-ui needs React 19 and, for the components, Tailwind CSS v4.
pnpm add @botiverse/raft-uiThe package's entry points:
| Import | Contains |
|---|---|
@botiverse/raft-ui | Stable components, providers and hooks |
@botiverse/raft-ui/wip | Experimental components; their API can change in any release |
@botiverse/raft-ui/cn | The class merger, cn |
@botiverse/raft-ui/styles.css | The Tailwind CSS v4 integration; it imports foundation.css |
@botiverse/raft-ui/foundation.css | The theme variables alone, for pages without React or Tailwind |
@botiverse/raft-ui/fonts.css | Optional web fonts: Space Grotesk and Space Mono |
@botiverse/raft-ui/tokens.json | Every token in the Design Tokens Community Group format |
Set up the styles
With React and Tailwind CSS v4
Import Tailwind and raft-ui's styles in your global stylesheet, and point Tailwind at the installed package so it generates the utilities the components use:
@import "tailwindcss";
@import "@botiverse/raft-ui/styles.css";
@source "../node_modules/@botiverse/raft-ui/dist/**/*.{js,mjs}";@source is relative to this CSS file and must resolve to the installed package.
Import the fonts from the app entry:
import "@botiverse/raft-ui/fonts.css";fonts.css starts with a web-font @import. If you import it from a stylesheet instead, put it first in that file, because a CSS @import must come before other rules. To use other fonts, set --heading-font, --sans-font and --mono-font.
Without React or Tailwind
Link foundation.css, or copy it beside your HTML. It defines the same theme variables the components use, without component markup, interactions or a CSS reset:
<link rel="stylesheet" href="./foundation.css" />
<style>
body {
background: var(--layer-canvas);
color: var(--foreground);
}
</style>Theme and dark mode
Raft has one visual language, in light and dark mode; components adapt to the mode themselves. Pick the mode with a class on the root: class="light" or class="dark". Without either, the page follows the system setting.
In a React app, wrap the app in ThemeProvider. It sets the mode class and color-scheme on document.documentElement.
import { ThemeProvider } from "@botiverse/raft-ui";
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider defaultMode="system" storageKey="theme">
{children}
</ThemeProvider>
);
}modeordefaultModeislight,darkorsystem; the default islight.onModeChangereports changes, andstorageKeykeeps an uncontrolled mode inlocalStorage.useTheme()returnsmode,resolvedMode(lightordark, withsystemresolved) andsetMode.- A nested scope, such as a preview, uses
syncDom={false}and puts the matchinglightordarkclass on its own root. - An app rendered on the server that keeps the mode has to restore it before the first paint, or the page flashes the other mode.
Two more providers are needed only with their components: TooltipProvider for tooltips and ToastProvider for the toast API (Toast).
Base UI overlays portal to document.body. Give the app root isolation: isolate so its stacking contexts stay below them.
A first component
import { Button } from "@botiverse/raft-ui";
export function CreateTaskButton() {
return <Button variant="primary">Create Task</Button>;
}variant="primary" is the pink sticker for the one action a screen exists for; every other action uses the default variant. Each component's page lists its variants, parts and props, generated from the code, and its rules. Check a prop there or in node_modules/@botiverse/raft-ui/dist/index.d.mts rather than guessing it: names differ between components (the negative intent is danger on Button and destructive on Banner).
Two rules hold for everything you build with raft-ui:
- Use real elements. Anything clickable is a
<button>or an<a>with anhref; checkboxes and radios hold a native input; a switch is a button withrole="switch". - Give every icon-only button an
aria-label.
Which component to use for which job is in the components overview and Choosing a component.
Versions and upgrading
raft-ui is pre-1.0: a minor version can include breaking changes. Each release lists them, with what to change, in the changelog.
A component's status follows where it is exported:
| Status | Exported from | Meaning |
|---|---|---|
| Stable | @botiverse/raft-ui | Breaking changes follow the versioning above |
| Experimental | @botiverse/raft-ui/wip | The API can change in any release |
| Deprecated | @botiverse/raft-ui, marked @deprecated | Still works; its page names the replacement. Removed in the next major version |
Codemods
The package ships two codemods. Up to 0.6.0 the package was published as raft-ui; to move a project to @botiverse/raft-ui and the current token names:
pnpm add @botiverse/raft-ui
node node_modules/@botiverse/raft-ui/codemods/package-name.mjs src
node node_modules/@botiverse/raft-ui/codemods/token-names.mjs src
pnpm remove raft-uipackage-name.mjsrewrites imports, CSS@importand@sourcepaths and the dependency. It lists any bare"raft-ui"strings (bundler aliases, path mappings) for you to change by hand.token-names.mjsrewrites Tailwind utilities andvar(--…)references from the former token names to the current ones. The former names keep working as deprecated aliases until 1.0.
After token-names.mjs, review two kinds of code by hand:
- Guards that forbid a name. A lint rule, a test or an allowlist that bans a former name has that name rewritten too, so it now bans the current one. Put the former name back there.
- Patterns built from parts. A regular expression that assembles a token name from parts isn't a literal token, so it keeps the former name and stops matching the rewritten source. Widen it to the current name.
If a package you depend on declares raft-ui as a peer dependency, also install "raft-ui": "npm:@botiverse/raft-ui@<version>", so it resolves to the same copy instead of bundling a second one.
Tokens
@botiverse/raft-ui/tokens.json holds every color, elevation and metric token in the Design Tokens Community Group format, with light and dark values. It is the single source for token values: foundation.css is generated from it, and so are the token tables in these docs. Use it for design tools and code generators.
Which token to use for what is in the foundations, starting with Color.
For agents
Every page of these docs is available to agents as Markdown:
llms.txtlists every page with its one-line summary.- Any page as Markdown: its path plus
.md, for examplehttps://raft-design.botiverse.build/components/button.md. A component page includes its API, generated from the code. components.jsonlists every component with its status, other names, summary and doc URLs.- Agent Skills ship in the npm package and match its version:
raft-ui-guidefor adopting raft-ui and looking up its API, andraft-ui-critiquefor visual, interaction, accessibility and light and dark review.
The skills use the standard skills/*/SKILL.md layout. With TanStack Intent, allow them in the consuming project's package.json:
{
"intent": {
"skills": ["@botiverse/raft-ui"]
}
}Then install the project guidance and load a skill:
pnpm dlx @tanstack/intent@latest install --map
pnpm dlx @tanstack/intent@latest load @botiverse/raft-ui#raft-ui-guide
pnpm dlx @tanstack/intent@latest load @botiverse/raft-ui#raft-ui-critiqueOther installers that read the same layout, such as skills-npm or Vercel skills, work too.
Related
- Designing a new feature: how to choose and compose components for new work.
- Components overview: every component, by what it is for.
- Package README: the same setup, next to the code.