Template
Pathquire
One step of a contact-data enrichment tool's onboarding that asks which CRM the team uses, so the next step can offer the right sync. It is for sales and revenue-ops users signing up and needs to be scannable in seconds with a long list of options.
Onboarding question with two-column radio tile grid · App screen: discovery questions · Small tools and apps · full-stack app (auth + DB)
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/1.3
- InterBody: Inter 400 16px/1.5
Patterns
- full-width progress bar under top bar
- question heading with required marker
- two-column radio tile grid
- 'none' and 'other' escape options
- back / next button pair
- signed-in identity with log-out link
- floating support chat bubble
States it is designed for
- nothing selected (Next disabled)
- tile selected
- Other selected with empty text (inline validation)
- saving (Next shows spinner)
- save failed (toast with retry, selection kept)
- returning to the step (previous answer restored)
- last step (Next label becomes Finish)
Who it is for
- sales reps
- revenue operations managers
- agency founders
Layout
- top bar: wordmark left; 'Signed in as {email}' plus underlined log-out link right
- 4px progress bar directly under the top bar, filled to the step fraction
- centred column (~560px): question heading with asterisk
- two-column grid of radio tiles (7 rows), last row holds 'I don't use a CRM' and 'Other'
- Other reveals a text input below the grid
- button row: outlined Back, filled Next
- floating round chat launcher bottom-right
- on mobile the grid becomes one column and the button row sticks to the bottom
Palette
Efficient, neutral, a little eager; the purple bar and button carry all the colour.
- page background
#ffffff - primary text
#1f2328 - secondary text
#57606a - tile border
#8c959f - tile selected fill
#f1ecfe - primary / progress fill
#6a3de8 - progress track
#e4e4e7 - required asterisk
#6e7781 - error text
#c62828
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text | 15.80:1 | 4.5:1 |
| Aa | muted text / identity | 6.39:1 | 4.5:1 |
| Aa | Next label on primary | 6.09:1 | 4.5:1 |
| tile border | 3.04:1 | 3:1 | |
| Aa | tile text on selected fill | 13.67:1 | 4.5:1 |
| progress fill vs track | 4.80:1 | 3:1 | |
| Aa | error text | 5.62: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. Marked tokens are solved from the palette, because no swatch held that role at 4.5:1.
- background
- card
- muted
- primary
- secondary
- accent
- destructive *
Type scale
- Display
- Inter 600, 24px/1.3
- Body
- Inter 400 16px/1.5
Tile labels 16px 400, selected tiles 500; top bar 14px; asterisk in muted grey with an sr-only 'required'.
Spacing and imagery
Moderate density: tiles 44px tall, 8px gap, 12px internal padding; 24px under the heading; 16px above buttons. Radius 6px tiles and buttons; no shadows except the chat launcher (0 4px 12px rgba(0,0,0,0.15)).
None beyond a small line-art wordmark glyph and the chat icon. Option tiles are text-only; do not add third-party logos.
Components
- TopBar (wordmark, identity, log-out)
- StepProgress
- QuestionHeading
- RadioTileGrid
- OtherInput (conditional)
- ButtonRow (Back, Next)
- ChatLauncher
Interactions
- whole tile is clickable; the radio dot fills and the tile tints
- arrow keys move selection within the radio group
- Next is disabled until a choice is made (and Other text is filled when Other is chosen)
- progress bar animates to the next fraction on Next
- Back preserves the current selection
- Enter on a selected tile advances
Data
OnboardingStep{key, order, question, kind (single|multi|text), required}OnboardingResponse{id, user_id, step_key, value, other_text, created_at}Workspace{id, owner_id, crm (enum of generic CRM ids|none|other), onboarding_completed_at}
Guardrails
Experience
- Keep the option list alphabetical or by popularity, with 'none' and 'Other' always last.
- Use invented or generic option labels in mocks (e.g. 'Pipeline CRM A', 'Spreadsheet'); never real vendor names.
- Show step progress both as the bar and as 'Step 3 of 6' text for screen readers.
- Keep the chat launcher from covering the button row on small screens.
- Remember answers so going Back never clears them.
Accessibility
- Tiles are a native radio group inside a fieldset whose legend is the question.
- Required is conveyed with aria-required and sr-only text, not only the asterisk.
- Progress bar uses role=progressbar with aria-valuenow/max and a text label.
- Selected state is shown by fill, border weight and the filled radio dot, not colour alone.
- Focus ring is 2px primary with 2px offset on tiles and buttons.
Security
- Validate the value against the allowed enum server-side; cap Other text at 80 characters.
- RLS: responses and workspace fields writable only by workspace owners.
- Mask the email in the top bar on shared screens (show on hover/focus).
- Log-out is a POST with CSRF protection, not a GET link.
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 **Pathquire**, a multi-step onboarding for a contact-enrichment SaaS, focusing on the step that asks which CRM the team uses. The answer decides which sync is offered next. The screen must handle a long list of choices, required validation, back/next navigation and resumable progress.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (RadioGroup, Button, Input, Progress) with lucide-react. react-hook-form + zod per step, TanStack Query for loading and saving responses. Supabase Auth and Postgres.
### Pages & layout
1. **/onboarding/:step** shell: top bar, progress bar, centred step body, button row, chat launcher.
2. Steps: role, team size, **CRM (this screen)**, main use case, sync offer, done.
3. Mobile: one-column tiles, sticky button row with safe-area padding.
### Design system
- Colors: `--bg: #ffffff` (page background), `--fg: #1f2328` (primary text), `--muted: #57606a` (secondary text), `--tile-border: #8c959f` (tile border), `--tile-selected: #f1ecfe` (tile selected fill), `--primary: #6a3de8` (primary / progress fill), `--track: #e4e4e7` (progress track), `--required: #6e7781` (required asterisk), `--error: #c62828` (error text).
- Fonts: Inter 600, 24px/1.3 for headings; Inter 400 16px/1.5 for body. Tile labels 16px 400, selected tiles 500; top bar 14px; asterisk in muted grey with an sr-only 'required'.
- Spacing: Moderate density: tiles 44px tall, 8px gap, 12px internal padding; 24px under the heading; 16px above buttons. Radius 6px tiles and buttons; no shadows except the chat launcher (0 4px 12px rgba(0,0,0,0.15)).
- Radius: 6px tiles and buttons; launcher fully round.
- Shadows: launcher only.
- Motion: progress width 300ms ease-out; tile tint 120ms; step content cross-fade 200ms.
### Components & interactions
TopBar, StepProgress, QuestionHeading, RadioTileGrid (generic options: 'Pipeline CRM A', 'Pipeline CRM B', 'Enterprise suite', 'Recruiting CRM', 'Lightweight CRM', 'Spreadsheet', 'I don't use a CRM', 'Other'), OtherInput, ButtonRow, ChatLauncher (opens a placeholder panel).
Interactions: whole tile is clickable; the radio dot fills and the tile tints; arrow keys move selection within the radio group; Next is disabled until a choice is made (and Other text is filled when Other is chosen); progress bar animates to the next fraction on Next; Back preserves the current selection; Enter on a selected tile advances.
States to build: nothing selected (Next disabled); tile selected; Other selected with empty text (inline validation); saving (Next shows spinner); save failed (toast with retry, selection kept); returning to the step (previous answer restored); last step (Next label becomes Finish).
### Data & state
Step definitions live in a typed config array. Responses upsert per `(user_id, step_key)`. The last visited step is stored on the workspace so a returning user resumes there. Seed a demo user mid-way through onboarding.
Model: `OnboardingStep{key, order, question, kind (single|multi|text), required}`; `OnboardingResponse{id, user_id, step_key, value, other_text, created_at}`; `Workspace{id, owner_id, crm (enum of generic CRM ids|none|other), onboarding_completed_at}`.
### Accessibility
Tiles are a native radio group inside a fieldset whose legend is the question. Required is conveyed with aria-required and sr-only text, not only the asterisk. Progress bar uses role=progressbar with aria-valuenow/max and a text label. Selected state is shown by fill, border weight and the filled radio dot, not colour alone. Focus ring is 2px primary with 2px offset on tiles and buttons.
Verified contrast: body text: #1f2328 on #ffffff = 15.8:1; muted text / identity: #57606a on #ffffff = 6.39:1; Next label on primary: #ffffff on #6a3de8 = 6.09:1; tile border: #8c959f on #ffffff = 3.04:1; tile text on selected fill: #1f2328 on #f1ecfe = 13.67:1; progress fill vs track: #6a3de8 on #e4e4e7 = 4.8:1; error text: #c62828 on #ffffff = 5.62:1.
### Security
Validate the value against the allowed enum server-side; cap Other text at 80 characters. RLS: responses and workspace fields writable only by workspace owners. Mask the email in the top bar on shared screens (show on hover/focus). Log-out is a POST with CSRF protection, not a GET link. RLS per table: `onboarding_responses` select/insert/update where `user_id = auth.uid()`; `workspaces` select for members, update only for `owner_id = auth.uid()`; `onboarding_steps` read-only to authenticated users.
### Performance & SEO
Prefetch the next step's config and saved response. Keep each step under 20kB of JS. Onboarding is noindex.
### Guardrails
- Keep the option list alphabetical or by popularity, with 'none' and 'Other' always last.
- Use invented or generic option labels in mocks (e.g. 'Pipeline CRM A', 'Spreadsheet'); never real vendor names.
- Show step progress both as the bar and as 'Step 3 of 6' text for screen readers.
- Keep the chat launcher from covering the button row on small screens.
- Remember answers so going Back never clears them.
- Write all copy fresh; use invented people, companies and numbers only. No real brands, logos, wordmarks or third-party product names anywhere in the UI or seed data.
- Do not trace or copy any existing product's layout assets, icons or illustrations; draw generic ones.
- Acceptance criteria:
- [ ] Next stays disabled until a valid choice
- [ ] Other requires text
- [ ] Back keeps the previous answer
- [ ] Refresh resumes on the same step with the answer restored
- [ ] Keyboard users can pick an option with arrow keys and advance with Enter