---
title: Theme Configuration
tags: [design-system, theme, mui]
created: 2026-05-23
updated: 2026-05-30
---

# Theme Configuration

> [!info] Amaneh v2.7.0 (commit 56fc84e)
> The active theme now uses the Amaneh warm-earth palette and the three-font stack (Source Serif 4 / IBM Plex Sans / IBM Plex Mono). MUI component overrides were updated for `Button`, `Card`, `Paper`, `AppBar`, `Chip`, and `Label`. The settings-drawer color-preset swatch picker was simplified to a single Amaneh entry.

The MUI theme is constructed in `frontend/src/theme/index.ts` and composed from option modules in `frontend/src/theme/options/`. The resulting theme is provided at the root layout, wrapped by an RTL-aware emotion cache.

---

## 1. Construction pipeline

```ts
// approximate — read theme/index.ts for the canonical version
import { createTheme } from '@mui/material/styles';
import { palette } from './options/palette';
import { typography } from './options/typography';
import { shadows, customShadows } from './options/shadows';
import { componentsOverrides } from './options/overrides';

export function buildTheme(opts: { mode: 'light' | 'dark'; direction: 'ltr' | 'rtl'; preset: string; }) {
  return createTheme({
    direction: opts.direction,
    palette: palette(opts.mode, opts.preset),
    typography,
    shape: { borderRadius: 8 },
    shadows: shadows(opts.mode),
    customShadows: customShadows(opts.mode),
    components: componentsOverrides(opts),
    breakpoints: {
      values: { xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536 },
    },
  });
}
```

The settings context calls `buildTheme()` whenever any axis changes, then re-mounts `<ThemeProvider>`. Because MUI v7 supports CSS variables, the swap is cheap (no React tree thrash).

---

## 2. Provider wiring (root layout)

```tsx
// frontend/src/app/layout.tsx (simplified)
<AppRouterCacheProvider options={{ key: dir === 'rtl' ? 'css-rtl' : 'css', prepend: true, stylisPlugins: dir === 'rtl' ? [rtlPlugin] : [] }}>
  <ThemeProvider theme={theme}>
    <CssBaseline />
    <LocalizationProvider dateAdapter={AdapterDayjs}>
      <I18nProvider>
        <QueryClientProvider client={queryClient}>
          <SocketProvider>
            <SnackbarProvider>{children}</SnackbarProvider>
          </SocketProvider>
        </QueryClientProvider>
      </I18nProvider>
    </LocalizationProvider>
  </ThemeProvider>
</AppRouterCacheProvider>
```

> [!warning]
> The cache `key` MUST differ between LTR and RTL (`'css'` vs `'css-rtl'`) — otherwise switching direction at runtime keeps the previous direction's CSS in the head and the layout breaks.

---

## 3. Palette

See [[Colors]] for the full table. Structure:

```ts
{
  mode: 'light' | 'dark',
  primary:   { lighter, light, main, dark, darker, contrastText },
  secondary: { lighter, light, main, dark, darker, contrastText },
  info:      { lighter, light, main, dark, darker, contrastText },
  success:   { lighter, light, main, dark, darker, contrastText },
  warning:   { lighter, light, main, dark, darker, contrastText },
  error:     { lighter, light, main, dark, darker, contrastText },
  grey:      { 100…900 },
  text:      { primary, secondary, disabled },
  background:{ default, paper, neutral },
  action:    { active, hover, selected, focus, disabled, disabledBackground },
}
```

Color presets (selectable in settings) swap the `primary` + `secondary` color groups while leaving `grey`, `error`, etc. untouched.

---

## 4. Typography

```ts
{
  fontFamily: '"Public Sans Variable", "Helvetica", "Arial", sans-serif',
  fontWeightRegular: 400,
  fontWeightMedium: 500,
  fontWeightSemiBold: 600,
  fontWeightBold: 700,
  h1: { fontSize: 64, lineHeight: 80/64, letterSpacing: -1 },
  h2: { fontSize: 48, lineHeight: 64/48 },
  h3: { fontSize: 32, lineHeight: 48/32 },
  h4: { fontSize: 24, lineHeight: 36/24 },
  h5: { fontSize: 20, lineHeight: 30/20 },
  h6: { fontSize: 18, lineHeight: 28/18 },
  subtitle1: { fontSize: 16, fontWeight: 600 },
  subtitle2: { fontSize: 14, fontWeight: 600 },
  body1: { fontSize: 16 },
  body2: { fontSize: 14 },
  caption: { fontSize: 12 },
  overline: { fontSize: 12, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 1.1 },
  button: { fontSize: 14, fontWeight: 700, textTransform: 'unset' },
}
```

Variants `h1` and `h2` scale down responsively. Use the `responsiveFontSizes(theme)` helper if you need automatic scaling.

See [[Typography]] for font loading, secondary font (Barlow), and i18n font fallbacks.

---

## 5. Spacing

```ts
spacing: 8  // unit
// theme.spacing(1) === '8px'
// theme.spacing(2) === '16px'
// theme.spacing(0.5) === '4px'
```

Use the shorthand props on the `sx`:

```tsx
<Box sx={{ p: 2, mt: 3, mx: { xs: 1, md: 4 } }} />
```

---

## 6. Shape & radii

```ts
shape: { borderRadius: 8 }   // default
// cards typically: borderRadius: 16
// pills:         borderRadius: 9999
```

---

## 7. Shadows

MUI's default `shadows` array (25 levels: `none`, then `1`–`24`) is replaced with a softer custom set. Additionally, `customShadows` defines brand-specific tinted shadows used by the `Card`, `Button`, `Dialog` overrides:

```ts
customShadows: {
  z1, z4, z8, z12, z16, z20, z24,
  primary:   'rgba(<primary.main>, 0.24) 0 8 16 0',
  secondary: '…',
  info:      '…',
  success:   '…',
  warning:   '…',
  error:     '…',
  card:      'rgba(0,0,0,0.04) 0 0 2 0, rgba(0,0,0,0.08) 0 12 24 -4',
  dialog:    '…',
  dropdown:  '…',
}
```

Read inside `sx`:

```tsx
sx={{ boxShadow: (theme) => theme.customShadows.z8 }}
```

---

## 8. Component overrides

`theme/options/overrides/` contains one file per overridden component, each exporting `{ defaultProps, styleOverrides }` merged into `components`:

| Component | Notable override |
|---|---|
| `MuiButton` | `disableRipple`, custom `customShadows.primary` on contained variant |
| `MuiCard` | `borderRadius: 16`, `customShadows.card` |
| `MuiPaper` | elevation flattened to `0` by default (use `card` shadow explicitly) |
| `MuiTextField` | `variant: 'outlined'` default, custom focus ring |
| `MuiTooltip` | dark background regardless of mode, smaller padding |
| `MuiAvatar` | letter-based fallback with hashed background color |
| `MuiTableCell` | denser padding, header weight 600 |
| `MuiDialog` | `customShadows.dialog`, max-width responsive |
| `MuiAlert` | tinted background using palette `lighter` |
| `MuiTab` | uppercase off, color from `primary.main` when selected |
| `MuiAutocomplete` | custom popup paper shadow |

Add an override:

1. Create `theme/options/overrides/<Component>.ts` exporting a function `(theme) => ({...})`.
2. Register it in `theme/options/overrides/index.ts`.
3. It will be merged into the next `createTheme` call automatically.

---

## 9. CSS variables (MUI v7)

`createTheme` with `cssVariables: true` (recommended) emits CSS custom properties at the `<html>` root and switches them per mode without re-rendering. The default is on in v7 — verify in `theme/index.ts`.

This means a custom CSS rule can reference:

```css
.my-thing {
  background: var(--mui-palette-primary-main);
  padding: calc(var(--mui-spacing) * 2);
}
```

Use this sparingly — prefer the `sx` prop.

---

## 10. Per-locale font swap

When the active locale is `fa` or `ar`, the typography option module can layer a Persian/Arabic font (e.g., **Vazirmatn**, **IRANSans**) ahead of `Public Sans Variable`:

```ts
fontFamily: dir === 'rtl'
  ? '"Vazirmatn", "Public Sans Variable", sans-serif'
  : '"Public Sans Variable", sans-serif',
```

Otherwise Latin glyphs still render via the Public Sans fallback. See [[Internationalization & RTL]].

---

## 11. Customisation checklist

- [ ] New brand color → update `palette.ts` (light + dark) and consider a preset.
- [ ] New variant → add to typography options and the `TypographyOptions` interface augmentation.
- [ ] Component override → file in `overrides/`.
- [ ] Verify in both modes (light + dark) AND both directions (ltr + rtl).
- [ ] Verify against WCAG AA contrast.
- [ ] Snapshot test the component if visual stability matters.

---

## Related

- [[Design System Overview]]
- [[Colors]] · [[Typography]] · [[Components]] · [[Layouts]]
- [[Internationalization & RTL]] · [[Settings & Theming]]
- [[Frontend Architecture]]
