Skip to main content

Frontend Theming (MUI)

Refract’s admin UI uses a token-based theming setup: raw design tokens in themeConstants, mode selection in theme.ts, and ThemeProvider wiring in layouts.

How it works

If you keep the flow in your head, theming becomes straightforward.

1) Tokens: raw values for light/dark

All color/typography/spacing values come from: apps/portal/src/components/hooks/themeConstants.ts

2) Mode selection: useAdminTheme

apps/portal/src/components/hooks/theme.ts selects light/dark using:
  • localStorage key themeMode
  • system preference (prefers-color-scheme: dark)
  • cross-tab sync via the storage event

3) Layout wiring: ThemeProvider in layouts

The admin layout wraps page content with the chosen theme (see apps/portal/src/pages/admin/layout/index.tsx). Pattern:

Extending this system

Use this recipe to change theme tokens without breaking the light/dark mode behavior.
  1. Update the tokens you want to change in apps/portal/src/components/hooks/themeConstants.ts.
Example: adjust PRIMARY_MAIN:
  1. Keep mode selection logic in apps/portal/src/components/hooks/theme.ts (don’t duplicate it).
  2. Use the updated theme values via MUI’s sx and palette semantics instead of hardcoding.
Example:
  1. Verify:

What not to do

These are the guardrails that keep theming centralized and predictable. ❌ Never hardcode a palette color in multiple components when the token should live in themeConstants. ❌ Never add a new ThemeProvider just to style one page; it will drift from the shared mode logic. ❌ Never remove CssBaseline behavior in apps/portal/src/main.tsx.

File conventions

Use these locations and naming patterns so new theme code stays easy to find. Prefer:
  • semantic palette usage (like background.paper, text.primary)
  • sx for local styling that still uses theme semantics

What’s next?

Most frontend work that touches UI also touches either forms or error UX.