Theming

BTNG.UI uses the shadcn/ui token contract. That means any drop-in component from the shadcn ecosystem works without adaptation.

The tokens

Defined in tokens.css on :root and .dark. Every component reads only semantic names.

PairRole
background / foregroundPage
card / card-foregroundCards, elevated surfaces
popover / popover-foregroundFloating content
primary / primary-foregroundBrand action — Forest in light, Forest-500 in dark
secondary / secondary-foregroundSubdued button fill
muted / muted-foregroundInset, hover, placeholder text
accent / accent-foregroundLime — the contrast CTA
destructive / destructive-foregroundRed, for delete/cancel
border / input / ringBorders, form inputs, focus rings

BTNG-specific extras

On top of the stock shadcn set, we expose two brand aliases for direct use:

  • --brand-forest — the Forest-950 dark green. Always available regardless of theme.
  • --brand-lime — the lime accent.

Use these when you need the raw brand colour outside the semantic system (marketing pages, illustrations, etc).

Dark mode

Toggled via the .dark class on <html>. The ThemeProvider uses next-themes with attribute="class", so setTheme("dark") adds the class and the tokens remap.

<html lang="en" className="dark">

Color schemes

Dark mode is global — the whole page flips. Sometimes you want one section to look different from the rest without leaving light mode: a sand-tinted testimonial strip, a forest-green hero, a white pricing panel on an otherwise tinted page. That's what schemes are for.

A scheme is a scoped re-declaration of the semantic tokens. Set data-scheme on any wrapper and every descendant — Card, Button, Badge, text utilities, borders — re-renders against the new palette.

Four schemes ship today:

SchemeCanvasCardUse
white#ffffff#ffffff + borderPure white sections, minimalist
lightbrand-100 (sand)#ffffffSoft grouping, default for cards strips
heavybrand-200brand-100Callouts, sponsorship blocks
darkbrand-900 forestbrand-800Heroes, CTAs, dramatic sections

Usage

The easy path is SectionShell:

import { SectionShell } from "@btng/registry/sections/section-shell";

<SectionShell scheme="dark">
  <h2 className="text-4xl">Built for commerce, not dashboards.</h2>
  <Card>{/* automatically reads --card = brand-800, text in brand-50 */}</Card>
</SectionShell>

Or drop data-scheme on any element you control:

<section data-scheme="light" className="bg-background text-foreground py-16">
  <div className="grid grid-cols-3 gap-6">
    <Card>…</Card>
    <Card>…</Card>
    <Card>…</Card>
  </div>
</section>

Why this works

bg-card is a Tailwind utility that resolves to var(--card). The scheme scope rewrites --card for its subtree, so the same utility renders a different colour depending on where it sits in the DOM. No variant prop, no component branching — CSS does the work.

Schemes compose with dark mode. Inside .dark, the baseline tokens are already the dark palette; adding data-scheme inside that still overrides locally, which is useful for a light "pricing" strip inside an otherwise dark landing page.

Rebranding

For a non-BTNG project: override the semantic tokens on :root and .dark. You only touch one file.

/* your-theme.css — override selected tokens */
:root {
  --primary: oklch(35% 0.12 250);      /* your brand */
  --primary-foreground: #ffffff;
  --ring: oklch(50% 0.12 250);
}

.dark {
  --primary: oklch(70% 0.14 250);
  --primary-foreground: oklch(15% 0.04 250);
}

Import this after BTNG's tokens.css and every component inherits the new brand.

Accessibility

Light and dark modes both pass WCAG 2.1 AA for body text out of the box. If you override tokens, re-check contrast on --foreground against --background, and --primary-foreground against --primary. Target 4.5:1 for body, 3:1 for large text (≥18pt or 14pt bold).