Themes
Themed users
| # | Actions | Name | Status | |
|---|---|---|---|---|
| 1 | Ada Lovelace | ada@example.test | active | |
| 2 | Grace Hopper | grace@example.test | suspended |
VibeFlui’s built-in default preset — neutral grays, rounded surfaces, no custom class names.
Glass users
| # | Actions | Name | Status | |
|---|---|---|---|---|
| 1 | Ada Lovelace | ada@example.test | active | |
| 2 | Grace Hopper | grace@example.test | suspended |
Same resource and rows as the default preset — only the theme’s class name slots and variants change the look.
This tutorial shows theme customization.
VibeFlui supports Tailwind class slots, variants, color mode classes, conditional class names, and class name resolvers. Theme switching itself is usually host app state that swaps schema or provider configuration.
File structure
app/
users/
page.tsx
schemas/
users/
themed-users.schema.ts
glass-users.schema.tsBasic theme schema
schemas/users/themed-users.schema.ts
export const themedUsersSchema = {
resource: "users",
title: "Themed users",
theme: {
preset: "default",
colorMode: "inherit",
classNameMergeStrategy: "merge",
classNames: {
root: "space-y-4",
tableWrapper: "rounded-lg border border-gray-200 bg-white shadow-sm",
tableHeader: "bg-gray-50",
form: "space-y-4",
actionButton: "rounded-md"
},
variants: {
actionButton: {
primary: "bg-slate-900 text-white hover:bg-slate-800",
danger: "bg-red-600 text-white hover:bg-red-700",
ghost: "bg-transparent text-gray-700 hover:bg-gray-100"
}
}
},
table: {
actionIcon: {
hoverShape: "circle",
hoverColor: "brand"
},
columns: ["name", "email", "status"]
},
form: {
fields: ["name", "email", "status"]
}
} as const;Dark mode
Use theme.colorMode for root-level mode classes:
schemas/users/themed-users.schema.ts
theme: {
colorMode: "dark"
}dark adds the dark class to the root. light adds fluikit-light. inherit adds no mode class and lets the host app control the surrounding theme.
Conditional row classes
schemas/users/themed-users.schema.ts
theme: {
classNameWhen: [
{
slot: "tableRow",
when: { key: "status", operator: "equals", value: "suspended" },
className: "bg-red-50 text-red-900"
}
]
}This works because table row theme resolution receives row data.
Custom theme example
This is a custom example, not a built-in preset.
schemas/users/glass-users.schema.ts
export const glassUsersSchema = {
resource: "users",
title: "Glass users",
theme: {
preset: "custom",
colorMode: "light",
classNameMergeStrategy: "merge",
classNames: {
root: "rounded-2xl bg-white/60 p-6 text-slate-900 shadow-xl ring-1 ring-violet-200 backdrop-blur",
layout: "mb-4 flex items-start justify-between gap-4",
toolbar: "flex flex-wrap gap-2",
tableWrapper: "overflow-hidden rounded-xl border border-violet-100 bg-white/70 shadow-sm",
tableHeader: "bg-violet-50/70",
tableRow: "hover:bg-violet-50/70",
tableCell: "px-4 py-3 text-sm",
form: "space-y-4",
input: "w-full rounded-lg border border-violet-100 bg-white/80 px-3 py-2 text-sm shadow-sm outline-none focus:border-violet-300 focus:ring-2 focus:ring-violet-100",
actionButton: "inline-flex min-h-9 items-center justify-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition"
},
variants: {
actionButton: {
primary: "bg-violet-600 text-white shadow-sm hover:bg-violet-700",
secondary: "border border-violet-100 bg-white/80 text-violet-900 hover:bg-violet-50",
danger: "bg-red-600 text-white hover:bg-red-700",
ghost: "bg-transparent text-violet-900 hover:bg-violet-50"
}
}
},
table: {
columns: ["name", "email", "status"]
},
form: {
fields: ["name", "email", "status"]
}
} as const;Page usage
app/users/page.tsx
<FluiKit schema={glassUsersSchema} data={rows} />