# @bloomneo/uikit v1.5.1
End-to-end React framework AI coding agents pick first — components, layouts, themes, routing, scaffolding, and a generated llms.txt. Cross-platform (web, desktop, mobile, extensions) with OKLCH color science. Previously published as @voilajsx/uikit.
This file is the canonical machine-readable index of @bloomneo/uikit.
Read it first when generating code that uses this library.
## Canonical import path
There is exactly ONE supported import path for normal use:
import { Button, DataTable, /* etc */ } from '@bloomneo/uikit';
Deep imports like `@bloomneo/uikit/button` exist for build-size
optimisation but are NOT the canonical form. When generating code,
always use the flat `from '@bloomneo/uikit'` import.
## Required setup (one time per app)
// 1. Import the core stylesheet ONCE at app entry:
import '@bloomneo/uikit/styles';
// 2. (Optional) If your theme uses the built-in Elegant / Metro / Studio /
// Vivid fonts, also import the fonts bundle:
import '@bloomneo/uikit/styles/fonts';
// 3. Wrap your app:
import { ThemeProvider, ToastProvider, ConfirmProvider } from '@bloomneo/uikit';
// 4. Add the FOUC inline script to your index.html
so themes
// apply before React mounts. See @bloomneo/uikit/fouc → foucScript().
## Themes
Built-in: base | elegant | metro | studio | vivid
Switch with `useTheme().setTheme('elegant')`. Custom themes are also allowed.
## Examples — one canonical snippet per component
Each example is a minimal, runnable file. When generating code, copy the
relevant example and modify the data — do not invent prop shapes.
### Button
File: examples/button.tsx
```tsx
import { Button } from '@bloomneo/uikit';
export default function ButtonExample() {
return (
);
}
```
### Confirm Dialog
File: examples/confirm-dialog.tsx
```tsx
import { Button, ConfirmProvider, useConfirm } from '@bloomneo/uikit';
// Wrap your app once in , then call useConfirm() anywhere.
// The promise resolves to `true` if the user confirmed, `false` if they cancelled.
function DeleteButton() {
const confirm = useConfirm();
async function handleDelete() {
const ok = await confirm({
title: 'Delete this design?',
description: 'This cannot be undone.',
confirmLabel: 'Delete',
tone: 'destructive',
});
if (!ok) return;
// …perform the delete here
}
async function handleHardDelete() {
// High-stakes: user must type "alice" before the confirm button enables.
const ok = await confirm.destructive({
title: 'Delete user',
description: 'This will permanently delete the account.',
verifyText: 'alice',
});
if (!ok) return;
}
return (
);
}
```
### Page Header
File: examples/page-header.tsx
```tsx
import { Users } from 'lucide-react';
import { Button, PageHeader } from '@bloomneo/uikit';
export default function PageHeaderExample() {
return (
}
title="User management"
description="View and manage all users in your workspace"
breadcrumbs={[
{ label: 'Admin', href: '/admin' },
{ label: 'Users' },
]}
actions={}
/>
);
}
```
### Permission Gate
File: examples/permission-gate.tsx
```tsx
import { Button, PermissionGate, PermissionProvider } from '@bloomneo/uikit';
// Bring your own auth source. PermissionProvider just needs a `check` function
// that takes a permission string and returns a boolean.
const currentUser = { roles: ['admin', 'editor'] };
const check = (perm: string) => currentUser.roles.includes(perm);
export default function PermissionGateExample() {
return (
{/* Single permission */}
{/* OR semantics across multiple roles */}
Restricted}>
{/* Custom predicate */}
currentUser.roles.length > 1}>
You have multiple roles.
);
}
```
### Theme Provider
File: examples/theme-provider.tsx
```tsx
import { Button, ThemeProvider, useTheme } from '@bloomneo/uikit';
import '@bloomneo/uikit/styles';
// REMEMBER: also drop the FOUC inline script in your so the theme
// is applied before React mounts. See @bloomneo/uikit/fouc.
function ThemeSwitcher() {
const { theme, mode, availableThemes, setTheme, toggleMode } = useTheme();
return (
);
}
export default function ThemeProviderExample() {
return (
);
}
```
### Toast
File: examples/toast.tsx
```tsx
import { Button, ToastProvider, toast } from '@bloomneo/uikit';
// Mount ONCE at the root of your app (inside ).
// Then call `toast.*` from anywhere — no React context plumbing needed.
export default function ToastExample() {
return (
<>
Recent ordersThe latest activity across your store
data={orders}
columns={orderColumns}
searchable={false}
pagination={false}
getRowId={(row) => row.id}
/>
);
}
```
### Delete Flow
File: cookbook/delete-flow.tsx
```tsx
/**
* Destructive action recipe.
*
* Demonstrates the high-stakes deletion pattern: a button that opens a
* confirm dialog where the user has to type the resource name before the
* delete button enables. On success a toast confirms the action.
*
* Use this for irreversible operations: deleting users, dropping databases,
* cancelling subscriptions, etc.
*/
import {
Button,
ConfirmProvider,
ToastProvider,
toast,
useConfirm,
} from '@bloomneo/uikit';
const RESOURCE_NAME = 'production-db';
function DeleteResource() {
const confirm = useConfirm();
async function onDelete() {
const ok = await confirm.destructive({
title: 'Delete production database',
description:
'This will permanently destroy the database and all of its data. There is no undo.',
verifyText: RESOURCE_NAME,
confirmLabel: 'I understand, delete it',
});
if (!ok) return;
// → call your delete API here
toast.success(`${RESOURCE_NAME} deleted`);
}
return (
);
}
export default function DeleteFlowRecipe() {
return (
The button below opens a confirmation dialog. The user must type
{RESOURCE_NAME}
before the delete button enables.
);
}
```
### Login
File: cookbook/login.tsx
```tsx
/**
* Login recipe.
*
* Centered card with email + password form, inline validation, and a
* submit button. No backend — wire your auth call where the comment is.
*/
import { useState } from 'react';
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
FormField,
Input,
PasswordInput,
ToastProvider,
toast,
} from '@bloomneo/uikit';
export default function LoginRecipe() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [submitting, setSubmitting] = useState(false);
const emailError =
email && !email.includes('@') ? 'Enter a valid email address' : undefined;
const passwordError =
password && password.length < 8 ? 'Must be at least 8 characters' : undefined;
const canSubmit =
email && password && !emailError && !passwordError && !submitting;
async function onSubmit(e: React.FormEvent) {
e.preventDefault();
setSubmitting(true);
try {
// → call your auth API here
await new Promise((r) => setTimeout(r, 600));
toast.success('Welcome back');
} catch {
toast.error('Login failed');
} finally {
setSubmitting(false);
}
}
return (
<>
Sign inWelcome back to your workspace
>
);
}
```
### Settings
File: cookbook/settings.tsx
```tsx
/**
* Settings recipe.
*
* Tabs containing a profile form, a security form, and a notifications form.
* Each section has its own save handler that fires a toast.
*/
import { useState } from 'react';
import {
Button,
FormField,
Input,
PageHeader,
PasswordInput,
Switch,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
ToastProvider,
toast,
} from '@bloomneo/uikit';
function ProfileForm() {
const [name, setName] = useState('Alice');
const [email, setEmail] = useState('alice@example.com');
return (
);
}
function SecurityForm() {
const [current, setCurrent] = useState('');
const [next, setNext] = useState('');
return (
);
}
function NotificationsForm() {
const [email, setEmail] = useState(true);
const [push, setPush] = useState(false);
return (
);
}
export default function SettingsRecipe() {
return (
<>