Our UI layer is built on Tailwind CSS 3/4 with a strongly typed theme driven by CSS variables. This page explains how the configuration in tailwind.config.ts and src/app/globals.css works, when to extend tokens, and how component teams should consume the utilities.
| Setting | Value | Notes |
|---|---|---|
darkMode | "class" | We toggle the .light class on <html> to switch themes. Default is dark. |
content | ./src/**/*.{js,ts,jsx,tsx,mdx} (plus legacy paths) | Ensure any new directories holding JSX/MDX are added so purge picks up classes. |
theme.extend.colors | CSS variable backed colors (background-primary, content-body, etc.) | All colors resolve to rgb(var(--color-…)/<alpha-value>), so theming stays in CSS variables. |
screens | Adds portrait raw query | Use portrait: prefix for orientation-specific layouts. |
keyframes & animation | fadeIn, shake, typing, etc. | Reusable animation tokens, e.g. className="animate-fadeIn". |
| Plugins | none (yet) | Add Tailwind plugins in tailwind.config.ts as needed. |
// tailwind.config.ts
export default {
darkMode: "class",
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
theme: {
extend: {
colors: {
"background-primary": "rgb(var(--color-bg-primary) / <alpha-value>)",
// …
},
screens: { portrait: { raw: "(orientation: portrait)" } },
keyframes: { fadeIn: {/* … */} },
animation: { fadeIn: "fadeIn 0.5s ease-in-out" },
},
},
};The color palette lives in src/app/globals.css under @layer base. We define dark-mode values on :root and light-mode overrides under .light. Each --color-* variable is referenced by Tailwind through rgb(var(--color-…)/<alpha-value>) so utilities like bg-background-primary automatically adapt across themes.
@layer base {
:root {
--color-bg-primary: 8 9 12;
--color-content-body: 210 214 222;
--color-accent-orange: 234 88 12;
/* gradients etc */
}
.light {
--color-bg-primary: 245 247 250;
--color-content-body: 46 63 88;
--color-accent-orange: 249 115 22;
}
}:root and .light (if applicable).tailwind.config.ts (e.g., "badge-success": "rgb(var(--color-badge-success)/<alpha-value>)").className="bg-badge-success text-content-headline".globals.css also declares:
.glass-panel, .light .glass-panel, etc. Use these classes with Tailwind utilities when composing cards..scrollbar-orange, .text-balance, etc. Extend these via @layer utilities to avoid editing Tailwind config for one-off helpers.Example:
<div className="glass-panel p-6 text-content-primary bg-background-secondary">
<h3 className="text-2xl font-semibold">New template</h3>
<p className="text-content-body">Ship faster with the prebuilt stack.</p>
</div>Use the predefined animation tokens to keep motion consistent:
<button className="animate-fadeIn bg-accent-orange text-white px-4 py-2 rounded-full">
Deploy now
</button>Available classes: animate-fadeIn, animate-fadeOut, animate-shake, animate-pulse, animate-typing. Each maps to the keyframes defined in tailwind.config.ts.
gap-6, px-8) over hard-coded CSS.max-w- and mx-auto combos for responsive containers.portrait:flex-col pairs with desktop defaults.bg-background-primary, text-content-body, border-border-primary.text-content-headline. Body copy uses text-content-body.bg-accent-orange / bg-accent-amber with hover state hover:bg-accent-orange-dark.src/app/components/ui/* and already compose Tailwind classes with variants. Use the exported components instead of re-implementing styles..scrollbar-orange for consistent branding.<html> element receives class="light" when the user toggles themes. Use dark: variants sparingly; prefer CSS variables so components automatically adapt.style={{ backgroundColor: "rgb(var(--color-surface-card))" }}.content array; otherwise classes may be purged.tailwind.config.ts and document in this page (e.g., typography, forms) so everyone knows the global impact.shadcn/ui components should use bg-background-primary and text-content-body utilities.@layer components/utilities in globals.css rather than sprinkling <style> tags.export function FeatureCard() {
return (
<article className="glass-panel rounded-3xl p-6 text-content-body">
<div className="inline-flex items-center gap-2 text-xs uppercase tracking-wide text-content-placeholder">
<span className="h-2 w-2 rounded-full bg-accent-amber" />
Shipping update
</div>
<h3 className="mt-4 text-2xl font-semibold text-content-headline">
Launch SaaS faster
</h3>
<p className="mt-2">Use the 70+ components, Supabase schema, and Stripe flows already wired.</p>
<button className="mt-6 inline-flex items-center gap-2 rounded-full bg-accent-orange px-5 py-2 font-medium text-white transition hover:bg-accent-orange-dark">
Deploy now
</button>
</article>
);
}This card combines base classes (glass-panel) with theme-aware utilities so it inherits light/dark tokens automatically.
| Issue | Fix |
|---|---|
| Colors look wrong in light mode | Ensure .light class is toggled on <html> and the color references a var(--color-*) token. |
| New utility missing in build | Confirm the class exists in a file matched by the content glob. |
| Animations not playing | Verify you used animate-* class names defined in tailwind.config.ts. |
| Scrollbars unchanged | Apply .scrollbar-orange on the wrapper and ensure the element can scroll (overflow-auto). |
Keep this page up to date whenever we add new tokens, animations, or Tailwind plugins so UI work stays consistent across teams.