Figma alignment, batch 1: implementation plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task by task.
Goal: Bring the web app’s existing screens in line with the Figma “Final Design” page wherever that needs no new data and no product decision.
Architecture: This is styling and layout work in web/src only. Design tokens change once, in styles/tokens.css, and components consume them. No API, schema, or Go change. Every row this plan fixes is a numbered row of the audit.
Tech stack: React 19, Tailwind v4 tokens, Radix primitives, i18next (en/ms/zh), Vitest, and Playwright (existing specs only).
Spec: docs/superpowers/specs/2026-09-25-figma-alignment-audit.md. The audit is the spec: each task cites its rows by number, e.g. “3.7”. Figma file xjx0pz8JSVeCqP1kYg3elT, page “Final Design” (77:4027). The audit gives each row’s node id; read exact values with the Figma MCP tools (get_design_context on the node, after reading skill://figma/figma-design-to-code/SKILL.md), and never guess.
Global constraints
Section titled “Global constraints”- Out of scope: every row marked [PD], and everything in audit §12 (“App has, Figma doesn’t — keep”). Do not remove any feature, nav item, locale, hint, or control the app has.
- Tokens: colours, radii and sizes go in
web/src/styles/tokens.cssas tokens. Components use tokens; no new hex literals in components. Dark-theme tokens must still be defined for every token you add or change. - Contrast: the status/tone text colours stay as they are (audit 0.2 is [PD]). Soft tints, borders, backgrounds and shapes may change.
- Assets: raster assets ship as WebP (animation as WebM). SVG stays SVG. The sidebar astronaut is already at
web/src/assets/sidebar-astronaut.webp(304×320, a 2× asset for about 151×161 display). Do not shipcompany-logo.png. - Money and i18n: a float never touches money (
web/src/lib/money.tsonly). Every user-facing string is an i18n key in en, ms and zh, and ms must be genuine Malay. - Tests: keep all existing vitest and e2e green. Update test selectors only where markup legitimately changed. Add a vitest case for any behaviour change (tabs not scrolling, title per tab, footer per state). While iterating, run
pnpm -C web test -- <path>. Before committing, runpnpm -C web typecheck && pnpm -C web lint && pnpm -C web test. - Runtime check: each task loads its screens in the running app at 1920×1080 and 1280×800, light and dark, and compares them against the Figma frame renders in
/private/tmp/claude-501/-Users-limziyang-Documents-repos-mcomtask/211212d1-b84f-40e0-bd2b-3ef9b4196ddd/scratchpad/figma/(e.g.my-claims.png,petty-cash.png,wizard-*.png,detail-*.png).- The API runs on :3000.
- Start vite from this worktree:
pnpm -C web dev --port 5190 --strictPort, in the background, and stop it when done. - Signing in: an agent must not type passwords. Use the e2e helper pattern (
web/e2e/support.tslogin) from a throwaway Playwright script outside the repo, or report that the check could not be run.
- Commits end with
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>.
Task FA1: Shared tokens and primitives
Section titled “Task FA1: Shared tokens and primitives”Audit rows: 0.1, 0.3, 0.4, 0.5, 0.6, 3.7, 3.8, 3.9, 3.14, 3.15, 3.17 (shape only), 3.18, 3.22, 9.7, 9.10.
Files:
web/src/styles/tokens.cssweb/src/components/ui/{Tabs,DataTable,Tag}.tsxweb/src/components/workflow/semanticTone.tsweb/src/features/claims/CategoryPills.tsx- their tests
Changes:
- Card radius becomes 20px.
- Soft tints and the table-header/top-bar background take the Figma values.
- Row hover takes the Figma light blue (as a token, with a dark variant).
- The paid (
payout) chip becomes the green outline chip. - Tabs:
- The tablist never scrolls vertically: fix the
-mb-px/overflow-x-autocause described in 3.8. - Figma tab styling (3.7): fixed-width items, 20px icons, active text primary, a rounded 2px underline under the label only, and no full-width rule.
- Vitest: the list’s
scrollHeight <= clientHeight, or assert the class change that removes the cause.
- The tablist never scrolls vertically: fix the
- DataTable:
- Draws no frame of its own when nested in a framed container. Add a prop such as
framed={false}, and use it wherever a table sits inside a card section. - Header text is black regular.
- Row height 54px.
- The actions column header and cell are centred, with a 24px kebab.
- Draws no frame of its own when nested in a framed container. Add a prop such as
- Tags:
- Status chips: radius 50, padding 6/16, white background; the draft chip gets a
#d3d2dc-equivalent border token. - Category pill shape: radius 10, padding 6/16. Colour stays as is (per-category colour is [PD]).
- Status chips: radius 50, padding 6/16, white background; the draft chip gets a
Commit: Figma batch 1: shared tokens, tabs, table and chips.
Task FA2: Shell — sidebar and header
Section titled “Task FA2: Shell — sidebar and header”Audit rows: 1.1 (hidden scrollbar and tighter spacing; the nav item count stays), 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10 (style only, no logo), 1.11, 2.1, 2.5 (colour and size only; photo and job title are [PD]).
Files:
web/src/app/shell/{Sidebar,OrgSwitcher,TopBar,UserMenu}.tsxweb/src/app/shell/nav.tsweb/src/styles/tokens.css- tests
Changes:
- Solid sidebar background, 237px wide.
- Group headings keep their i18n text but lose the uppercase transform: 14px bold white.
- A 1px 20%-white divider above every group after the first.
- Nav items: 20px icons, gap 8, padding 8/16, radius 10, regular weight. The active item is a solid primary fill.
- The astronaut sits at the bottom-right behind the nav: mirrored, 30% opacity,
aria-hidden,alt="", not interactive. It is hidden when the sidebar is collapsed to icons and in the mobile drawer. - Icons: pick the closest lucide equivalents to Figma’s set for the items Figma shows (e.g.
LayoutGrid,FolderOpen,Activity,ReceiptText→ a $-document,Wallet,Users,Calculator). Keep the other items’ icons consistent in weight. - The scrollbar is hidden (
scrollbar-width: noneplus the WebKit rule) while scrolling still works, so small windows can reach every item. - Header: 55px, no bottom border, Figma background.
- User chip: white pill, h34.
- Collapse mode and the mobile drawer must keep working.
Commit: Figma batch 1: sidebar and header.
Task FA3: Claims list — My claims, My approvals, All claims
Section titled “Task FA3: Claims list — My claims, My approvals, All claims”Audit rows: 3.1, 3.2, 3.3, 3.4, 3.5 (the approved vs paid label distinction may use the existing green tokens; no new text colours), 3.6, 3.10, 3.12 (size and placeholder copy only; search semantics unchanged), 3.13, 3.20, 3.21, 3.23, 4.1, 4.2, 5.1, 5.4, 6.1.
Files:
web/src/features/claims/{ClaimsListPage,ClaimsTable}.tsxweb/src/components/ui/{PageHeader,StatCard,Button}.tsxweb/src/components/workflow/StateFilterChips.tsxweb/src/lib/format(date and time on two lines)- the claims locale files
- tests
Behaviour changes, each needing a vitest:
- The page title and subtitle follow the tab. The subtitle counts:
- on My claims: my claims;
- on All claims: the org’s claims, within the existing summary scopes;
- on My approvals: those awaiting me.
- The New claim button is shown on My claims only.
- All claims has no actions column.
- The My approvals tab is shown only to a member who can act on claims: gate it on the approvals count or on an existing permission the session exposes. Say which in the report.
Commit: Figma batch 1: claims list.
Task FA4: Wizard and petty-cash dialogs
Section titled “Task FA4: Wizard and petty-cash dialogs”Audit rows: 7.1, 7.2, 7.3, 7.4, 7.5, 7.8, 7.9, 7.10 (a currency Select, only if a currency list already exists client-side or in the API; otherwise leave it and say so), 7.12, 7.14, 10.1, 10.3 (style only), 11.1, 11.3 (style only).
Files:
web/src/components/ui/{Dialog,Field,Input}.tsxweb/src/features/claims/wizard/*web/src/components/upload/DocumentUploader.tsxweb/src/features/pettycash/{RequestFloatDialog,ReturnFloatDialog}.tsx- tests
Changes:
- A dialog
sizeof 700px with radius 20 and padding 32. - An optional tinted header band that can hold the stepper.
- Field label 14px bold, input border in the light outline token, h36, radius 10.
- The wizard’s repeated step heading is removed.
- All existing wizard fields and Plan 11’s ChargeTo stay.
Commit: Figma batch 1: wizard and dialogs.
Task FA5: Claim detail drawer
Section titled “Task FA5: Claim detail drawer”Audit rows: 8.1, 8.3, 8.4, 8.5 (only if receipts already support downloading), 8.6, 8.2.
Files:
web/src/components/ui/Drawer.tsxweb/src/features/claims/detail/*- tests
Changes:
- Drawer 470px wide.
- Amount banner tinted by state, including a rejected variant that shows the rejection reason, which the view already carries.
- Approver actions sit under the banner at equal width.
- 基本信息 is collapsible: four rows collapsed, all rows expanded.
- 8.2, the footer by state, uses existing transitions and the existing wizard only. Map each footer button to an available transition or to “edit draft”; no new endpoint:
- claimant pending: cancel + edit;
- rejected: cancel + resubmit;
- closed: download receipts + close.
- A state with no mapped action keeps Close.
- Vitest: one test per footer case.
Commit: Figma batch 1: claim detail.
Task FA6: Petty cash page
Section titled “Task FA6: Petty cash page”Audit rows: 9.1 (style only; Record spend stays), 9.2 (style, fixed card widths, and a progress bar for Available as available/cash-balance computed from money strings in lib/money.ts; card 2’s project total is [PD], so keep the current card 2 content in the new style), 9.3 (always rendered, with an empty state; Figma styling; the columns keep the app’s data), 9.5 (stacked for now), 9.8 (a date column with the time on a second line; signed, coloured amounts where the direction is known; an actions kebab only if a row action already exists), 9.9, 9.11 (a “view record” link on the success toast, only if the toaster supports actions).
Files:
web/src/features/pettycash/*web/src/components/ui/StatCard.tsx(only if shared)- the petty cash locale files
- tests
Commit: Figma batch 1: petty cash.
FA1 → FA2 → FA3 → FA4 → FA5 → FA6. FA1’s tokens and primitives feed the rest.
Self-review
Section titled “Self-review”- Coverage: every non-[PD] audit row in sections 0–11 is assigned to a task, except:
- 3.11 Export: a spec gap, but M, and a behaviour decision about scope.
- 7.6, 7.11, 7.13, 8.7, 8.8, 9.4, 9.6, 10.2 and 11.2: [PD] or need data.
- 3.16, 3.19 and 3.24: [PD].
- Constraint check: no task removes a §12 feature, and text colours are untouched because 0.2 is [PD].