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.