Brik Design System
Getting Started

Framework Guides

Wire BDS into a Next.js product app or an Astro marketing site. Same tokens, same components, different render targets.

BDS ships one set of tokens, components, and theming primitives consumed by two first-class render targets:

TargetUsed forReference repo
Next.js (App Router)Product apps — portal, renew-pmsbrik-client-portal
AstroClient marketing sitesvale-partners

Webflow is supported as a legacy surface only — see the last section.

Read Installation first for the bare-minimum cascade. This page layers framework-specific concerns on top: fonts, theme provider, anti-FOUC, motion, multi-brand scope.


Next.js

Recommended for product apps that authenticate users, write to Supabase, and live behind a sign-in. Reference: brik-client-portal.

1. Install

npm install @brikdesigns/bds

2. Fonts

BDS does not ship fonts. Load them via next/font in src/lib/fonts.ts and expose CSS variables that the BDS --font-family-* tokens can consume:

// src/lib/fonts.ts
import { Poppins } from 'next/font/google';

export const poppins = Poppins({
  subsets: ['latin'],
  weight: ['300', '400', '500', '600', '700'],
  variable: '--font-poppins',
  display: 'swap',
});

3. Token cascade in globals.css

/* src/app/globals.css */

/* Declare the layer order first — before any @import. */
@layer bds-tokens, bds-components, client-theme, client-overrides;

/* 1. BDS canon — never edit. tokens.css ships unlayered; layer() places it. */
@import '@brikdesigns/bds/tokens.css' layer(bds-tokens);
@import '@brikdesigns/bds/styles.css'  layer(bds-components);

/* 2. Your brand override (same token names, brand values). */
@import '../styles/theme-{client}.css'  layer(client-theme);

For the override matrix and a minimal theme-{client}.css template, see Client Themes.

Don't reach for var(--text-primary) directly in TSX — wrap tokens in typed primitives (src/lib/tokens.ts + src/lib/styles.ts) so editor autocomplete catches typos at write-time. Full pattern: React Composition Layer.

4. Root layout

The portal pattern: next/font body class, anti-FOUC theme-init script, and a thin <BDSProvider> wrapping ThemeProvider from @brikdesigns/bds.

// src/app/layout.tsx
import type { Metadata } from 'next';
import { poppins } from '@/lib/fonts';
import { BDSProvider } from '@/components/bds-provider';
import './globals.css';

export const metadata: Metadata = {
  title: 'My App',
  description: '...',
};

// Anti-FOUC — runs before first paint so dark mode doesn't flash.
const themeInitScript = `
(function() {
  try {
    var saved = localStorage.getItem('theme');
    var theme = saved || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    document.documentElement.dataset.theme = theme;
    document.documentElement.style.colorScheme = theme;
  } catch (e) { /* private browsing — defaults are fine */ }
})();
`;

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
      </head>
      <body className={`body ${poppins.variable} ${poppins.className}`}>
        <BDSProvider>{children}</BDSProvider>
      </body>
    </html>
  );
}
// src/components/bds-provider.tsx
'use client';

import { ThemeProvider } from '@brikdesigns/bds';

export function BDSProvider({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider
      initialTheme={{ themeNumber: 'brik' }}
      persist={false}
      applyToBody={false}
    >
      {children}
    </ThemeProvider>
  );
}

applyToBody={false} prevents ThemeProvider from setting a theme-X class on <body> — your app uses your own brand tokens, not the eight numbered web-store themes.

5. Tailwind v3 bridge

Tailwind v3's preflight outputs unlayered resets that beat BDS's @layer bds-components rules. If you use Tailwind, add this once in globals.css:

/* Lets BDS components keep their layered styles even though Tailwind preflight is unlayered. */
[class*="bds-"] {
  all: revert-layer;
}

Skip this block if you don't use Tailwind.

6. Motion

The lightweight tier (token-driven CSS animations) is automatically available. Product apps rarely escalate beyond it. See Motion → Tiers.

7. Client-side routing

BDS navigation components (SidebarNavigation, SubNavigation, NavItem) render a bare <a> by default, which triggers a full-page reload on every click in Next.js. Pass next/link as linkComponent so nav items route client-side and prefetch:

import Link from 'next/link';
import { SidebarNavigation } from '@brikdesigns/bds';

<SidebarNavigation linkComponent={Link} navItems={navItems} logo={logo} />;

next/link satisfies the BdsLinkComponent contract ({ href: string } + standard anchor props) with no wrapper. Items that are disabled or have no href fall back to <a> automatically. See ADR-012. (react-router's to-based NavLink needs a one-line hrefto adapter.)

Common Next.js gotchas

Don't import BDS components into Server Components without a 'use client' boundary. Most BDS components carry React hooks. Wrap interactive UI in a client component (the BDSProvider is one).

metadata.themeColor belongs on a per-route export, not in app/layout.tsx's metadata object — Next 15+ moves it to the generateViewport API.


Astro

Recommended for client marketing sites — landing pages, content pages, sectioned long-form. Reference: vale-partners (real client site, multi-brand).

1. Install

npm install @brikdesigns/bds

2. Astro config

Minimal — BDS imposes no build plugins:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
import icon from 'astro-icon';

export default defineConfig({
  site: 'https://example.com',
  integrations: [sitemap(), icon()],
});

Astro has no next/font analog. Load fonts with a preconnect + stylesheet <link> in your base layout's <head>. BDS --font-family-* tokens read from whatever family you load — pick the family in your theme-client.css.

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&display=swap"
  rel="stylesheet"
/>

4. Base layout

Vite (Astro's bundler) lets you import CSS files. Unlike a CSS @import, a JS import can't carry a layer(…) clause — so Astro's cascade is governed by import order, not by @layer. Order them so BDS canon loads first, then your client overrides last (last wins):

---
// src/layouts/BaseLayout.astro

// Cascade by source order (JS imports can't assign a layer): BDS canon → client overrides last.
import '@brikdesigns/bds/tokens.css';
import '@brikdesigns/bds/styles.css';
import '../styles/global.css';
import '../styles/theme-client.css';
import '../styles/manual-overrides.css';
import '../styles/animations.css';

interface Props {
  title: string;
  description?: string;
}

const { title, description } = Astro.props;
---

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{title}</title>
    <meta name="description" content={description} />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
  </head>
  <body>
    <slot />
  </body>
</html>

5. Theme and mode switches

Astro is HTML — there's no ThemeProvider. Set switches as attributes on the <html> element directly. Full attribute table in The Cascade → HTML attribute switchboard.

<html lang="en" data-theme="dark" data-mode-borderwidth="bold">

For runtime theme toggles (user preference), add a small inline <script> in <head> that mirrors the Next.js anti-FOUC pattern.

6. Multi-brand sites

Some clients carry multiple brand colors at once (Vale Partners has three audience verticals — healthcare, land, commercial). Re-bind the canonical brand tokens inside [data-audience=X] scopes; components stay scope-blind.

---
const { pageAudience } = Astro.props;  // "healthcare" | "land" | "commercial"
---
<html lang="en" data-audience={pageAudience}>
/* src/styles/theme-client.css */
[data-audience='healthcare'] {
  --background-brand-primary: var(--vale-partners-navy);
  --text-brand-primary: var(--vale-partners-navy-dark);
}
[data-audience='land'] {
  --background-brand-primary: var(--vale-partners-olive);
  --text-brand-primary: var(--vale-partners-olive-dark);
}

Full pattern: Client Themes → Per-audience scope binding.

7. Atmospheres and blueprints

Astro is the primary consumer for the heavier theming layers:

Atmospheres — decoration-only CSS overlays (grain, vignettes, orbs). Import one after tokens.css:

import '@brikdesigns/bds/atmospheres/minimal-clinical.css';

See Atmospheres.

Blueprints — pre-composed .astro section templates (hero, services, testimonials). BDS ships them under @brikdesigns/bds/blueprints-astro:

---
import Features3ColBrandedDark from '@brikdesigns/bds/blueprints-astro/Features3ColBrandedDark.astro';
---

<Features3ColBrandedDark
  heading="Three things we do well"
  features={[/* ... */]}
/>

See Blueprints for the catalog and authoring rules.

8. Motion

Astro sites typically use the lightweight tier (CSS-only) plus Lenis for smooth scroll if the design audit calls for it. Escalation to GSAP / Premium is a per-client design decision. See Motion.

Common Astro gotchas

CSS custom properties resolve at use-time, not at definition time. The import order in BaseLayout.astro controls which :root declaration winstheme-client.css loaded last overrides BDS defaults. Don't try to "load styles.css after theme-client.css to make components pick up brand colors" — components reference var(--background-brand-primary) lazily; cascade resolves at render time.

<head> font preconnects matter for Lighthouse. Late font requests during a kinetic hero reveal flag CLS. Preconnect to fonts.googleapis.com and fonts.gstatic.com in every base layout — even on pages that don't use kinetic typography, since it's free.


Webflow legacy

For clients still on Webflow only. New work goes Next.js or Astro.

The Webflow CSS layer is partially synced from BDS via the Designer Bridge but is not the source of truth — token-rename migrations land in BDS first and propagate via the bridge on a manual cadence. Token names match the canonical registry (--text-primary, --background-brand-primary, etc.) so guidance on this site applies, with the caveat that Webflow's class-naming UI doesn't enforce BDS class conventions.

For the per-token rename log and migration cadence, see the BDS releases changelog.


  • Installation — bare-minimum setup before framework-specific concerns
  • The Cascade — token cascade architecture and the full <html> switch table
  • Theming — the four Theming Dimensions (tokens, atmospheres, archetypes, blueprints)
  • Client Themes — override matrix, minimal theme-{client}.css template, multi-brand scope binding
  • Motion — three-tier motion system and when to escalate

On this page

💬