Template
Tavisto
The results page after uploading a CSV of contacts into a customer messaging platform. It confirms the import is processing, summarises what happened (created, updated, skipped) and immediately suggests what to do with the new segment: send a broadcast, start a campaign or sync an ad audience.
CSV import summary with next-step cards · App screen: success · 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, 22px page title
- InterBody: Inter 400/500, 16px/1.5; definition labels 12px 600 uppercase with 0.06em tracking; card titles 16px 600
Patterns
- success banner under page title
- two-column summary + next actions
- definition list in inset panel
- action cards stack
- status badge in title row
- icon rail + collapsible section sidebar
- trial countdown in top bar
States it is designed for
- Processing (badge + progress text)
- Complete with counts
- Completed with warnings (amber banner, link to skipped rows)
- Failed (red banner, reason, retry upload)
- Zero rows created (explain matching rule)
- Loading skeleton for cards
- Permission-denied for non-admins trying to download
Who it is for
- Lifecycle marketers importing contact lists
- Growth teams onboarding a new data source
- Ops people cleaning up customer records
Layout
- Dark top bar: workspace switcher, environment badge, trial countdown, Upgrade button, help, notifications, settings, profile
- Icon rail (48px) + section sidebar (220px) with grouped nav and collapsible groups; active item tinted
- Breadcrumb row, then title row: status badge, file name, Download menu button right
- Full-width success banner (tinted green) with check icon
- Left card (60%): 'Import summary' sentence with counts and link, inset definition list (file name, created via, rules, empty values, skipped warnings)
- Right card (40%): 'Use your new segment' with segment chip + link, then three stacked action cards
- Footer line: version string left, small credit right
- Below 1024px: cards stack; sidebar collapses to the icon rail with flyouts
Palette
practical, reassuring, forward-looking. It confirms the job and points at the next win.
- app background
#f9f9f9 - card surface
#ffffff - top bar
#0f3d2e - text on top bar
#ffffff - primary text
#1c2421 - muted text
#5c6360 - link
#0b6bcb - success banner
#e3f7d9 - success text
#1a6d3a - processing badge
#fdf1c7 - processing badge text
#7a5b00 - border
#8a918e
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on card | 15.86:1 | 4.5:1 |
| Aa | muted text on app bg | 5.85:1 | 4.5:1 |
| Aa | link on app bg | 5.01:1 | 4.5:1 |
| Aa | success text on banner | 5.64:1 | 4.5:1 |
| Aa | processing badge text | 5.59:1 | 4.5:1 |
| Aa | text on top bar | 12.16:1 | 4.5:1 |
| card/input border | 3.22:1 | 3:1 | |
| focus ring (link colour) on card | 5.28:1 | 3: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, 22px page title
- Body
- Inter 400/500, 16px/1.5; definition labels 12px 600 uppercase with 0.06em tracking; card titles 16px 600
Neutral grotesk similar to the observed face; uppercase used only for the short definition labels.
Spacing and imagery
Dense but calm: 24px page padding, 24px card padding, 16px gap between action cards; card radius 6px with 1px borders; no heavy shadows; content max 1200px.
Line icons in sidebar and banner; no illustrations; action cards are text-only.
Components
- Top bar with trial countdown and Upgrade
- Icon rail
- Grouped section sidebar with collapsible groups
- Breadcrumbs
- Status badge (processing/complete/failed)
- Download menu (original CSV, error report)
- Success banner
- Summary sentence with counts
- Definition list panel
- Segment chip with link
- Action card (title + description, whole card clickable)
- Version footer
Interactions
- Status badge polls every 5s until complete, then counts update without reload and a toast fires
- Action cards lift border colour on hover and are fully keyboard focusable
- Download menu offers original file and skipped-rows report
- Sidebar groups collapse with remembered state
- Breadcrumb returns to the imports list
Data
Import{id, workspace_id, file_name, created_via (web|api), rule (email_new_and_existing|email_new_only|id_update_only), empty_values (ignored|cleared), status (processing|complete|failed), created_count, updated_count, skipped_count, started_at, finished_at}ImportError{import_id, row_number, reason}Segment{id, workspace_id, name, source_import_id, member_count}
Guardrails
Experience
- Lead with the outcome in a sentence ('created 21, updated 0') before the details
- Show the import rules so users understand why rows were created or updated
- Offer next actions tied to the new segment, not generic promos
- Make skipped rows downloadable with reasons
- Keep processing state honest; never show final counts before completion
Accessibility
- Banner uses role=status and icon + text
- Definition list uses dl/dt/dd semantics
- Action cards are links with descriptive accessible names
- Polling updates announced once on completion
- Uppercase labels only on short terms, with 0.06em tracking
Security
- RLS on imports, import_errors and segments scoped to workspace membership
- Store uploaded CSVs in a private bucket; signed URLs expire in 5 minutes
- Validate CSV server-side (size cap, row cap, header allow-list); neutralise formula-leading cells in the error report
- Only admins can download original files; audit downloads
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 **Tavisto**'s import summary screen for a customer-messaging platform. After someone uploads a contacts CSV, this page confirms the job, reports created/updated/skipped counts, documents the rules applied, and turns the imported rows into a ready-made segment with three next steps.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix primitives) and lucide-react icons. TanStack Query for server state, react-hook-form + zod for forms, date-fns for dates. Supabase for Auth, Postgres and Row Level Security. A Supabase Edge Function parses CSVs in the background and updates the import row; the page polls with TanStack Query.
### Pages & layout
1. **App shell**: 48px `--topbar` bar (workspace switcher, 'Test mode' badge, '12 days left in trial', Upgrade button, help, bell, settings, avatar); 48px icon rail; 220px section sidebar (Dashboard, Analysis, Campaigns, Broadcasts, Transactional, Deliveries, People, Custom objects, Segments, Activity logs, Data & integrations (active), Content).
2. **/imports/:id**: breadcrumb 'Imports / contacts-march.csv'; title row with status badge, file name and a Download menu; success banner 'Your import is processing'.
3. Two-column grid (3fr 2fr): **Import summary** card with the sentence 'This import created 21 new profiles and updated 0 existing profiles. View people >' and an inset panel listing File name, Created via, Import rules, Empty values, Skipped warnings. **Use your new segment** card with a segment chip, 'View segment >', then three action cards: Send a broadcast, Start a campaign, Sync an ad audience.
4. Footer: version string and small credit line.
5. Below 1024px stack cards; sidebar collapses to rail flyouts.
### Design system
- Colors: `--bg: #f9f9f9` (app background), `--card: #ffffff` (card surface), `--topbar: #0f3d2e` (top bar), `--on-topbar: #ffffff` (text on top bar), `--fg: #1c2421` (primary text), `--muted: #5c6360` (muted text), `--link: #0b6bcb` (link), `--success-bg: #e3f7d9` (success banner), `--success: #1a6d3a` (success text), `--warn-bg: #fdf1c7` (processing badge), `--warn: #7a5b00` (processing badge text), `--border: #8a918e` (border).
- Fonts: Inter 400/500/600; title 22px 600; body 16px/1.5; definition labels 12px 600 uppercase 0.06em.
- Spacing: 4px base; page padding 24px; card padding 24px; action card gap 16px.
- Radius: 6px cards and inputs, 9999px badges.
- Shadows: none; 1px `--border` on cards at 40% opacity, full `--border` on inputs.
- Motion: 150ms border colour on hover; toast slide 200ms; counts crossfade when polling completes.
### Components & interactions
`TopBar`, `IconRail`, `SectionSidebar` (collapsible groups persisted in localStorage), `Breadcrumbs`, `StatusBadge` (processing/complete/warnings/failed with text), `DownloadMenu` (Radix DropdownMenu: original file, skipped rows CSV), `Banner` (success/warn/error), `SummarySentence`, `DefinitionPanel`, `SegmentChip`, `ActionCard`, `Toast`.
### Data & state
`imports(id, workspace_id, file_path, file_name, created_via, rule, empty_values, status, created_count, updated_count, skipped_count, started_at, finished_at, created_by)`, `import_errors(import_id, row_number, reason)`, `segments(id, workspace_id, name, source_import_id, member_count)`, `memberships(workspace_id, user_id, role)`. Poll `imports` every 5s while `status = processing`. Mock: seed one processing, one complete, one with warnings, one failed.
### Accessibility
Banner `role=status`; completion announced once. Definition list uses `dl`. Action cards are single links with title + description as the accessible name. Status badge carries text. Focus ring 2px `--link` offset 2px.
Verified contrast: body text on card: #1c2421 on #ffffff = 15.86:1; muted text on app bg: #5c6360 on #f9f9f9 = 5.85:1; link on app bg: #0b6bcb on #f9f9f9 = 5.01:1; success text on banner: #1a6d3a on #e3f7d9 = 5.64:1; processing badge text: #7a5b00 on #fdf1c7 = 5.59:1; text on top bar: #ffffff on #0f3d2e = 12.16:1; card/input border: #8a918e on #ffffff = 3.22:1; focus ring: #0b6bcb on #ffffff = 5.28:1.
### Security
RLS: all four tables select where the user is a member of `workspace_id`; `imports` insert by members, update only by the Edge Function (service role). Files in a private bucket under `workspace_id/` with signed download URLs (5 min) for admins only; log downloads. Server-side CSV validation: 20MB and 100k-row caps, header allow-list, email normalisation; prefix `'` to cells starting with `= + - @` in exported error reports.
### Performance & SEO
Stream-parse CSVs server-side; never load the whole file in the browser. Stop polling when complete or the tab is hidden. App routes `noindex`.
### Guardrails
- Invent file names, segment names and counts.
- Use generic labels for ad destinations ('ad audience').
- No real vendor logos in action cards.
Acceptance criteria:
- [ ] Processing flips to complete without reload
- [ ] Warnings variant links to a downloadable error report
- [ ] Non-members cannot read imports (RLS)
- [ ] Action cards are keyboard reachable
- [ ] Contrast pairs pass