Template
Vantrell
A contacts list screen in a CRM that a team assembled with a no-code app builder. The builder chrome (workspace sidebar, tabs, Edit/Publish) frames the running app, whose Contacts tab lists people with role, company and open deal count.
CRM contacts list inside a no-code app builder · App screen: table · 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, 28px H1
- InterBody: Inter 400/500, 16px names (500), 14px meta line; line-height 1.5
Patterns
- app-within-app frame
- top tab navigation inside card
- search bar full width
- avatar list rows with meta line
- count pill per row
- chevron row affordance
- recents list in sidebar
- upgrade promo card in sidebar
States it is designed for
- Loading skeleton rows
- Empty workspace ('Add your first contact')
- No search results with clear-search button
- Error loading with retry
- Long names/companies truncate with tooltip
- Missing avatar falls back to initials
- Read-only viewer (no New contact button)
Who it is for
- Small sales teams running a self-built CRM
- Operations people assembling internal tools
- Builders previewing an app before publishing
Layout
- Builder sidebar (240px, light grey): workspace switcher, Home/Projects/App store links, Recents list, promo card at bottom
- Builder top bar: back/forward, app tab with close, grid icon; right: sync, overflow, Edit, Publish (bright cyan pill)
- App frame (white rounded card): app logo + name left, tab nav centre (Dashboard, Deals, Contacts active, Companies)
- Contacts header: H1 left, dark 'New contact' button right
- Full-width grey search field, then '38 contacts' count
- Bordered list: avatar, name, 'Title · Company' meta, deal count pill, chevron
- Mobile: builder chrome hidden; app tabs become a bottom bar; rows stay
Palette
friendly, tidy, approachable. A CRM that feels hand-built but polished.
- builder background
#f5f5f4 - app surface
#ffffff - primary text
#1c1917 - muted text
#5f5f5f - row border
#8f8f8f - search fill
#f0f0ef - dark button
#1c1917 - publish pill
#5ee0e8 - publish label
#0c2a2c - focus ring
#0e7c86 - count pill fill
#ededed
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on app | 17.49:1 | 4.5:1 |
| Aa | muted meta on app | 6.39:1 | 4.5:1 |
| Aa | muted sidebar text | 5.85:1 | 4.5:1 |
| Aa | placeholder on search fill | 5.60:1 | 4.5:1 |
| Aa | dark button label | 17.49:1 | 4.5:1 |
| Aa | publish label on cyan | 9.62:1 | 4.5:1 |
| row border | 3.23:1 | 3:1 | |
| focus ring | 4.95:1 | 3:1 | |
| Aa | count pill text | 14.94: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, 28px H1
- Body
- Inter 400/500, 16px names (500), 14px meta line; line-height 1.5
Neutral grotesk similar to observed; meta line 14px muted with middle-dot separator.
Spacing and imagery
Comfortable list: 64px rows, 16px horizontal padding, 40px avatars; app card radius 12px; search 40px tall radius 8px; builder sidebar radius 12px on promo card.
Circular headshot avatars (use illustrated or initials avatars in mocks); line icons in builder chrome.
Components
- Builder sidebar with recents
- Upgrade promo card
- Builder top bar with Edit and Publish
- App header with logo and tab nav
- New contact button
- Search input
- Result count
- Contact row (avatar, name, meta, deal pill, chevron)
- Contact detail drawer
- New contact form dialog
Interactions
- Search filters by name, title or company as you type (debounced 150ms)
- Row hover tints background; click or Enter opens detail drawer
- Tabs switch app screens; active tab has grey pill
- New contact opens a dialog; new row appears at top with brief highlight
- Keyboard: / focuses search, arrow keys move between rows
Data
Contact{id, app_id, full_name, title, company_id, avatar_url, email, phone, owner_id, created_at}Company{id, app_id, name, sector_label}Deal{id, contact_id, company_id, stage (lead|qualified|proposal|won|lost), value}
Guardrails
Experience
- Show count above the list so users know search scope
- Put the most useful facts (title and company) in the meta line
- Make the whole row the click target
- Keep builder chrome visually quieter than the app itself
- Surface deal counts as neutral pills, not colours
Accessibility
- List is a semantic ul of links with name + meta as accessible name
- Search has a visible label (can be visually hidden) and announces result count
- Avatars have empty alt; initials fallback aria-hidden
- Publish label on cyan passes 4.5:1
- Focus ring 2px teal visible on white and grey
Security
- RLS: contacts/companies/deals scoped to app workspace members; viewers read-only
- Validate contact fields (email, phone) with zod client and server
- Avatar uploads: image types only, 2MB cap, private bucket with signed URLs
- Audit contact deletes; soft-delete with 30-day restore
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 **Vantrell**, the Contacts screen of a small CRM assembled inside a no-code app builder. The builder chrome frames a running app whose Contacts tab lists 38 people with title, company and deal count, supports instant search, and opens a detail drawer.
### 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.
### Pages & layout
1. **Builder sidebar** (240px, `--chrome`): workspace switcher, search and collapse icons; Home, Projects, App store; 'Recent' list with app names and small type labels; bottom promo card 'Keep building' with a dark Upgrade button; help icon.
2. **Builder top bar**: back/forward, app tab with close, apps grid; right: refresh, overflow, Edit (grey pill), Publish (`--publish` pill).
3. **App frame** (white, radius 12px): app logo + name; centred tabs Dashboard, Deals, Contacts (active grey pill), Companies.
4. **Contacts**: H1 + 'New contact'; search; '38 contacts'; list rows with avatar, name, 'Title · Company', '1 deal' pill and chevron.
5. **Detail drawer** (right 420px): avatar, name, title, company link, email, phone, deals list, Edit.
6. Mobile: builder chrome hidden; app tabs become a bottom bar.
### Design system
- Colors: `--chrome: #f5f5f4` (builder background), `--bg: #ffffff` (app surface), `--fg: #1c1917` (primary text), `--muted: #5f5f5f` (muted text), `--border: #8f8f8f` (row border), `--field: #f0f0ef` (search fill), `--primary: #1c1917` (dark button), `--publish: #5ee0e8` (publish pill), `--on-publish: #0c2a2c` (publish label), `--ring: #0e7c86` (focus ring), `--pill: #ededed` (count pill fill).
- Fonts: Inter 400/500/700; H1 28px 700; row name 16px 500; meta 14px; body line-height 1.5.
- Spacing: 4px base; rows 64px; app padding 24px.
- Radius: app frame 12px, buttons 8px, pills 6px, avatars full.
- Shadows: app frame `0 1px 2px rgb(0 0 0 / 0.05)`; drawer `-8px 0 24px rgb(0 0 0 / 0.1)`.
- Motion: 120ms row hover; drawer slide 200ms; new-row highlight fades over 2s.
### Components & interactions
`BuilderSidebar`, `PromoCard`, `BuilderTopBar`, `AppFrame`, `AppTabs` (Radix Tabs as nav links), `SearchInput` ('/' shortcut), `ResultCount` (aria-live), `ContactRow`, `CountPill`, `ContactDrawer`, `ContactFormDialog` (react-hook-form + zod), `AvatarWithFallback`.
### Data & state
`contacts(id, workspace_id, full_name, title, company_id, avatar_path, email, phone, owner_id, created_at, deleted_at)`, `companies(id, workspace_id, name)`, `deals(id, workspace_id, contact_id, stage, value)`, `members(workspace_id, user_id, role enum builder|editor|viewer)`. Deal counts via a view. Search is client-side for <500 rows, server `ilike` beyond. Seed 38 invented contacts across invented companies.
### Accessibility
List of links with 'Name, Title at Company, 1 deal'. Search labelled, result count announced politely. Arrow keys move focus between rows. Drawer is a Radix Dialog with focus return. Initials avatars aria-hidden.
Verified contrast: body text on app: #1c1917 on #ffffff = 17.49:1; muted meta on app: #5f5f5f on #ffffff = 6.39:1; muted sidebar text: #5f5f5f on #f5f5f4 = 5.85:1; placeholder on search fill: #5f5f5f on #f0f0ef = 5.60:1; dark button label: #ffffff on #1c1917 = 17.49:1; publish label on cyan: #0c2a2c on #5ee0e8 = 9.62:1; row border: #8f8f8f on #ffffff = 3.23:1; focus ring: #0e7c86 on #ffffff = 4.95:1; count pill text: #1c1917 on #ededed = 14.94:1.
### Security
RLS per table: select for workspace members; insert/update for builder and editor roles; viewers read-only; deletes set `deleted_at` (editors) and hard delete only by builders after 30 days. Validate email/phone with zod on client and in a Postgres check or function. Avatars in a private bucket with signed URLs. Audit deletes.
### Performance & SEO
Virtualise the list above 200 rows. Avatars 80px WebP lazy-loaded. Prefetch the drawer data on row hover. App routes `noindex`.
### Guardrails
- Invent all people and companies; use illustrated or initials avatars.
- Keep builder brand neutral; no real platform names.
- Don't colour-code deal counts.
Acceptance criteria:
- [ ] Search narrows the list and updates the count
- [ ] Row click opens the drawer; Esc returns focus
- [ ] Viewer role sees no New contact button and RLS blocks writes
- [ ] Empty and no-results states render
- [ ] Contrast pairs pass