Styling and Theming
Boreal UI styles are driven by CSS variables, shared style props, global defaults, and the color scheme theme provider.
Global Styles
Import the global stylesheet once.
import "@boreal-ui/core/globals.css";
For Next.js:
import "@boreal-ui/next/globals.css";
The global stylesheet provides CSS variables, resets, theme values, animations, and shared utility styles used by components.
Be careful with the default globals.css created by many Next.js starters:
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
When that reset is loaded after Boreal, the universal padding and margin declarations can override spacing used by Boreal components and nested content. A safer app-level baseline is:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
margin: 0;
}
Keep broader spacing rules scoped to your app shell, page layouts, or utility classes so they do not erase component-level padding and margins.
The CLI can create or repair that safer baseline for Next.js apps:
npx @boreal-ui/cli@latest init --framework next --recommended-globals
Interactive Next.js setup prompts for this by default. Use --recommended-globals to apply it without the prompt, or --no-recommended-globals to skip it.
Shared Style Props
Many components support a common styling vocabulary.
| Prop | Canonical values |
|---|---|
theme |
primary, secondary, tertiary, quaternary, clear |
state |
success, error, warning, info, disabled, empty string |
size |
xs, small, medium, large, xl |
rounding |
none, small, medium, large |
shadow |
none, light, medium, strong, intense |
borderWidth |
none, xs, small, medium, large, xl |
variant |
solid, outline, glass, glassOutline |
className |
Consumer class hook on the root element. |
Exact support varies by component. Use TypeScript or generated prop docs to confirm a component's full API. Structural utilities such as Portal intentionally keep a smaller styling surface because they render content into another DOM container rather than owning a themed visual surface.
For form controls, size changes the rendered control's minimum height, padding, font size, and internal gap. Composite controls apply the same density to their inputs, action buttons, and selectable options so xs through xl remain visually consistent.
import { Button, Card } from "@boreal-ui/core";
export function Actions() {
return (
<Card
theme="secondary"
rounding="large"
shadow="strong"
variant="glassOutline"
>
<Button theme="primary" size="large" variant="outline">
Save changes
</Button>
</Card>
);
}
Global Style Defaults
Use borealConfig to set project-wide defaults for components that read Boreal style config. setBorealStyleConfig is still exported for the same behavior.
import { borealConfig } from "@boreal-ui/core";
borealConfig({
defaultTheme: "secondary",
defaultSize: "medium",
defaultRounding: "medium",
defaultShadow: "light",
defaultBorderWidth: "none",
defaultVariant: "solid",
defaultColorSchemeName: "Forest Dusk",
});
For Next.js:
import { borealConfig } from "@boreal-ui/next";
Component props override global defaults.
glassOutline deliberately applies both treatments, allowing a translucent surface with an outlined edge. full is available only on components that can meaningfully render as a pill or circle; it is not part of the library-wide rounding scale. Component-specific values remain local—for example, ColorPicker additionally supports shape="pill".
Short aliases such as sm, md, and lg remain available for faster authoring, while the canonical names above are recommended in documentation and shared component APIs.
<Button theme="primary" size="large">
Save
</Button>
ThemeProvider
ThemeProvider manages the active color scheme and writes it into CSS variables. It resolves text colors against the active surfaces with a WCAG 2.1 AA normal-text contrast target, so low-contrast custom schemes fall back to readable foreground colors instead of blindly using forceTextColor.
import { ThemeProvider } from "@boreal-ui/core";
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider initialSchemeName="Forest Dusk">{children}</ThemeProvider>
);
}
For Next.js:
"use client";
import { ThemeProvider } from "@boreal-ui/next";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider initialSchemeName="Forest Dusk">{children}</ThemeProvider>
);
}
The Next provider synchronizes the selected scheme to localStorage and the
boreal-theme cookie by default. For full SSR theming, read that cookie in the
root layout and apply the server-generated theme attributes to <html>:
import { cookies } from "next/headers";
import { ThemeProvider } from "@boreal-ui/next/ThemeProvider";
import {
getThemeAttributes,
resolveThemeScheme,
THEME_COOKIE_NAME,
} from "@boreal-ui/next/server/ThemeProvider";
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const cookieStore = await cookies();
const savedThemeName = cookieStore.get(THEME_COOKIE_NAME)?.value;
const scheme = resolveThemeScheme(savedThemeName);
return (
<html lang="en" {...getThemeAttributes(scheme)}>
<body>
<ThemeProvider initialSchemeName={scheme.name}>
{children}
</ThemeProvider>
</body>
</html>
);
}
This renders the selected CSS variables in the server response and keeps live theme switching available after hydration.
When the server cannot read cookies, the initialization script remains
available as a fallback. It checks localStorage, then the theme cookie, before
hydration. Because the script changes <html> before hydration, the root
element needs React's suppressHydrationWarning prop:
import { getThemeInitializationScript } from "@boreal-ui/next";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<script
dangerouslySetInnerHTML={{ __html: getThemeInitializationScript() }}
/>
{children}
</body>
</html>
);
}
ThemeProvider props:
| Prop | Description |
|---|---|
children |
Application or subtree to theme. |
customSchemes |
Registers additional color schemes. |
enableThemeScript |
Controls pre-hydration script generation. Core defaults to true; Next defaults to false. |
initialSchemeName |
Selects the starting scheme by name. |
syncThemeCookie |
Persists changes to the SSR theme cookie. Defaults to true for Next. |
themeCookieName |
Overrides the SSR theme cookie name. Defaults to boreal-theme. |
useOnlyCustomSchemes |
Uses only custom schemes instead of built-in schemes. |
When initialSchemeName is provided, it is preferred over the saved theme name. Without it, the saved theme name is used when available, then the configured Boreal default, then the first available scheme.
Equivalent customSchemes arrays reuse the existing scheme snapshot. For a large custom collection, keep the array in a module-level constant or memoize it so ThemeProvider can also avoid repeated serialization work.
Custom Color Schemes
import { ThemeProvider } from "@boreal-ui/core";
import type { ColorScheme } from "@boreal-ui/types";
const schemes: ColorScheme[] = [
{
name: "Brand Night",
primaryColor: "#4f46e5",
secondaryColor: "#06b6d4",
tertiaryColor: "#a855f7",
quaternaryColor: "#22c55e",
backgroundColor: "#0f172a",
forceTextColor: "#ffffff",
},
];
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider customSchemes={schemes} initialSchemeName="Brand Night">
{children}
</ThemeProvider>
);
}
You can also register a scheme outside the provider.
import { registerColorScheme } from "@boreal-ui/core";
registerColorScheme({
name: "Brand Light",
primaryColor: "#2563eb",
secondaryColor: "#0891b2",
tertiaryColor: "#7c3aed",
quaternaryColor: "#16a34a",
backgroundColor: "#ffffff",
});
ThemeSelect
ThemeSelect renders a control for selecting registered color schemes.
import { ThemeProvider, ThemeSelect } from "@boreal-ui/core";
export function Settings() {
return (
<ThemeProvider>
<ThemeSelect aria-label="Select color scheme" />
</ThemeProvider>
);
}
CSS Variable Overrides
Override variables globally or scope them to a subtree.
:root {
--font-family-ui: Inter, system-ui, sans-serif;
--border-radius-md: 0.5rem;
--transition-default: 160ms ease;
--focus-outline-color: #2563eb;
}
.admin-shell {
--background-color: #0f172a;
--text-color: #f8fafc;
}
Class Name Customization
Most components accept className. Larger components expose section-level class props so consumers can style specific regions while preserving Boreal's internal classes.
<Card
title="Revenue"
className="dashboard-card"
headerClassName="dashboard-card-header"
contentClassName="dashboard-card-content"
>
<MetricBox value="$42,180" label="This month" />
</Card>
Prefer CSS variables for global visual changes and class props for local layout or component-specific polish.