# Client theme Colors, tokens, dark mode, direction, fonts, motion. The brand's *look* is the [frontend-designer](../../../.claude/skills/frontend-designer/SKILL.md) skill's job; this file is the mechanism it runs on. > Last verified: 2026-07-30 against commit `d3ec723`. --- ## 1. Brand identity Balinyaar is a trust-first home-nursing marketplace in Iran. The tone is calm, warm, clinical-but-human — not a cold medical dashboard. The default audience is Persian (RTL); English is secondary. | Role | Light | Dark | | --- | --- | --- | | Primary — deep teal | `#1d4a40` | `#6fc0ac` (lifted, readable on dark) | | Secondary — terracotta | `#d98c6a` | `#e6a98a` | | Page surface | `#faf9f5` cream | `#0f1c19` deep teal | | Paper / card | `#ffffff` | `#16302a` teal surface | | Text primary | `#1b2521` ink | `#f3efe9` cream | **Teal ground, cream glyph, terracotta accent** is the whole identity. Use terracotta sparingly as the single accent; teal carries everything else. The logo mark is a deep-teal rounded square, a cream lowercase "b" built from a stem plus a ring bowl, and one terracotta dot. Two SVGs under `components/common/AppIcon/icons/`: `LogoMark.tsx` (monochrome `currentColor` glyph only, registered as `ICONS.logo`) and `LogoLockup.tsx` (full colour, token-driven so it tracks the scheme, used by `BrandMark`). **The wordmark beside it stays a real, translated ``** — never bake locale text into an SVG. The favicon and `public/img/favicon/*.png` are rasterized from the same construction with fixed brand hex, which is the one place a literal hex is correct; regenerate with a `sharp`-based script rather than hand-editing the binaries. --- ## 2. Two mirrored color homes Colors exist in two places that must stay in sync. Pick the right one. | Home | File | Reach it via | Use for | | --- | --- | --- | --- | | **MUI palette** | `theme/colors.ts` (`BRAND`, `LIGHT_PALETTE`, `DARK_PALETTE`) | `color="primary"`, `sx={{ color: 'text.secondary', bgcolor: 'background.paper' }}` | **The default** for styling a MUI component | | **`--bal-*` CSS variables** | `theme/tokens.css`, under `[data-mui-color-scheme='light'\|'dark']` | `var(--bal-primary)` | Custom CSS outside MUI's palette, and every semantic feedback color | - Styling a MUI component → palette keys. - Need success / error / warning / info → **`--bal-*`, not MUI's defaults.** The MUI palette defines no semantic colors, and these tokens are brand-harmonized. - Need a custom color in raw CSS → add a `--bal-*` token **in both scheme blocks**, then `var(--…)`. - **Never hard-code a hex or rgb** in `sx`, `styled`, or a component. - Adding or changing a color means editing `tokens.css` **and** `colors.ts` together. Both file headers call out the sync requirement. ### The token catalogue Every token is defined under both `[data-mui-color-scheme]` blocks unless noted. | Group | Tokens | Notes | | --- | --- | --- | | Brand | `--bal-primary`, `-light`, `-dark`, `-contrast`, `-soft`; same five for `--bal-secondary` | | | Surfaces | `--bal-bg-default`, `--bal-bg-paper`, `--bal-frame-canvas` | `frame-canvas` is the backdrop `AppFrame` paints *outside* the phone-width column — **never a surface a component draws on** | | Text | `--bal-text-primary`, `--bal-text-secondary`, `--bal-divider` | | | Semantic | `--bal-{success,error,warning,info}` + each `-contrast` + each `-soft` | `-contrast` is the text color on that fill; `-soft` is the tinted background variant | | Elevation | `--bal-shadow-1/2/3` | Teal-tinted (black-teal in dark). They back `theme.ts`'s `shadows` array, so every MUI elevation resolves through them — never MUI's grey stack | | Radius | `--bal-radius-sm` 6px (controls), `-md` 8px (cards/paper, `= theme.shape.borderRadius`), `-lg` 12px (dialogs), `-pill` 999px | Reference the token, **never a numeric `sx={{ borderRadius: n }}`** — that multiplies the shape unit, which is how the login card once became a 30px pill. `MuiPaper` pins the `md` step so a Paper can't drift past it. `-pill` is for shapes that genuinely *are* pills (the floating nav, a segmented control's active chip), never a card | | Motion | `--bal-motion-fast/base/slow` (120/200/300ms), `--bal-easing-standard` | `theme.ts` points `MuiDialog`/`MuiDrawer`/`MuiPopover`/`MuiMenu`'s `defaultProps.transitionDuration` at the same numbers, in one place, instead of MUI's per-variant defaults | | Focus | `--bal-focus-ring` | The 2px ring `MuiCssBaseline`'s global `:focus-visible` override uses. **Don't hand-roll a focus style** — it is already uniform everywhere | | Rating | `--bal-rating`, `--bal-rating-empty` | `RatingInput` uses these, **not** `--bal-warning` | | Trust | `--bal-trust`, `--bal-trust-soft` | A distinct identity for verified marks — not primary, not success. `TrustBadge` and any future verification UI | | Money | `--bal-money-emphasis` | AA-contrast-safe emphasized money text. `--bal-secondary` (terracotta) **fails AA at small sizes on light backgrounds** — never use it for money text | | Avatar | `--bal-avatar-1..6` + each `-contrast` | Six warm pairs `InitialsAvatar` picks from by a deterministic name hash | | Map | `--bal-pin-shadow` | The address-picker pin | `--bal-chrome-top` / `--bal-chrome-bottom` are **not** in `tokens.css` — `AppFrame` publishes them at runtime on its scroll container. See [components.md](components.md). --- ## 3. Dark mode, and the no-flash boot The mechanism is **pure CSS. There is no boot script**, no inline `