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.
| Pair | Role |
|---|---|
| background / foreground | Page |
| card / card-foreground | Cards, elevated surfaces |
| popover / popover-foreground | Floating content |
| primary / primary-foreground | Brand action — Forest in light, Forest-500 in dark |
| secondary / secondary-foreground | Subdued button fill |
| muted / muted-foreground | Inset, hover, placeholder text |
| accent / accent-foreground | Lime — the contrast CTA |
| destructive / destructive-foreground | Red, for delete/cancel |
| border / input / ring | Borders, 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:
| Scheme | Canvas | Card | Use |
|---|---|---|---|
white | #ffffff | #ffffff + border | Pure white sections, minimalist |
light | brand-100 (sand) | #ffffff | Soft grouping, default for cards strips |
heavy | brand-200 | brand-100 | Callouts, sponsorship blocks |
dark | brand-900 forest | brand-800 | Heroes, 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).