Installation
Install the package, import the stylesheet, and wire the app shell.
Copy this prompt into Codex or another coding agent. It installs the package, initializes the bundled skill, and uses that skill to configure your app.
Prompt
Install and initialize raft-ui in this repository.
1. Inspect the repository and use its existing package manager. Do not change package managers or lockfile format.
- Single-package repository: install the latest stable raft-ui as a dependency.
- Monorepo: install raft-ui in the target app workspace.
2. At the project or workspace root, add raft-ui to package.json under intent.skills without removing existing entries, then run the package-manager equivalent of @tanstack/intent@latest install --map.
3. Load raft-ui#raft-ui-guide and follow it for this task. Run intent load raft-ui#raft-ui-guide --path before opening rules/setup.md or another linked rule, then resolve that file relative to the returned SKILL.md directory.
4. The package also ships raft-ui#raft-ui-critique for visual and experience reviews; do not load it unless the task includes a critique.
5. Follow the skill to initialize raft-ui: wire Tailwind CSS v4, import raft-ui/styles.css once, decide whether to load raft-ui/fonts.css separately, add ThemeProvider, and add TooltipProvider or ToastProvider only when the app needs them.
6. Put the application inside an isolated layout root so body portals stack above app content. Keep default body portals unless this is an iframe, preview, or embedded shell.
7. Inspect node_modules/raft-ui/dist/index.d.mts before using any prop, part, or variant you have not verified. Do not guess APIs or import from raft-ui/wip unless asked.
8. Validate the changed files with the repository's existing checks.
When finished, summarize the files changed, the Intent skill loaded, the providers added, and the validation results.Load the package source hint and raft-ui/styles.css once from your global stylesheet, in that order before @import "tailwindcss". This step assumes you already have Tailwind v4 wired into your build.
- Add the global stylesheet.
src/styles.css
@import "tailwindcss"; @import "raft-ui/styles.css"; @source "../node_modules/raft-ui/dist/**/*.{js,mjs}";@sourceis relative to this CSS file, andTailwind v4does not scannode_modulesby default. Adjust the path when your global stylesheet lives deeper in the app.src/styles.css
@source "../../node_modules/raft-ui/dist/**/*.{js,mjs}"; - Load fonts (optional).
Font loading and default family assignments are intentionally separate from
raft-ui/styles.css. Loadraft-ui/fonts.cssas its own stylesheet if you want the package default font setup. Do not nest it inside your main CSS file; it contains a font@import. If you prefer self-hosted fonts, skip this file and assign your own--heading-font,--sans-font, and--mono-fontvalues.src/main.tsx
import "raft-ui/fonts.css"; import "./styles.css";
Set up providers once near the root.
- Wrap the app.
ThemeProvidersyncs theme attributes to the document root. AddTooltipProviderwhen the app usesTooltip. AddToastProvideronce when the app uses toast feedback.Dark mode is class-driven: the provider resolves the system preference and stamps the
.lightor.darkclass on the root. Server-rendered apps that persist a mode should replay it with a small inline script before first paint (thenext-themespattern) so system-dark visitors do not flash light.src/App.tsx
import { ThemeProvider, ToastProvider, TooltipProvider } from "raft-ui"; export function App() { return ( <ThemeProvider> <ToastProvider> <TooltipProvider> <YourApp /> </TooltipProvider> </ToastProvider> </ThemeProvider> ); } - Isolate the layout root.
Base UIportals render floating surfaces such asDialog,Popover,Select,DropdownMenu, andTooltipat the document body. AddTailwind'sisolateclass to the application root so portaled surfaces stack above page content instead of competing with app-levelz-indexvalues. Keep the default body portal behavior unless the app renders inside aniframe, preview frame, or embedded shell.layout.tsx
<body> <div className="isolate"> {children} </div> </body>styles.css
.root { isolation: isolate; }If the host layout does not use
Tailwind, apply the same CSS withisolation: isolate.
Import components from the package.
src/App.tsx
import { Button } from "raft-ui";
export function App() {
return <Button>Save</Button>;
}Optional: theme switching, reading, and customization after the core setup.
Switch themes
Use setTheme. Pass a mode only when the family is elegant: light, dark, or system.
src/ThemeSwitcher.tsx
import { Button, useTheme } from "raft-ui";
export function ThemeSwitcher() {
const { setTheme } = useTheme();
return (
<div>
<Button onClick={() => setTheme("brutal")}>Brutal</Button>
<Button onClick={() => setTheme("elegant", { mode: "light" })}>Elegant light</Button>
<Button onClick={() => setTheme("elegant", { mode: "dark" })}>Elegant dark</Button>
<Button onClick={() => setTheme("elegant", { mode: "system" })}>Elegant system</Button>
</div>
);
}Read theme state
Read the active family, selected mode, and resolved mode from useTheme.
src/ThemeStatus.tsx
import { useTheme } from "raft-ui";
export function ThemeStatus() {
const { theme, mode, resolvedMode } = useTheme();
return (
<p>
{theme} / {mode} / resolved {resolvedMode}
</p>
);
}Customize with data-theme
Prefer semantic tokens. Use data-theme variants only for family-specific differences.
src/BillingCard.tsx
<section
className={cn(
"rounded-lg border border-line bg-layer-panel p-4 text-foreground-strong shadow-raft-sm",
"[[data-theme=brutal]_&]:rounded-none [[data-theme=brutal]_&]:border-2 [[data-theme=brutal]_&]:border-black",
"[[data-theme=elegant]_&]:shadow-raft-md",
"[[data-theme=elegant].dark_&]:bg-layer-popover",
)}
>
Billing
</section>Optional: add ToastProvider near the app root, then call toast.info, toast.success, toast.warning, or toast.error from event handlers.
src/App.tsx
import { ThemeProvider, ToastProvider, TooltipProvider } from "raft-ui";
export function App() {
return (
<ThemeProvider>
<ToastProvider timeout={3600} limit={3}>
<TooltipProvider>
<YourApp />
</TooltipProvider>
</ToastProvider>
</ThemeProvider>
);
}src/CopyLinkButton.tsx
import { Button, toast } from "raft-ui";
export function CopyLinkButton() {
return (
<Button
onClick={() => {
toast.success("Link copied.", {
description: "Ready to paste into the thread.",
action: {
label: "Undo",
onClick: () => toast.dismiss(),
},
});
}}
>
Copy link
</Button>
);
}raft-ui ships raft-ui-guide for implementation and raft-ui-critique for interface review. TanStack Intent discovers both from the installed package, including dependencies of pnpm workspace packages.
package.json
{
"intent": {
"skills": ["raft-ui"]
}
}Shell
pnpm dlx @tanstack/intent@latest install --map
pnpm dlx @tanstack/intent@latest load raft-ui#raft-ui-guideAvailable skills
pnpm dlx @tanstack/intent@latest load raft-ui#raft-ui-guide
pnpm dlx @tanstack/intent@latest load raft-ui#raft-ui-critiqueUsing skills-npm
The same package remains compatible with skills-npm and other standard Agent Skill installers. skills-npm 1.2.0 has a known limitation when recursively aggregating multiple skills from one package in a monorepo, so prefer Intent for that layout or run skills-npm non-recursively where raft-ui is a direct dependency.
Shell
pnpm add -D skills-npm
pnpm exec skills-npm setup