Styling and Design Tokens
FluxKit token system, Tailwind token flow, and safe extension conventions.
Token Architecture
Token source: src/app/globals.css
Semantic aliases: @theme inline -> --color-* mapped to --* base vars
Theme state: .dark class on <html> (set by ThemeProvider)
Consumption: Tailwind utility classes in src/components and src/app
Runtime overrides: use-theme-manager hook writes CSS vars on documentElementDependency direction for styling:
globals.css variables
-> semantic utility classes (bg-background, text-foreground, border-border, ...)
-> ui primitives (src/components/ui/*)
-> application components (src/components/*)
-> route composition (src/app/*)Core Token Definitions in src/app/globals.css
The file defines:
- Base CSS variables (
:root) for light mode. - Dark-mode overrides under
.dark. - Tailwind
@theme inlinesemantic aliases (--color-*,--radius-*).
Semantic alias layer
@theme inline {
--font-sans: var(--font-inter);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-border: var(--border);
--color-ring: var(--ring);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-border: var(--sidebar-border);
}Light/dark token values (excerpt)
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--border: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--border: oklch(1 0 0 / 10%);
--ring: oklch(0.556 0 0);
}Convention: define raw values once (--background, --primary, etc.), then consume semantic utility classes in components.
Theme Activation via Provider
ThemeProvider toggles the dark or light class on document.documentElement, which activates token set selection in globals.css.
"use client";
import * as React from "react";
import { ThemeProviderContext } from "@/contexts/theme-context";
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
}: {
children: React.ReactNode;
defaultTheme?: "dark" | "light" | "system";
storageKey?: string;
}) {
const [theme, setTheme] = React.useState(
() =>
(typeof window !== "undefined" &&
(localStorage.getItem(storageKey) as "dark" | "light" | "system")) ||
defaultTheme,
);
React.useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system") {
root.classList.add(
window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light",
);
return;
}
root.classList.add(theme);
}, [theme]);
return (
<ThemeProviderContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeProviderContext.Provider>
);
}How Components Consume Tokens
Primitives consume semantic tokens through Tailwind classes, never raw color values.
Example: button token consumption
const buttonVariants = cva("inline-flex items-center justify-center", {
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
},
},
});Example: card token consumption
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className,
)}
{...props}
/>
);
}Convention: prefer token-backed utility classes (bg-card, text-muted-foreground) over arbitrary color literals in JSX.
Runtime Token Overrides (useThemeManager)
For custom theming, useThemeManager can reset and reapply CSS variables directly on document.documentElement.
const applyTheme = React.useCallback(
(themeValue: string, darkMode: boolean) => {
const theme = colorThemes.find((t) => t.value === themeValue);
if (!theme) return;
resetTheme();
const styles = darkMode
? theme.preset.styles.dark
: theme.preset.styles.light;
const root = document.documentElement;
Object.entries(styles).forEach(([key, value]) => {
root.style.setProperty(`--${key}`, value);
});
},
[resetTheme],
);Key convention: reset before apply to avoid stale variables leaking between presets.
Layout-Level Styling Contracts
App layout and dashboard layout rely on global semantic classes and CSS variable contracts.
// src/app/layout.tsx
<html
lang="en"
className={`${inter.variable} antialiased`}
suppressHydrationWarning
>
<body className={inter.className}>{children}</body>
</html>// src/app/(dashboard)/layout.tsx
<SidebarProvider
style={
{
"--sidebar-width": "16rem",
"--sidebar-width-icon": "3rem",
"--header-height": "calc(var(--spacing) * 14)",
} as React.CSSProperties
}
className={config.collapsible === "none" ? "sidebar-none-mode" : ""}
>
{children}
</SidebarProvider>globals.css includes targeted fixes tied to these contracts, e.g. .sidebar-none-mode and right-side inset margin behavior.
Token Conventions
- Add new global tokens in
src/app/globals.cssonly. - If token needs Tailwind utility support, map it in
@theme inline. - Consume via semantic class names in components (
bg-*,text-*,border-*). - Keep dark mode parity by defining both
:rootand.darkvalues. - Use runtime overrides sparingly and through centralized hooks (
useThemeManager).
Example: Adding a New Semantic Token
/* 1) globals.css */
:root {
--warning: oklch(0.82 0.16 82);
--warning-foreground: oklch(0.2 0.03 82);
}
.dark {
--warning: oklch(0.72 0.14 82);
--warning-foreground: oklch(0.97 0.02 82);
}
@theme inline {
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}// 2) component usage
<div className="bg-warning text-warning-foreground rounded-md p-3">
Plan needs attention
</div>Common Pitfalls to Avoid
- Hardcoding raw color values in component class strings.
- Updating only light tokens and forgetting
.darkparity. - Adding one-off inline styles where a semantic token should exist.
- Creating feature-local CSS variables that should be global design tokens.