Files
baya-monorepo/.claude/skills/frontend-designer/SKILL.md
T
2026-06-21 00:05:07 +03:30

238 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: frontend-designer
description: >-
Design and build UI for the Balinyaar client (Next.js 16 + MUI v9). Use when
creating or restyling any screen, page, component, layout, or visual in
client/ — turning a feature, mockup, or Figma design into branded, RTL-aware,
dark-mode-ready, i18n-complete React/MUI code. Covers the brand palette,
design tokens, typography, the App* component library, layout shells, icons,
and the hard rules every Balinyaar UI must follow.
---
# Balinyaar Frontend Designer
Build UI that looks like Balinyaar and behaves correctly in both locales and both
color schemes on the first try. This skill is the design contract; the engineering
contract (providers, fetch, cookies, routing) lives in [client/CLAUDE.md](../../../client/CLAUDE.md) — read it
before touching layout/provider/data code, **don't restate it**, and never violate it.
**Stack:** Next.js 16 (App Router, Turbopack) · React 19 · MUI v9 (`@mui/material`) ·
Emotion (RTL via `stylis-plugin-rtl`) · next-intl v4 · notistack. Everything below
lives under `client/src/`.
---
## 1. Brand identity
Balinyaar is a **trust-first home-nursing marketplace in Iran**. The visual tone is
calm, warm, clinical-but-human — not a cold medical dashboard. Default audience is
Persian (RTL); English is secondary.
**Logo mark** (`product/balinyaar.html` seed deck): deep-teal square, lowercase
display glyph in cream, a single terracotta dot. That trio — **teal ground, cream
text, terracotta accent** — is the whole identity. Use terracotta sparingly as the
single accent; teal carries everything else.
| 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 |
---
## 2. Design tokens — the two-layer system (read this before styling anything)
Colors exist in **two mirrored places** that must stay in sync. Pick the right one:
1. **MUI palette**`src/theme/colors.ts` (`BRAND`, `LIGHT_PALETTE`, `DARK_PALETTE`).
Drives `--mui-palette-*` and all MUI component coloring. Reach it through MUI APIs:
`color="primary"`, `sx={{ color: 'text.secondary', bgcolor: 'background.paper' }}`.
**This is the default for component styling.** It auto-switches with the color scheme.
2. **`--bal-*` CSS variables** — `src/theme/tokens.css`, defined under
`[data-mui-color-scheme='light'|'dark']`. The source of truth for **custom CSS
outside MUI's palette** and for **semantic feedback colors MUI doesn't define**:
`--bal-success`, `--bal-error`, `--bal-warning`, `--bal-info` (each `+ -contrast`).
Reference as `var(--bal-primary)`, `var(--bal-success-contrast)`, etc.
**Rules:**
- Styling a MUI component → use palette keys (`color="primary"`, `sx` palette refs).
- Need success/error/warning/info → use `--bal-*` tokens, **not** MUI defaults — the
MUI palette has 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** in `sx`, `styled`, or a component.
- Adding/changing a color means editing `tokens.css` **and** `colors.ts` together (the
file headers call out the sync requirement).
---
## 3. Typography & fonts
- `shape.borderRadius: 10` (set in `src/theme/theme.ts`) — the house corner radius.
Don't override per-component unless deliberate; prefer multiples that read as related.
- Buttons: `textTransform: 'none'`, weight 600 (set globally in `typography.ts`). Never
re-uppercase button text.
- Headings (`h1``h6`) use the display font; `h6` is weight 600, the rest 700.
- **Fonts are loaded per-locale in `src/app/[locale]/layout.tsx` only** — Mikhak
(`--font-mikhak`) for `fa`, system stack for `en` (Space Grotesk `--font-space-grotesk`
is declared but not yet wired). **Never load a font in a component or page.**
- Use `<Typography variant=…>` for text — it inherits the correct direction-aware family
(`TYPOGRAPHY_RTL` = Mikhak everywhere for full Persian glyph coverage; `TYPOGRAPHY_LTR`).
Import neither directly in components; let the theme apply them.
---
## 4. The component library — reach for these before raw MUI
Shared primitives live in `src/components/` (barrel: `@/components`). Prefer the `App*`
wrapper over the bare MUI component — the wrappers carry the house defaults.
| Component | Use for | Notes |
|-----------|---------|-------|
| `AppButton` | all buttons & button-links | default `variant="contained"`; pass `to`/`href` to render as a link automatically; `startIcon`/`endIcon` accept an **icon name string** (e.g. `startIcon="search"`) or a node; non-MUI `color` strings become text color |
| `AppIconButton` | icon-only actions | takes an icon `name`, `title`, `to`/`onClick` |
| `AppIcon` | any icon | `icon="home"` by registered name (§6); `size`, `color` props |
| `AppLink` | internal/external links | locale-aware Next navigation; default underline `hover` |
| `AppAlert` | inline alerts | default `severity="error"`, `variant="filled"` |
| `AppImage` | images | wrapper around next/image conventions |
| `AppLoading` | loading state | default circular, `primary`, `3rem` |
| `ErrorBoundary` | wrapping fault-prone subtrees | already wraps page content in the shell |
| `UserInfo` | user avatar/identity block | feature component |
Defaults for these live in `src/components/config.ts` (`APP_BUTTON_VARIANT`,
`APP_ICON_SIZE = 24`, `CONTENT_MAX_WIDTH = 800`, `CONTENT_MIN_WIDTH = 320`, alert/link/
loading defaults). Change a default there, not per-call-site.
For layout/spacing use MUI primitives directly: `Box`, `Stack`, `Container`, `Grid`,
`Paper`, `Card`. Use the `spacing`/`sx` system (theme spacing unit = 8px) — never inline
pixel margins for rhythm.
**New shared component?** Put it in `src/components/<Name>/<Name>.tsx` with an
`index.tsx` barrel, follow the `App*` prop-spreading + JSDoc style of `AppButton.tsx`,
and add a co-located `.test.tsx` (mandatory for anything imported in >1 place — see
CLAUDE.md "Unit Testing"; wrap with `<ThemeProvider>`, never mock MUI).
---
## 5. Layout & page shells
- **Private (authenticated) screens** render inside `PrivateLayout`
`TopBarAndSideBarLayout` (`src/layout/`): a `TopBar` + a `SideBar` (variant
`sidebarPersistentOnDesktop`: persistent ≥desktop, temporary drawer on mobile) +
a dark-mode toggle. Sidebar nav items are `{ title, path, icon }` arrays built with
`useTranslations('nav')`. Page content is auto-wrapped in `ErrorBoundary`.
- **Public screens** use `PublicLayout`.
- Shell dimensions are constants in `src/layout/config.ts` (`SIDE_BAR_WIDTH = 240px`,
top-bar `56px` mobile / `64px` desktop, anchors). Respect them; don't hard-code.
- A page is `src/app/[locale]/(private|public-routes)/…/page.tsx`. Keep page bodies to
composition + content; push reusable visuals into `src/components/`.
- Constrain reading width with `CONTENT_MAX_WIDTH` (800) for text-heavy views; full-bleed
is fine for dashboards/tables.
- Use `useIsMobile()` (`@/hooks`) for responsive branching, or MUI breakpoints in `sx`.
---
## 6. Icons
Icons are a **name registry**, not free imports. `src/components/common/AppIcon/config.ts`
maps lowercase names → MUI/SVG components. Render with `<AppIcon icon="home" />` or pass
the name to `AppButton`/`AppIconButton` (`icon="search"`).
Currently registered: `default, logo, close, menu, settings, visibilityon,
visibilityoff, daynight, night, day, search, info, home, account, signup, login,
logout, notifications, error`.
**Need a new icon:** import it into `config.ts`, add a **lowercase** key to `ICONS`, then
reference by that name. Custom SVGs go in `AppIcon/icons/`. An unregistered name logs a
warning and falls back to `default` — never pass a raw MUI icon where a name is expected.
---
## 7. Non-negotiable rules for every Balinyaar UI
Every screen/component you produce must satisfy **all** of these:
1. **i18n — no hard-coded user-facing strings.** Add the key to **both**
`messages/en.json` and `messages/fa.json` (keep them in sync; top-level keys are
namespaces). Client: `useTranslations(ns)`. Server: `getTranslations(ns)`.
2. **RTL-safe.** `fa` is the default locale and is **RTL**. Never use directional
hard-coding (`marginLeft`, `left:`, `textAlign: 'left'`) for layout flow — use
logical/MUI-flipped props (`ml`→ MUI flips; prefer `marginInlineStart`, `start`/`end`,
`sx` shorthand that the RTL Emotion cache mirrors). Test the layout visually at `/fa`.
Do **not** pass `flexWrap`/`useFlexGap` as `Stack` props — use `sx={{ flexWrap: 'wrap' }}`.
3. **Dark-mode correct.** Pull every color from the palette or `--bal-*` tokens so it
switches automatically. Verify on both schemes — never assume a light background.
4. **Tokens, not hexes.** No raw color literals in `sx`/`styled`/components (§2).
5. **Constants, not magic values.** Cookie names, routes, repeated dimensions, event
names → named constants (CLAUDE.md "Constants").
6. **Use the wrappers** (§4) and the **icon registry** (§6) before bare MUI.
7. **Shared component ⇒ co-located test** (§4).
8. **MUI v9 API only.** No v5/v6-era props (e.g. `Stack` `useFlexGap`, `storageWindow`).
Avoid deprecated APIs that throw.
---
## 8. Workflow: turning a design or feature into a screen
1. **Locate & scope.** Decide private vs public route; confirm the layout shell. Identify
which existing `App*` components and tokens already cover the design.
2. **Tokens first.** If the design needs a color/spacing not in the system, add the
`--bal-*` token (both schemes) + mirror in `colors.ts` if it's palette-level.
3. **Compose with primitives.** Build with `Box`/`Stack`/`Grid`/`Paper` + `App*`
wrappers. Keep raw MUI to layout/structural components.
4. **Wire copy through i18n.** Every label/placeholder/aria string → both message files.
5. **Verify the four axes:** `/fa` (RTL) and `/en` (LTR) × light and dark. The default
route is `/fa` — start there.
6. **Tests** for any new shared component; **never** add a layout above `[locale]`
(breaks locale/dir — see CLAUDE.md).
7. Data/fetch/auth/cookies/toasts → follow CLAUDE.md (`serverFetch`/`clientFetch`,
`@/lib/cookies/*`, `dispatchToast`/`useSnackbar`). Don't reinvent these.
---
## 9. Anti-patterns (design-specific — CLAUDE.md has the full engineering list)
- Hard-coded hex/rgb in components → use palette keys or `--bal-*` tokens.
- MUI default success/error colors for feedback → use `--bal-*` semantic tokens.
- `marginLeft`/`left`/`textAlign:'left'` for flow → breaks RTL; use logical props.
- Hard-coded English (or any) UI string → add to both message files.
- Loading a font in a component/page → fonts live only in `src/app/[locale]/layout.tsx`.
- `createTheme()` in a component → use `APP_THEME_LTR`/`APP_THEME_RTL`.
- Raw MUI icon where a registry name is expected → register it in `AppIcon/config.ts`.
- New shared component without a `.test.tsx`, or mocking MUI in tests.
- Re-introducing `src/app/layout.tsx` / any layout above `[locale]`.
---
## 10. Design ↔ Figma (optional)
A Figma MCP is connected. When the user provides a figma.com URL or asks to implement a
Figma frame, use the Figma tools (`get_design_context`, `get_screenshot`, `get_metadata`)
to pull the design, then map it onto **this** system: translate Figma colors to the
nearest `--bal-*`/palette token (don't introduce new hexes unless the design truly adds a
brand color), Figma type to `<Typography>` variants, and Figma components to the `App*`
library. Follow the Figma plugin skills (`/figma-use`, `/figma-generate-design`) when
pushing code back into Figma.
---
## Key files
| Concern | File |
|---------|------|
| Brand color tokens (CSS) | `client/src/theme/tokens.css` |
| MUI palette (mirror) | `client/src/theme/colors.ts` |
| Typography & fonts | `client/src/theme/typography.ts` |
| Theme factory (shape, schemes, dir) | `client/src/theme/theme.ts` |
| Theme provider / RTL cache | `client/src/theme/ThemeProvider.tsx` |
| Component library | `client/src/components/` (`@/components`) |
| Component defaults | `client/src/components/config.ts` |
| Icon registry | `client/src/components/common/AppIcon/config.ts` |
| Layout shells | `client/src/layout/` |
| Layout dimensions | `client/src/layout/config.ts` |
| Messages (i18n) | `client/messages/{en,fa}.json` |
| Engineering contract | `client/CLAUDE.md` |