EasyStarter logoEasyStarter

Theme System

Configure web app theme presets, light/dark mode, and the default theme

Theme System

The web app uses a CSS variable-based theme system built on shadcn/ui, supporting light / dark mode and multiple preset themes.


Theme Presets

All presets are defined in:

apps/web/src/configs/theme-presets.ts

Each preset contains a full set of CSS variables for both light and dark modes:

export const themePresets = {
  "modern-minimal": {
    label: "Modern Minimal",
    styles: {
      light: {
        background: "#ffffff",
        foreground: "#333333",
        primary: "#3b82f6",
        // ...
      },
      dark: {
        background: "#171717",
        foreground: "#e5e5e5",
        primary: "#3b82f6",
        // ...
      },
    },
  },
  // more presets...
};

25 built-in presets are included: modern-minimal, violet-bloom, t3-chat, mocha-mousse, amethyst-haze, doom-64, kodama-grove, cosmic-night, and more.


Changing the Default Theme

The default theme is the first key in the themePresets object. Move the desired preset to the top of the object:

apps/web/src/configs/theme-presets.ts
export const themePresets = {
  "violet-bloom": { /* ... */ },   // ← first = default
  "modern-minimal": { /* ... */ },
  // ...
};

If no presets exist, the app falls back to "modern-minimal" (the fallbackThemePresetKey in web-config.ts).


Adding a Custom Preset

Append a new entry to the themePresets object with both light and dark variable sets:

apps/web/src/configs/theme-presets.ts
export const themePresets = {
  // existing presets...
  "my-brand": {
    label: "My Brand",
    styles: {
      light: {
        background: "#fafafa",
        foreground: "#111111",
        primary: "#e11d48",
        // ... see existing presets for full variable list
      },
      dark: {
        background: "#0f0f0f",
        foreground: "#f5f5f5",
        primary: "#fb7185",
        // ...
      },
    },
  },
};

The new preset will automatically appear in the theme selector UI.


User Preference Storage

User theme choices are persisted in localStorage:

KeyContent
{AppName}-ui-themeAppearance mode: light / dark / system
{AppName}-ui-presetCurrent preset key, e.g. "modern-minimal"
{AppName}-ui-preset-stylesSerialized CSS variables (used to prevent FOUC)

AppName comes from the app name configured in packages/app-config.


Anti-FOUC Script

theme-config.ts exports an inline themeScript that runs in the HTML <head> before React hydrates. It applies the correct theme class and CSS variables to <html> immediately on page load, eliminating the flash of unstyled content.