Documentation

Theming

Learn how to customize the appearance of your directory website using Tailwind CSS and Base UI components.

Dirstarter uses a modern theming system built on Tailwind CSS v4 and Base UI primitives. This guide will help you understand and customize the styles of your website.

Why this theming system?

Tailwind CSS and Base UI give you ready-to-use, accessible components that you can fully customize to match your brand's design.

Color System

The color system is defined using CSS variables in the @theme configuration. The base theme includes:

app/styles.css
@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:

app/styles.css
@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

Dirstarter uses Inter Variable as its default font. The font is self-hosted through the @fontsource-variable/inter package, so pages make no request to Google Fonts. The root layout imports the optical-size build, which draws Inter Display at large sizes and Inter Text at body sizes from one file:

app/layout.tsx
import "@fontsource-variable/inter/opsz.css"

The font tokens live in app/styles.css, with the system stack as the fallback:

app/styles.css
@theme {
  --font-system:
    -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji";

  --font-sans: "Inter Variable", var(--font-system);
  --font-display: "Inter Variable", var(--font-system);
}

Body text uses font-sans. Headings use font-display. To change the font, install another Fontsource package, import its CSS in app/layout.tsx and point both tokens at the new family.

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:

components/common/button.tsx
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 Base UI Primitives

Components are built on Base UI primitives for accessibility and behavior. They wrap the primitives with sensible defaults. See components/common/ for examples.

Styling with Tailwind

Components use Tailwind CSS for styling. You can customize the appearance by:

  1. Modifying the base styles in the component's cva configuration
  2. Extending the component with additional classes using the className prop
  3. 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

  1. Use CSS Variables: Always use theme variables for colors and other design tokens
  2. Maintain Consistency: Follow the established patterns for component variants
  3. Accessibility: Build on the Base UI primitives for accessible components
  4. Responsive Design: Use the responsive utilities provided by Tailwind
  5. Dark Mode: Test your customizations in both light and dark modes

Extending the Theme

To extend the theme:

  1. Add new CSS variables in the @theme configuration
  2. Create new component variants using cva
  3. Add new utility classes in the Tailwind configuration
  4. Create new components following the established patterns

Last updated on

On this page

Join hundreds of directory builders

Build your directory, launch, earn

Don't waste time on Stripe subscriptions or designing a pricing section. Get started today with our battle-tested stack and built-in monetization features.

Get Lifetime Access
30-day money-back guarantee