Styling System
Tailwind CSS v4, shadcn/ui, and SavvySolve brand theming configuration.
Styling System
SavvySolve uses Tailwind CSS v4 for utility-first styling combined with shadcn/ui for pre-built accessible components. The styling system implements the SavvySolve brand identity with a custom color palette featuring orange (#FF8A0D) as the primary accent and purple (#1B0F40) for dark mode backgrounds.
Brand Identity
The SavvySolve brand uses three core colors:
| Color | Hex | Usage |
|---|---|---|
| Orange | #FF8A0D | Primary actions, accents, active states |
| Purple | #1B0F40 | Dark mode background, secondary elements |
| White | #FFFFFF | Light mode background, text on dark |
The brand font is Poppins for headings and body text, with JetBrains Mono for code blocks.
Logo Assets
Logo files are stored in public/logos/ with standardized naming for different use cases:
| File | Background | Usage |
|---|---|---|
icon-light.svg | Light | Icon on light backgrounds (orange background) |
icon-dark.svg | Dark | Icon on dark backgrounds (purple background) |
logo-horizontal-light.svg | Light | Full logo on light backgrounds ("Savvy" orange, "Solve" purple) |
logo-horizontal-dark.svg | Dark | Full logo on dark backgrounds (all orange) |
logo-oauth.svg | Any | Square logo for OAuth/social login buttons |
Usage Guidelines
Light mode / Light backgrounds:
<img src="/logos/icon-light.svg" alt="SavvySolve" />
<img src="/logos/logo-horizontal-light.svg" alt="SavvySolve" />Dark mode / Dark backgrounds:
<img src="/logos/icon-dark.svg" alt="SavvySolve" />
<img src="/logos/logo-horizontal-dark.svg" alt="SavvySolve" />Theme-aware logo component:
import { useTheme } from "next-themes";
function Logo() {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark"
? "/logos/logo-horizontal-dark.svg"
: "/logos/logo-horizontal-light.svg";
return <img src={logoSrc} alt="SavvySolve" className="h-8" />;
}PWA Icons
PWA icons are generated from icon-light.svg (the orange icon) and stored in public/icons/. To regenerate after logo updates:
bun run scripts/generate-pwa-icons.tsThis generates all required sizes: 72, 96, 128, 144, 152, 192, 384, 512px plus maskable variants and the Apple touch icon.
Why This Stack?
The PRD emphasizes several UI requirements that influenced these choices:
- Mobile-first design (60%+ expected mobile traffic) - Tailwind's responsive utilities make this natural
- Large touch targets for senior users - shadcn/ui components follow accessibility guidelines
- WCAG AA compliance - Both tools prioritize accessibility by default
- Rapid iteration - The solver dashboard and session interfaces need fast development cycles
Tailwind CSS v4
Tailwind v4 introduces CSS-first configuration, eliminating the need for a separate tailwind.config.js file. All configuration happens directly in CSS using the @theme directive.
CSS Configuration
The styling system is configured in the global stylesheet:
@import "tailwindcss";
@import "fumadocs-ui/css/preset.css";
@import "tw-animate-css";Note that we import only preset.css from Fumadocs (not neutral.css) because we define our own complete color theme.
Custom Theme
The SavvySolve theme defines all colors in the @theme block for light mode:
@theme {
/* Fonts */
--font-sans: var(--font-poppins);
--font-mono: var(--font-jetbrains-mono);
/* Light mode colors */
--color-fd-background: hsl(0, 0%, 100%);
--color-fd-foreground: hsl(256, 45%, 14%);
--color-fd-primary: hsl(33, 100%, 52%);
--color-fd-primary-foreground: hsl(0, 0%, 100%);
--color-fd-accent: hsl(33, 80%, 95%);
--color-fd-accent-foreground: hsl(33, 90%, 30%);
--color-fd-ring: hsl(33, 100%, 52%);
/* ... additional color definitions */
}Dark mode overrides use the .dark selector outside the @theme block:
.dark {
--color-fd-background: hsl(256, 63%, 8%);
--color-fd-foreground: hsl(0, 0%, 92%);
--color-fd-primary: hsl(33, 100%, 55%);
--color-fd-primary-foreground: hsl(0, 0%, 100%);
--color-fd-card: hsl(256, 55%, 11%);
/* ... additional dark mode overrides */
}This approach ensures our brand colors take precedence over any preset defaults.
Font Configuration
Fonts are loaded in the root layout using Next.js font optimization:
import { Poppins, JetBrains_Mono } from "next/font/google";
const poppins = Poppins({
variable: "--font-poppins",
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
});
const jetbrainsMono = JetBrains_Mono({
variable: "--font-jetbrains-mono",
subsets: ["latin"],
});The CSS variables --font-poppins and --font-jetbrains-mono are then referenced in the @theme block.
shadcn/ui
shadcn/ui provides copy-paste components rather than an npm package dependency. Components are installed into your codebase and can be customized as needed.
Configuration
The components.json file configures how shadcn/ui generates components:
{
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui"
}
}Adding Components
Install components using the shadcn CLI:
bunx shadcn@latest add button
bunx shadcn@latest add card dialog form inputComponents are installed to components/ui/ and can be imported:
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardContent } from "@/components/ui/card";The cn Utility
A utility function combines Tailwind classes with proper precedence handling:
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Dark Mode
Dark mode is managed by Fumadocs' RootProvider which uses next-themes under the hood:
import { RootProvider } from "fumadocs-ui/provider/next";
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body className={`${poppins.variable} ${jetbrainsMono.variable} antialiased`}>
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}The theme toggle in the documentation header switches between light and dark modes, with the purple brand background appearing in dark mode.
Color Reference
Light Mode
- Background: White
- Text: Dark purple (
hsl(256, 45%, 14%)) - Primary/Accents: Orange (
hsl(33, 100%, 52%)) - Cards: Near-white (
hsl(0, 0%, 98%))
Dark Mode
- Background: Deep purple (
hsl(256, 63%, 8%)) - Text: Light gray (
hsl(0, 0%, 92%)) - Primary/Accents: Bright orange (
hsl(33, 100%, 55%)) - Cards: Slightly lighter purple (
hsl(256, 55%, 11%))
Usage Guidelines
When building UI components:
- Use the brand colors -
bg-fd-primaryfor orange accents - Test both themes - Toggle dark mode to verify components work
- Prefer shadcn components - Check if a component exists before building custom
- Use Poppins - The brand font is automatically applied via CSS variables