Theming
Learn how to customize the appearance of your directory website using Tailwind CSS, Radix UI, and shadcn/ui components.
Dirstarter uses a modern theming system built on top of Tailwind CSS v4, Radix UI primitives, and shadcn/ui components. This guide will help you understand and customize the styles of your website.
Why this theming system?
The combination of Tailwind CSS, Radix UI and shadcn/ui components allows ready-to-use, complex UI components that can be fully customized to match your brands design.
Color System
The color system is defined using CSS variables in the @theme configuration. The base theme includes:
@theme {
--color-background: hsl(0 0% 100%);
--color-foreground: hsl(0 0% 12%);
--color-border: hsl(0 0% 88%);
--color-input: hsl(0 0% 88%);
--color-ring: hsl(0 0% 83%);
--color-card: hsl(0 0% 98%);
--color-card-foreground: hsl(0 0% 12%);
--color-popover: hsl(0 0% 100%);
--color-popover-foreground: hsl(0 0% 3.9%);
--color-primary: hsl(234 98% 61%);
--color-primary-foreground: hsl(0 0% 98%);
--color-secondary: hsl(0 0% 96%);
--color-secondary-foreground: hsl(0 0% 30%);
--color-muted: hsl(0 0% 96%);
--color-muted-foreground: hsl(0 0% 45.1%);
--color-accent: hsl(0 0% 96%);
--color-accent-foreground: hsl(0 0% 9%);
--color-destructive: hsl(0 84.2% 60.2%);
--color-destructive-foreground: hsl(0 0% 98%);
}Dark Mode
Dark mode is handled using the .dark class selector inside @layer base. The dark theme colors are defined as:
@layer base {
.dark {
--color-background: hsl(0 0% 5%);
--color-foreground: hsl(0 0% 90%);
--color-border: hsl(0 0% 15%);
--color-input: hsl(0 0% 15%);
--color-ring: hsl(0 0% 20%);
--color-card: hsl(0 0% 8%);
--color-card-foreground: hsl(0 0% 90%);
--color-popover: hsl(0 0% 5%);
--color-popover-foreground: hsl(0 0% 90%);
--color-primary: hsl(234 98% 61%);
--color-primary-foreground: hsl(0 0% 98%);
--color-secondary: hsl(0 0% 10%);
--color-secondary-foreground: hsl(0 0% 70%);
--color-muted: hsl(0 0% 10%);
--color-muted-foreground: hsl(0 0% 60%);
--color-accent: hsl(0 0% 10%);
--color-accent-foreground: hsl(0 0% 90%);
--color-destructive: hsl(0 62.8% 30.6%);
--color-destructive-foreground: hsl(0 0% 98%);
}
}Typography
The typography system uses Geist font as the primary font family by default:
@theme {
--font-system:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",
Helvetica, Arial, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji";
}The --font-sans variable is set by the Geist font loader in lib/fonts.ts and applied
to the root layout automatically by Next.js:
export const fontSans = Geist({
variable: "--font-sans",
display: "swap",
subsets: ["latin"],
weight: "variable",
})Component Variants
Components use the cva.style utility for variant management. This is a modern and powerful utility for defining and managing class variants. Here's an example of how variants are defined:
const buttonVariants = cva({
base: [
"group/button inline-flex items-center justify-center",
"border-transparent! font-medium text-[0.8125rem]/tight",
"text-start rounded-md overflow-clip hover:z-10",
"hover:border-transparent",
"disabled:opacity-60 disabled:cursor-not-allowed",
],
variants: {
variant: {
fancy:
"scheme-dark bg-primary text-primary-foreground hover:opacity-90",
primary:
"scheme-dark text-background bg-foreground hover:opacity-90",
secondary:
"scheme-light border-border! bg-background text-secondary-foreground"
+ " hover:bg-card hover:border-ring!",
soft:
"scheme-light bg-muted text-secondary-foreground"
+ " hover:bg-border/50 hover:text-foreground hover:outline-none",
ghost:
"scheme-light text-secondary-foreground"
+ " hover:bg-muted hover:text-foreground hover:outline-none",
destructive:
"scheme-light bg-destructive text-destructive-foreground"
+ " hover:bg-destructive/90",
},
size: {
sm: "px-2 py-1 gap-[0.66ch]",
md: "px-3 py-2 gap-[0.75ch]",
lg: "px-4 py-2.5 gap-[1ch] rounded-lg sm:text-sm/tight",
},
},
defaultVariants: {
variant: "primary",
size: "lg",
},
})Customizing Components
Using Radix UI Primitives
Components are built on top of Radix UI primitives for accessibility
and functionality. Components re-export Radix primitives with sensible defaults — see
components/common/ for examples.
Styling with Tailwind
Components use Tailwind CSS for styling. You can customize the appearance by:
- Modifying the base styles in the component's
cvaconfiguration - Extending the component with additional classes using the
classNameprop - Overriding the theme variables in your CSS
Example: Custom Button
const CustomButton = ({ className, ...props }) => (
<button
className={cx(buttonVariants({ variant: "primary", size: "lg" }), "custom-styles", className)}
{...props}
/>
)Best Practices
- Use CSS Variables: Always use theme variables for colors and other design tokens
- Maintain Consistency: Follow the established patterns for component variants
- Accessibility: Leverage Radix UI primitives for accessible components
- Responsive Design: Use the responsive utilities provided by Tailwind
- Dark Mode: Test your customizations in both light and dark modes
Extending the Theme
To extend the theme:
- Add new CSS variables in the
@themeconfiguration - Create new component variants using
cva - Add new utility classes in the Tailwind configuration
- Create new components following the established patterns
Last updated on