Template
Norrick
The final step of a money-movement flow where a user schedules an automatic conversion between two balances. A single card restates amount, source balance, rate condition, fee and estimated result so the user can confirm or jump back to edit.
Review-and-confirm step for a currency conversion · App screen: confirmation · Small tools and apps · front-end app (local state)
A mock-up of the screen, drawn from its layout, palette and typefaces. A build follows the full prompt below.
Start from this screenRead the build prompt
Typefaces
The catalog's own faces. A screen composed into a template is drawn in that template's typefaces.
- InterHeadings: Inter 600 24px page title; 20px 700 for the converted amount
- InterBody: Inter 400 16px / 1.5 for rows; row labels muted 16px
Patterns
- focused flow header with progress line and step labels
- single centered review card
- label/value summary rows with emphasised totals
- inline Edit link back to previous step
- full-width pill primary button
- close (X) to exit flow
States it is designed for
- review ready
- rate refreshing
- rate quote expired (button disabled, Refresh quote action)
- insufficient balance error
- submitting
- success: 'Conversion scheduled' with manage link
- server error with retry
- limit reached for automatic rules
Who it is for
- individuals and freelancers holding multi-currency balances
- small businesses paying suppliers abroad
Layout
- Flow header: wordmark left, a thin progress line with step labels (Amount, Confirm) centred, profile button and close icon right; 1px bottom border
- Page title centred ('Review your conversion')
- Review card (max 340px): header row 'Conversion details' + Edit link; label/value rows (you convert, from balance, when rate is at or better than, fee); bold total row with large converted amount; full-width pill Continue button
- Below the card: small print about rate movement and cancellation
- Mobile: header shrinks to back arrow + step count + close; card fills width with 16px gutter; button sticks to bottom safe area
Palette
Clean, confident and financial; lots of white with a single bright lime action.
- page
#ffffff - text
#0e0f0c - muted text
#5d6359 - card border
#e8ebe6 - primary lime
#9fe870 - on primary
#163300 - brand green text
#2f5711 - progress track
#c9cfc5 - progress fill
#163300 - focus ring
#163300 - danger
#c0281c
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on white | 19.23:1 | 4.5:1 |
| Aa | muted labels on white | 6.19:1 | 4.5:1 |
| Aa | Continue label on lime | 9.45:1 | 4.5:1 |
| Aa | Edit link on white | 8.43:1 | 4.5:1 |
| progress fill on white | 13.93:1 | 3:1 | |
| focus ring on lime button | 9.45:1 | 3:1 | |
| Aa | error text on white | 5.90:1 | 4.5:1 |
As vibld’s tokens
The palette on the fifteen colour tokens vibld styles a project with, each text colour on the fill it is read on.
- background
- card
- muted
- primary
- secondary
- accent
- destructive
Type scale
- Display
- Inter 600 24px page title; 20px 700 for the converted amount
- Body
- Inter 400 16px / 1.5 for rows; row labels muted 16px
Similar to the observed neo-grotesk; amounts use tabular numerals and always carry the currency code.
Spacing and imagery
Airy; 4px base, 48px under the header, 24px card padding, 12px between rows; radius 8px card, 999px button; no shadows, only a 1px border.
None; icons limited to profile, close and a small info icon on the rate row.
Components
- FlowHeader with StepProgress
- ReviewCard
- SummaryRow (label, value, optional info tooltip)
- TotalRow
- EditLink
- PillButton
- RateDisclaimer
Interactions
- Edit returns to the Amount step with values preserved
- Continue shows a spinner in the pill, then routes to a success screen
- Rate row info icon opens a popover explaining 'or better'
- Close asks 'Leave without scheduling?' if the user has entered data
- Rate refreshes every 30s; a changed estimate animates the total and shows 'updated' text for 3s
Data
Balance{id, currency, amount}ConversionRule{id, from_balance_id, to_currency, amount, trigger_rate, fee, status (draft|active|done|canceled)}RateQuote{pair, rate, fetched_at, expires_at}
Guardrails
Experience
- One card, one primary action; nothing else competes
- Bold the amounts that matter (you send, you get)
- Always show fee as its own row
- Let users edit without starting over
- Explain rate conditions in plain words
Accessibility
- The progress indicator exposes 'Step 2 of 2, Confirm' as text
- Summary uses a description list (dl/dt/dd)
- Rate updates are announced politely only when the total changes
- Edit link's accessible name is 'Edit conversion details'
- The dark focus ring stays visible on the lime button and on white
Security
- In the real app, re-validate amount, balance and fee server-side and use an idempotency key on submit
- Quotes expire; never submit an expired quote
- Require re-authentication for rules above a threshold
- Do not log full balances in client analytics
Build prompt
The baseline every prompt in the catalog assumes, then this design’s own ten sections, from goal to guardrails.
The baseline
### How to use these prompts Paste an entry's build prompt into your coding agent as the first message. Each prompt names its own stack, tokens and acceptance criteria; the rules below apply to all of them and can be prepended once per project. ### Engineering baseline - TypeScript strict mode, no `any`, small typed components, feature folders, and one source of truth for design tokens (CSS variables consumed by Tailwind). - Validate every input with a shared zod schema on the client and again on the server or edge function. Never trust client-side checks alone. - Show loading, empty and error states for every async view. Surface errors in plain language with a retry, and log details to the console in development only. - Keep secrets out of the bundle. Only publishable keys (for example a Supabase anon key) belong in client code; service-role keys, API keys and webhooks live in server or edge-function environment variables. ### Data and auth baseline (full-stack entries) - Enable Row Level Security on every table before inserting data. Default-deny, then add owner-scoped policies (`auth.uid() = user_id`) and explicit role checks for admin views. - Store roles in a separate table checked by a security-definer function, never in a user-editable profile field. - Upload files to private storage buckets with size and MIME limits, and serve them through signed URLs. - Rate-limit public endpoints (forms, auth, AI calls) and add a honeypot field or captcha to anonymous forms. - Take payments through a hosted checkout and verify webhooks by signature. Never handle raw card data. ### Accessibility and UX baseline - Target WCAG 2.2 AA: 4.5:1 contrast for normal text and 3:1 for large text, input borders, focus rings and meaningful icons or chart lines. Every palette in this catalog lists its verified pairs; re-check with a contrast tool after any colour change. - Keep body text at 16px or larger with 1.5 line height, nothing below 12px, no light weights under 24px, and uppercase only for short labels. - Give every interactive element a visible focus ring, full keyboard support, semantic landmarks, labelled form fields, and alt text on meaningful images. - Respect `prefers-reduced-motion` for every animation. Give drag-and-drop and carousels keyboard and button alternatives. - Build mobile-first and test at 375px, 768px and 1280px. ### Content guardrails - Use original copy, fictional sample data and placeholder or licensed imagery. Do not reuse another product's name, logo, screenshots or marketing text. - Label demo testimonials and metrics as samples. Collect the minimum personal data the feature needs. ### SaaS screen baseline - Design every screen for its full set of states: first-run empty, loading skeleton, partial data, error with retry, permission-denied, and success feedback. Each entry lists the states its screen needs. - Keep destructive actions (delete, revoke, downgrade, remove member) behind a confirmation that names the object, and prefer undo over a second dialog where the action is reversible. - Enforce authorisation on the server for every action a screen exposes. Hiding a button is not access control; check the role again in the API or RLS policy. - Never show secrets (API keys, tokens) in full after creation. Show them once, then mask them, and offer rotate and revoke. - Keep the app shell (navigation, workspace switcher, account menu) consistent across screens, and preserve filters, sort and scroll position when the user navigates back.
### Goal
Build **Norrick**, the review-and-confirm step of a scheduled currency conversion flow: flow header with progress, a summary card and a single action. Use fictional balances and a mocked rate feed, and implement all quote and submission states.
### Stack
Use React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, Radix, lucide-react, react-hook-form, zod, date-fns. Keep everything in local state backed by a typed mock-data module and a fake async API (200-600 ms latency, a toggle to force errors) so every state can be demonstrated without a backend.
### Pages & layout
1. **Amount step** (simple form: amount, from balance, target rate) so Edit has somewhere to go.
2. **Confirm step**: the review card.
3. **Done step**: confirmation with the rule summary and a link to manage rules.
Regions, in order:
- Flow header: wordmark left, a thin progress line with step labels (Amount, Confirm) centred, profile button and close icon right; 1px bottom border
- Page title centred ('Review your conversion')
- Review card (max 340px): header row 'Conversion details' + Edit link; label/value rows (you convert, from balance, when rate is at or better than, fee); bold total row with large converted amount; full-width pill Continue button
- Below the card: small print about rate movement and cancellation
- Mobile: header shrinks to back arrow + step count + close; card fills width with 16px gutter; button sticks to bottom safe area
### Design system
- Colors: `--page: #ffffff` (page), `--text: #0e0f0c` (text), `--muted-text: #5d6359` (muted text), `--card-border: #e8ebe6` (card border), `--primary-lime: #9fe870` (primary lime), `--on-primary: #163300` (on primary), `--brand-green-text: #2f5711` (brand green text), `--progress-track: #c9cfc5` (progress track), `--progress-fill: #163300` (progress fill), `--focus-ring: #163300` (focus ring), `--danger: #c0281c` (danger).
- Fonts: Inter 600 24px page title; 20px 700 for the converted amount for headings; Inter 400 16px / 1.5 for rows; row labels muted 16px for body. Similar to the observed neo-grotesk; amounts use tabular numerals and always carry the currency code.
- Spacing, radius and shadows: Airy; 4px base, 48px under the header, 24px card padding, 12px between rows; radius 8px card, 999px button; no shadows, only a 1px border.
- Motion: 150-200 ms ease-out for hover, focus and overlay transitions; overlays fade and scale from 98% to 100%; everything collapses to an instant change under prefers-reduced-motion.
- Mood: Clean, confident and financial; lots of white with a single bright lime action. Imagery: None; icons limited to profile, close and a small info icon on the rate row.
### Components & interactions
Build these components: FlowHeader with StepProgress; ReviewCard; SummaryRow (label, value, optional info tooltip); TotalRow; EditLink; PillButton; RateDisclaimer.
- Edit returns to the Amount step with values preserved
- Continue shows a spinner in the pill, then routes to a success screen
- Rate row info icon opens a popover explaining 'or better'
- Close asks 'Leave without scheduling?' if the user has entered data
- Rate refreshes every 30s; a changed estimate animates the total and shows 'updated' text for 3s
### Data & state
Model: `Balance{id, currency, amount}`; `ConversionRule{id, from_balance_id, to_currency, amount, trigger_rate, fee, status (draft|active|done|canceled)}`; `RateQuote{pair, rate, fetched_at, expires_at}`.
Keep flow state in a small reducer persisted to sessionStorage so refresh doesn't lose the draft. Format money with Intl.NumberFormat and never float-add amounts (use integer minor units).
States to implement and demo:
- review ready
- rate refreshing
- rate quote expired (button disabled, Refresh quote action)
- insufficient balance error
- submitting
- success: 'Conversion scheduled' with manage link
- server error with retry
- limit reached for automatic rules
### Accessibility
- The progress indicator exposes 'Step 2 of 2, Confirm' as text
- Summary uses a description list (dl/dt/dd)
- Rate updates are announced politely only when the total changes
- Edit link's accessible name is 'Edit conversion details'
- The dark focus ring stays visible on the lime button and on white
- Body text is 16px with line-height 1.5 (15px only inside dense tables), nothing renders below 12px, weights of 300 or lighter appear only at 24px and above, and uppercase is limited to short labels with at least 0.05em tracking.
Verified contrast: body text on white: #0e0f0c on #ffffff = 19.23:1; muted labels on white: #5d6359 on #ffffff = 6.19:1; Continue label on lime: #163300 on #9fe870 = 9.45:1; Edit link on white: #2f5711 on #ffffff = 8.43:1; progress fill on white: #163300 on #ffffff = 13.93:1; focus ring on lime button: #163300 on #9fe870 = 9.45:1; error text on white: #c0281c on #ffffff = 5.90:1.
### Security
- In the real app, re-validate amount, balance and fee server-side and use an idempotency key on submit
- Quotes expire; never submit an expired quote
- Require re-authentication for rules above a threshold
- Do not log full balances in client analytics
### Performance & SEO
Tiny route; prefetch the done step. Noindex. Avoid layout shift on rate refresh by fixing row heights.
### Guardrails
- One card, one primary action; nothing else competes
- Bold the amounts that matter (you send, you get)
- Always show fee as its own row
- Let users edit without starting over
- Explain rate conditions in plain words
- Use the product name Norrick and fresh, generic copy throughout; all people, companies, amounts and IDs are invented, and no third-party brand, logo or wordmark appears.
- Keep components small and typed (no `any`), and surface every failure visibly instead of swallowing it.
Acceptance criteria:
- [ ] Edit round-trip preserves values
- [ ] Expired quote blocks submit with refresh action
- [ ] Amounts formatted with currency codes and no float errors
- [ ] Works at 390px
- [ ] Contrast pairs pass