--- 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 ``. 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) {children} ``` > [!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 ``` --- ## 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(, 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/.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 `` 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]]