Template
Pressvane
The Company watchlists page of a news and business-events data API, before any list exists. Watchlists are uploaded lists of companies that can be searched across at once. The page gives a search/filter frame and a single creation path so analysts can go from empty to monitoring quickly.
Company watchlists empty state in a news-events API console · App screen: empty state · 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, -0.02em
- InterBody: Inter 400 16px/1.5
Patterns
- collapsible grouped sidebar with project picker
- tinted active nav item
- page title with help tooltip
- search bar with filters button
- card empty state with icon, text and CTA
- referral credits card in sidebar footer
- floating chat launcher
- CSV upload to create list
States it is designed for
- empty (no watchlists)
- search with no results (distinct from empty)
- uploading with progress
- CSV errors (missing column, too many rows, bad encoding) listed per row
- created (toast + row highlighted)
- loading skeleton rows
- permission-denied for viewers (Create hidden)
Who it is for
- market intelligence analysts
- sales researchers
- developers building news monitoring
Layout
- sidebar (~240px) with collapse chevron: wordmark; 'Events API' group with a project picker and items (New search, Past searches, Monitoring, Company watchlist active); 'News API' group (Overview, Playground); 'Manage' group with an inbox icon badge (API keys, Usage, Billing); footer referral card with gift icon
- main header: 24px title with help icon; black 'Create watchlist' button right
- full-width search input with Filters button
- white card: bank-building icon in indigo, bold 'No watchlists yet', helper, black CTA
- populated: table of watchlists (name, companies, last hit, created, actions)
- chat launcher bottom-right
- mobile: sidebar drawer; header button becomes icon+label under title
Palette
Crisp and data-serious with a cool blue-grey canvas and black actions.
- app background
#f3f6f9 - card / sidebar surface
#ffffff - primary text
#111827 - helper text
#58627a - active nav fill
#e7ecfb - active nav text / icon
#3346c9 - primary button
#0b0b0f - group label
#5f6675 - input border
#8a93a3 - notification badge
#4b5563
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text | 16.36:1 | 4.5:1 |
| Aa | helper text on card | 6.10:1 | 4.5:1 |
| Aa | active nav text | 6.22:1 | 4.5:1 |
| Aa | primary button label | 19.64:1 | 4.5:1 |
| Aa | group label | 5.76:1 | 4.5:1 |
| input border | 3.10:1 | 3:1 | |
| badge on sidebar | 7.56: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, 24px/1.3, -0.02em
- Body
- Inter 400 16px/1.5
Sidebar 14px 500; group labels 12px uppercase 600 with 0.06em tracking; helper text 16px in the card.
Spacing and imagery
Compact console: sidebar rows 32px, 8px radius; main padding 24px; search 40px tall; empty card 48px vertical padding, 8px radius, 1px #e5e8ee border.
Line icons only, one accent-coloured icon (a classical building) in the empty card; no illustrations or photos.
Components
- Sidebar with CollapsibleGroup
- ProjectPicker
- ReferralCard
- PageHeader with HelpTooltip
- SearchInput + FiltersButton
- EmptyCard
- CreateWatchlistDialog (name + CSV upload + column mapping)
- WatchlistsTable
- ChatLauncher
Interactions
- Create opens a dialog: name, drop or pick a CSV, preview first 5 rows, map the company-name and domain columns
- search filters lists as you type (debounced 200ms)
- Filters opens a popover (created by, size, last activity)
- sidebar groups collapse and remember state
- help icon shows a tooltip explaining watchlists
Data
Watchlist{id, project_id, name, company_count, created_by, created_at, last_match_at}WatchlistCompany{id, watchlist_id, name, domain, country}Project{id, org_id, name}Upload{id, watchlist_id, file_path, rows_ok, rows_failed, status}
Guardrails
Experience
- Differentiate 'nothing created yet' from 'no search results' with different copy and actions.
- Explain the benefit in the helper ('search across all of them at once').
- Offer a sample CSV download inside the create dialog.
- Keep the two create buttons consistent in label and style.
- Show row-level CSV errors and allow importing the valid rows anyway.
Accessibility
- Search input has a visible label or aria-label and announces result counts.
- Collapsible groups use buttons with aria-expanded.
- Upload drop zone is also a keyboard-accessible file button.
- Active nav uses aria-current plus colour and weight.
- The chat launcher has an accessible name and does not cover focused content.
Security
- Accept only .csv under 2 MB; parse server-side with row limits; neutralise formula-injection prefixes (=,+,-,@) on export.
- Store uploads in a private bucket with signed URLs.
- RLS: watchlists scoped to project members; viewers read-only.
- Rate-limit uploads per project.
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 **Pressvane**, the Company watchlists section of a news-events API console. Users upload a CSV of companies to create a watchlist and then search news across the whole list. Focus on the empty state, the create-from-CSV dialog and the first populated table.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Dialog, Popover, Collapsible, Table), lucide-react, TanStack Query and Table, react-hook-form + zod, date-fns, a lightweight CSV parser. Supabase Auth, Postgres, private Storage bucket and an Edge Function to parse uploads.
### Pages & layout
1. **Console shell**: grouped collapsible sidebar, referral card.
2. **/watchlists**: header, search + filters, empty card or table.
3. **Create watchlist dialog**: name, upload, preview, column mapping, import.
4. **/watchlists/:id**: company list with remove and add.
### Design system
- Colors: `--bg: #f3f6f9` (app background), `--surface: #ffffff` (card / sidebar surface), `--fg: #111827` (primary text), `--muted: #58627a` (helper text), `--nav-active: #e7ecfb` (active nav fill), `--nav-text: #3346c9` (active nav text / icon), `--primary: #0b0b0f` (primary button), `--label: #5f6675` (group label), `--input-border: #8a93a3` (input border), `--badge: #4b5563` (notification badge).
- Fonts: Inter 600, 24px/1.3, -0.02em for headings; Inter 400 16px/1.5 for body. Sidebar 14px 500; group labels 12px uppercase 600 with 0.06em tracking; helper text 16px in the card.
- Spacing: Compact console: sidebar rows 32px, 8px radius; main padding 24px; search 40px tall; empty card 48px vertical padding, 8px radius, 1px #e5e8ee border.
- Radius: 8px buttons, inputs, cards and nav pills.
- Shadows: none except dialogs and the chat launcher.
- Motion: 150ms fills; new-row highlight fades over 1.5s.
### Components & interactions
Sidebar, CollapsibleGroup, ProjectPicker, ReferralCard, PageHeader, HelpTooltip, SearchInput, FiltersPopover, EmptyCard, CreateWatchlistDialog, CsvPreviewTable, WatchlistsTable, ChatLauncher.
Interactions: Create opens a dialog: name, drop or pick a CSV, preview first 5 rows, map the company-name and domain columns; search filters lists as you type (debounced 200ms); Filters opens a popover (created by, size, last activity); sidebar groups collapse and remember state; help icon shows a tooltip explaining watchlists.
States to build: empty (no watchlists); search with no results (distinct from empty); uploading with progress; CSV errors (missing column, too many rows, bad encoding) listed per row; created (toast + row highlighted); loading skeleton rows; permission-denied for viewers (Create hidden).
### Data & state
Upload → Edge Function parses, validates and inserts companies, updating `company_count`. Search is client-side on name for up to 200 lists, server-side beyond. Seed one project with no watchlists and a sample CSV of 25 invented companies.
Model: `Watchlist{id, project_id, name, company_count, created_by, created_at, last_match_at}`; `WatchlistCompany{id, watchlist_id, name, domain, country}`; `Project{id, org_id, name}`; `Upload{id, watchlist_id, file_path, rows_ok, rows_failed, status}`.
### Accessibility
Search input has a visible label or aria-label and announces result counts. Collapsible groups use buttons with aria-expanded. Upload drop zone is also a keyboard-accessible file button. Active nav uses aria-current plus colour and weight. The chat launcher has an accessible name and does not cover focused content.
Verified contrast: body text: #111827 on #f3f6f9 = 16.36:1; helper text on card: #58627a on #ffffff = 6.1:1; active nav text: #3346c9 on #e7ecfb = 6.22:1; primary button label: #ffffff on #0b0b0f = 19.64:1; group label: #5f6675 on #ffffff = 5.76:1; input border: #8a93a3 on #ffffff = 3.1:1; badge on sidebar: #4b5563 on #ffffff = 7.56:1.
### Security
Accept only .csv under 2 MB; parse server-side with row limits; neutralise formula-injection prefixes (=,+,-,@) on export. Store uploads in a private bucket with signed URLs. RLS: watchlists scoped to project members; viewers read-only. Rate-limit uploads per project. RLS per table: `watchlists` select for project members, insert/update/delete for editors; `watchlist_companies` follows parent watchlist; `uploads` select for the uploader and editors, insert via Edge Function; `projects` select for org members.
### Performance & SEO
Parse CSVs off the main thread (worker) for preview. Virtualise company tables over 500 rows. Console routes noindex.
### Guardrails
- Differentiate 'nothing created yet' from 'no search results' with different copy and actions.
- Explain the benefit in the helper ('search across all of them at once').
- Offer a sample CSV download inside the create dialog.
- Keep the two create buttons consistent in label and style.
- Show row-level CSV errors and allow importing the valid rows anyway.
- 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:
- [ ] Empty state and no-results state are distinct
- [ ] A valid CSV creates a watchlist with the right count
- [ ] Invalid rows are reported with row numbers
- [ ] Viewers cannot create
- [ ] Every control is reachable by keyboard