Template
Quotara
A one-question welcome screen for a customer-feedback collection tool that asks how the new user heard about the product before sending them to their dashboard. It captures acquisition attribution without a form-heavy survey.
Welcome attribution survey (single choice) · 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 700, 32px/1.2, -0.02em
- InterBody: Inter 400 16px/1.5
Patterns
- welcome headline with emoji
- single-choice radio list
- attribution 'how did you hear' question
- primary CTA into dashboard
- minimal top bar with avatar
States it is designed for
- no selection
- selected
- Other with text
- submitting
- failed save (proceed anyway, retry in background)
- already answered (screen is skipped on next login)
Who it is for
- small business owners
- marketers
- founders collecting customer reviews
Layout
- top bar: round logo mark and wordmark left, avatar right, no nav
- centred column (~420px): large welcome headline with wave emoji
- question line
- vertical list of seven radio rows (channels + Other)
- Other reveals a short text field
- primary button 'Go to my dashboard' centred below
- mobile: full-width list with 16px gutters, sticky button
Palette
Friendly and brief; one quick favour before the real work starts.
- page background
#ffffff - headline
#111827 - body text
#374151 - muted note
#6b7280 - row border
#8b93a1 - selected row fill
#eef0ff - primary button
#4c5bd4 - error text
#c0262d
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | headline | 17.74:1 | 4.5:1 |
| Aa | body / row label | 10.31:1 | 4.5:1 |
| Aa | muted note | 4.83:1 | 4.5:1 |
| Aa | button label on primary | 5.59:1 | 4.5:1 |
| row border | 3.09:1 | 3:1 | |
| Aa | row label on selected fill | 9.10:1 | 4.5:1 |
| Aa | error text | 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. 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 700, 32px/1.2, -0.02em
- Body
- Inter 400 16px/1.5
Question 20px 400; row labels 16px 400; button label 16px 500.
Spacing and imagery
Rows 44px tall with 12px internal padding and 8px gaps; 24px between headline and question, 16px before list, 32px before the button. Radius 6px rows, 4px button. No shadows.
Only the wave and folded-hands emoji in text and the user's avatar; no illustrations.
Components
- TopBar (mark, wordmark, avatar menu)
- WelcomeHeadline
- RadioList
- OtherInput
- PrimaryButton
- SkipLink (text, optional)
Interactions
- clicking a row selects it and tints it
- arrow keys cycle the radio group
- button always enabled; with no choice it records 'skipped'
- Other focuses its text input immediately
- submit shows a spinner then routes to the dashboard
Data
AttributionAnswer{id, user_id, channel (search|social_a|social_b|social_c|blog|referral|other|skipped), other_text, created_at}Account{id, owner_id, onboarding_done bool}
Guardrails
Experience
- Label channels generically ('Search engine', 'A social network', 'A newsletter or blog', 'A friend or colleague'); never show real platform names.
- Never block the user: the dashboard is reachable even with no answer.
- Show the question once per account; mark the account done after submit.
- Keep emoji decorative and out of accessible names.
- Keep the list to at most eight options.
Accessibility
- Use a fieldset with the question as legend and native radios.
- Emoji are wrapped in aria-hidden spans.
- Selected rows show a filled radio and bolder border, not just tint.
- Button has a visible 2px focus ring offset from its edge.
- Headline is the page's single h1.
Security
- Validate channel against the enum; limit Other text to 60 characters and strip HTML.
- RLS: answers insert/select own only; aggregated stats read via a server view for admins.
- Store no extra PII with the answer (no IP, no user agent).
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 **Quotara**, the post-sign-up welcome screen of a tool that collects written and video reviews from customers. It asks one attribution question ('how did you find us?') with a single-choice list and sends the user to their dashboard. The answer feeds an internal acquisition report.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (RadioGroup, Button, Input, Avatar, DropdownMenu), lucide-react, zod, TanStack Query. Supabase Auth and Postgres, plus a SQL view for the admin report.
### Pages & layout
1. **/welcome**: top bar, headline, question, radio list, button.
2. **/dashboard**: placeholder with an empty 'Collect your first review' card so the redirect lands somewhere real.
3. **/admin/attribution**: tiny table of counts per channel for staff (role-gated).
### Design system
- Colors: `--bg: #ffffff` (page background), `--fg: #111827` (headline), `--body: #374151` (body text), `--muted: #6b7280` (muted note), `--row-border: #8b93a1` (row border), `--row-selected: #eef0ff` (selected row fill), `--primary: #4c5bd4` (primary button), `--error: #c0262d` (error text).
- Fonts: Inter 700, 32px/1.2, -0.02em for headings; Inter 400 16px/1.5 for body. Question 20px 400; row labels 16px 400; button label 16px 500.
- Spacing: Rows 44px tall with 12px internal padding and 8px gaps; 24px between headline and question, 16px before list, 32px before the button. Radius 6px rows, 4px button. No shadows.
- Radius: 6px rows, 4px button, full-round avatar.
- Shadows: none.
- Motion: 120ms row tint; 200ms fade into the dashboard.
### Components & interactions
TopBar, WelcomeHeadline, RadioList, OtherInput, PrimaryButton, AvatarMenu (Profile, Sign out), AttributionTable (admin).
Interactions: clicking a row selects it and tints it; arrow keys cycle the radio group; button always enabled; with no choice it records 'skipped'; Other focuses its text input immediately; submit shows a spinner then routes to the dashboard.
States to build: no selection; selected; Other with text; submitting; failed save (proceed anyway, retry in background); already answered (screen is skipped on next login).
### Data & state
Insert one `attribution_answers` row and set `accounts.onboarding_done = true` in a single RPC. Route guard skips /welcome when done. Seed the admin view with 60 invented answers.
Model: `AttributionAnswer{id, user_id, channel (search|social_a|social_b|social_c|blog|referral|other|skipped), other_text, created_at}`; `Account{id, owner_id, onboarding_done bool}`.
### Accessibility
Use a fieldset with the question as legend and native radios. Emoji are wrapped in aria-hidden spans. Selected rows show a filled radio and bolder border, not just tint. Button has a visible 2px focus ring offset from its edge. Headline is the page's single h1.
Verified contrast: headline: #111827 on #ffffff = 17.74:1; body / row label: #374151 on #ffffff = 10.31:1; muted note: #6b7280 on #ffffff = 4.83:1; button label on primary: #ffffff on #4c5bd4 = 5.59:1; row border: #8b93a1 on #ffffff = 3.09:1; row label on selected fill: #374151 on #eef0ff = 9.1:1; error text: #c0262d on #ffffff = 5.9:1.
### Security
Validate channel against the enum; limit Other text to 60 characters and strip HTML. RLS: answers insert/select own only; aggregated stats read via a server view for admins. Store no extra PII with the answer (no IP, no user agent). RLS per table: `attribution_answers` insert/select where `user_id = auth.uid()`; `accounts` select/update by owner; the admin view is exposed through a security-definer function that checks a `staff` role claim.
### Performance & SEO
Welcome page is a tiny chunk; prefetch the dashboard chunk on mount. noindex on all app routes.
### Guardrails
- Label channels generically ('Search engine', 'A social network', 'A newsletter or blog', 'A friend or colleague'); never show real platform names.
- Never block the user: the dashboard is reachable even with no answer.
- Show the question once per account; mark the account done after submit.
- Keep emoji decorative and out of accessible names.
- Keep the list to at most eight options.
- 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:
- [ ] Choosing a channel and continuing saves one row
- [ ] Continuing without a choice records 'skipped'
- [ ] The screen never shows again for that account
- [ ] Admin counts update
- [ ] Keyboard-only users can complete the step