How It Works

Workflow

1. Custom Variables

The src/styles/config folder contains all the color, spacing, font sizes, border radii, breakpoints & styling tokens needed for all libraries to have a consistent design.

The variables that are needed for library configurations are defined via the --custom-* prefix inside a :root { ... } OR inside the .dark { ... } class (This is used by Tailwind CSS & Shadcn to apply dark theme styles).

/* colors-light.css */
:root {
  --custom-color-primary: #357a6e;
  --custom-color-background: #f9f6f2;
}

/* colors-dark.css */
.dark {
  --custom-color-primary: #e58e6d;
  --custom-color-background: #1c1a18;
}

This project's config files include:

FileWhat it defines
colors/colors-light.cssColor for light mode.
colors/colors-dark.cssColor for dark mode (same token names as).
colors/colors.cssOther custom color tokens (e.g. shadows) OR applying default / base colors to standard HTML tags.
fonts/fonts.cssFont sizes (including per breakpoint variants), weight, style, etc.
borders.cssBorder and outline colors, sizes, etc.
measurements.cssSpacing and layout dimension tokens (padding, margins, heights, widths, etc.).
breakpoints.cssBreakpoint values

2. Code Generation

During development, if you change a value in src/styles/config/, four scripts are automatically run to generate files that assist in the configuration of other libaries.

The Scripts

ScriptWhat it does
generateTokensReads every config file and exports all --custom-* values as a TypeScript object
generateTailwindBreakpointsCopies breakpoint values as plain pixel strings
generateColorsRgbConverts every color to an RGB triplet
generateFontsResponsiveTurns breakpoint-scoped font size tokens into @media overrides

The Generated Files

tokens.ts

All --custom-* values exported as a typed TypeScript object.

The number value is useful when a library requires a numeric input (e.g. MUI's fontSize).

export const tokens = {
  // Color tokens — plain hex strings
  light: {
    colorPrimary: "#357a6e",
    colorBackground: "#f9f6f2",
    // ...
  },
  dark: {
    colorPrimary: "#e58e6d",
    colorBackground: "#1c1a18",
    // ...
  },
  // Non-color tokens — { string, number } shape
  radiusBase:   { string: "0.625rem", number: 10 },
  breakpointMd: { string: "900px",    number: 900 },
  fontSizeH1:   { string: "2.5rem",   number: 40 },
  // ...
};

tailwind-breakpoints.css

Breakpoint values as literal px strings for Tailwind's @theme block (Tailwind v4 doesn't support var() in breakpoints):

@theme inline {
  --breakpoint-xs: 0px;
  --breakpoint-sm: 600px;
  --breakpoint-md: 900px;
  --breakpoint-lg: 1200px;
  --breakpoint-xl: 1536px;
}

colors-rgb.css

Every color token as an RGB triplet, needed by React Bootstrap's internal rgba() calculations:

:root {
  --custom-color-primary-rgb: 53, 122, 110;
  --custom-color-background-rgb: 249, 246, 242;
  /* ... */
}

fonts-responsive.css

@media overrides that swap base font size tokens at each breakpoint:

@media (max-width: 899px) {
  :root {
    --custom-font-size-h1: var(--custom-font-size-h1-md);
    --custom-font-size-body: var(--custom-font-size-body-md);
    /* ... */
  }
}

3. Library Configurations

Once the generated files are in place, each library reads from them in TWO ways:

1. CSS variables

Tailwind & Shadcn and React Bootstrap map --custom-* vars directly into each library's own variable names.

/* shadcn.css */
@theme inline {
  --color-primary: var(--custom-color-primary);
}

/* reactbootstrap.css */
:root {
  --bs-primary:     var(--custom-color-primary);
  --bs-primary-rgb: var(--custom-color-primary-rgb);
}

2. TypeScript Tokens

MUI and Ant Design are configured in TypeScript, so they use the generated tokens object to configure their themes.

import { tokens } from '@/src/styles/_generated/tokens'

// muiTheme.ts
createTheme({
  colorSchemes: {
    light: { palette: { primary: { main: tokens.light.colorPrimary } } },
    dark:  { palette: { primary: { main: tokens.dark.colorPrimary } } },
  },
});

// antdTheme.ts
export const antdLightTheme: ThemeConfig = {
  token: { colorPrimary: tokens.light.colorPrimary },
};

How They Work Together

CSS Layering

All CSS is imported into src/styles/app.css, which declares an explicit layer order at the top:

@layer tailwind-theme, tailwind-preflight, shadcn-overrides, bootstrap, antd, reactbootstrap, base, mui, tailwind-components, tailwind-utilities;

The priority order is from least (left) to most importent (right).

This allows libraries to correctly override default setup but also allows custom (base layer) setup to have priority for a more consistent design. See Pain Points: React Bootstrap Global Styles for why this layer ordering was necessary.

Providers & Syncers

Most React UI libraries needs a Provider to apply its theme. All providers are consolidated into a CombinedThemeProvider, which is applied once in the root layout.

type CombinedThemeProviderProps = {
  children: ReactNode;
  initialTheme: "light" | "dark";
};

export const CombinedThemeProvider = ({
  children,
  initialTheme,
}: CombinedThemeProviderProps) => {
  return (
    <NextThemesProvider
      attribute={"class"}
      enableSystem
      defaultTheme="system"
      disableTransitionOnChange
    >
      <AntdThemeProvider initialTheme={initialTheme}>
        <MuiThemeProvider>
          {/* Syncers explained in the "Light & Dark Mode" section*/}
          <ReactBootstrapThemeSyncer>
            {children}
          </ReactBootstrapThemeSyncer>
        </MuiThemeProvider>
      </AntdThemeProvider>
    </NextThemesProvider>
  );
};

Light & Dark Mode

This project uses next-themes to manage the user's theme preference. It persists the choice to localStorage and toggles a dark class on <html> when dark mode is active.

dark Class

Tailwind, MUI and Shadcn respond to the dark class automatically. Custom color tokens are defined in :root and overridden in dark, so no extra config is needed.

Syncers

A Syncer is a small client component that uses const { resolvedTheme } = useTheme() to listen for changes in the user's preferred theme choice.

It is needed when:

  1. It cannot access the dark class directly
  2. It has its own light / dark mode state that needs to be in sync with the user's preference
LibraryWhen the theme is changed
MUICalls setMode() to keep MUI's internal state in sync
React BootstrapSets the data-bs-theme attribute on the <html>
Ant DesignSets the theme-resolved cookie then passes it into <ConfigProvider> — see Pain Points: SSR Flicker

Initial Injection Scripts & Cookies

For MUI and React Bootstrap, a script is added to set the get the user's preferred theme before the page is served.

For Ant Design, a cookie is read to get the user's preferred theme before the page is served.

This is done to remove a flicker when loading the page. See Pain Points: SSR Flicker for details.