Template
Ulvenote
A long-form report page in a metrics platform, where prose and live metric charts sit together like a document. This welcome report explains how reports work and shows the editing surface teams use to share insights.
Analytics report document with charts and prose · App screen: text editor · 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, 36px H1 (-0.02em); H2 28px 700; H3 22px 600
- InterBody: Inter 400, 17px/1.6 document body; UI 14px/1.5
Patterns
- document-style report page
- sidebar with teams and report folders
- breadcrumb header with Filters/Share/more
- author + last-edited meta line
- two-column how-to blocks in document
- demo-data notice card in sidebar
- emoji in title
States it is designed for
- Demo data mode (notice visible)
- Chart loading skeleton
- Chart error (metric missing) with fix link
- Empty report (placeholder prompt)
- Read-only viewer
- Filters applied indicator on the Filters button
- Unsaved edits saving indicator
Who it is for
- Data and product analysts writing narrative reports
- Team leads sharing weekly metric updates
- Stakeholders reading reports without a BI background
Layout
- Sidebar (220px): workspace switcher + search; Home, Settings (expandable), Help centre; Teams (All teams, team rows with small coloured letter tiles and lock/flag); Reports (All reports, My reports); bottom notice card 'Using demo data' with Continue setup button
- Header bar: back, breadcrumb 'Reports / title'; right: Filters, Share, more
- Document column (max 720px): H1 title with emoji, meta line (Last edited, Created by avatar + name)
- Body: paragraphs, bullet list, divider, H2 sections, two-column 'From a report' / 'From a metric' how-to blocks with numbered steps; embedded metric charts in later sections
- Mobile: sidebar collapses; two-column blocks stack
Palette
calm, literate, analytical. Numbers told as a story, not a dashboard.
- page
#ffffff - sidebar
#fafafc - primary text
#16161a - muted text
#63666f - border
#8b8d96 - chart primary
#4f46e5 - chart secondary
#0e7490 - team tile blue
#1d4ed8 - focus ring
#4f46e5 - demo notice fill
#f4f4f7
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | document text | 18.04:1 | 4.5:1 |
| Aa | muted meta on page | 5.74:1 | 4.5:1 |
| Aa | sidebar text | 5.50:1 | 4.5:1 |
| Aa | notice text on notice fill | 16.44:1 | 4.5:1 |
| Aa | team tile letter | 6.70:1 | 4.5:1 |
| chart series 1 | 6.29:1 | 3:1 | |
| chart series 2 | 5.36:1 | 3:1 | |
| border | 3.31:1 | 3:1 | |
| focus ring | 6.29: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 700, 36px H1 (-0.02em); H2 28px 700; H3 22px 600
- Body
- Inter 400, 17px/1.6 document body; UI 14px/1.5
Neutral grotesk similar to observed; document uses slightly larger body for reading comfort.
Spacing and imagery
Reading layout: 720px column centred in remaining space, 48px top padding, 16px paragraph spacing, 32px before H2; dividers 1px; sidebar items 32px; buttons 32px radius 8px.
Metric charts (line/bar) embedded inline with light gridlines; small avatars; emoji allowed in titles; no photos.
Components
- Workspace switcher
- Sidebar tree (teams, reports)
- Demo-data notice card
- Breadcrumb header
- Filters popover (date range, segment)
- Share dialog (team/link)
- Document title with emoji picker
- Meta line
- Block editor (paragraph, heading, list, divider, columns, chart)
- Metric chart block
- Slash command menu
- Add block hover button
Interactions
- Hover between blocks reveals a '+' to insert a chart or text block
- Typing '/' opens slash menu (Heading, List, Columns, Chart, Divider)
- Chart blocks re-query when report Filters change
- Share opens dialog with team access and link toggle
- Title emoji is clickable to change
Data
Report{id, workspace_id, team_id, title, emoji, content_json, created_by, updated_at, visibility}Metric{id, name, definition, default_view}ChartBlock{report_id, metric_id, view (line|bar|table), time_range}Team{id, name, color, private}
Guardrails
Experience
- Keep report-level filters visible and apply them to every chart
- Charts must carry titles, units and the time range
- Show authorship and freshness under the title
- Make demo mode obvious so no one mistakes sample numbers for real ones
- Keep reading width near 70 characters
Accessibility
- Document uses semantic headings; chart blocks have a caption and 'View as table' toggle
- Chart colours meet 3:1 against white; series also distinguished by marker shape
- Slash menu is a listbox with keyboard navigation
- Emoji in title has an accessible label or is aria-hidden
- Focus visible on block handles
Security
- RLS: reports scoped to team membership; private teams hidden from non-members
- Metric queries run server-side with parameterised filters
- Share links read-only and revocable
- Sanitise document JSON; no raw HTML embeds
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 **Ulvenote**'s report page: a document where analysts mix prose with live metric charts. The seeded welcome report explains what reports are for and how to add charts, and it doubles as the editing surface for every other report. ### Stack Next.js App Router + React 18 + TypeScript, Tailwind CSS, shadcn/ui, lucide-react, Tiptap with a custom chart node, Recharts for charts, TanStack Query, Supabase (Auth, Postgres, RLS). Metric queries run in a server route. ### Pages & layout 1. **Sidebar**: workspace switcher, search; Home, Settings, Help centre; Teams: All teams, 'Finance' (tile + lock), 'Launch' (tile); Reports: All reports, My reports; bottom card 'Using demo data', helper text, 'Continue setup'. 2. **Header**: back, breadcrumb 'Reports / Welcome to reports 👋'; Filters, Share, more. 3. **/reports/:id**: H1 with emoji; meta 'Last edited: Today · Created by [avatar] Sam Ortega' (invented); intro paragraphs; bullet list of use cases (metric updates, deep dives, live trackers, ad-hoc analyses, private explorations); divider; H2 'Add charts'; two-column blocks 'From a report' / 'From a metric' with numbered steps; further sections with embedded line and bar charts using invented metrics (weekly active teams, trial conversions). 4. Mobile: sidebar drawer; columns stack. ### Design system - Colors: `--bg: #ffffff` (page), `--sidebar: #fafafc` (sidebar), `--fg: #16161a` (primary text), `--muted: #63666f` (muted text), `--border: #8b8d96` (border), `--chart-1: #4f46e5` (chart primary), `--chart-2: #0e7490` (chart secondary), `--tile: #1d4ed8` (team tile blue), `--ring: #4f46e5` (focus ring), `--notice: #f4f4f7` (demo notice fill). - Fonts: Inter 400/600/700; H1 36px, H2 28px, H3 22px; document body 17px/1.6; UI 14px/1.5. - Spacing: 4px base; column 720px; 16px paragraph gap; 32px before H2. - Radius: 8px buttons and cards; team tiles 4px. - Shadows: none; charts in bordered cards. - Motion: block handle fade 100ms; chart draw 300ms (off under reduced motion). ### Components & interactions `AppSidebar`, `TeamTile`, `DemoNotice`, `ReportHeader`, `FiltersPopover` (date range, segment), `ShareDialog`, `TitleWithEmoji`, `MetaLine`, `ReportEditor` (Tiptap: headings, lists, divider, two-column, chart node), `SlashMenu`, `InsertHandle`, `MetricChartBlock` (title, units, range, line/bar/table toggle, skeleton, error). ### Data & state `reports(id, workspace_id, team_id, title, emoji, content jsonb, created_by, updated_at, visibility)`, `metrics(id, workspace_id, name, unit, definition)`, `teams(id, workspace_id, name, color, private)`, `team_members(team_id, user_id, role)`. Chart nodes store `metric_id, view, range`; data fetched via `/api/metrics/:id?range=&segment=`. Demo mode flag on workspace swaps to seeded series. ### Accessibility Semantic headings; charts have captions and a table view; series distinguished by colour and marker shape, both colours ≥3:1. Slash menu listbox with arrow keys. Title emoji `aria-hidden`. Focus ring 2px `--ring`. Verified contrast: document text: #16161a on #ffffff = 18.04:1; muted meta on page: #63666f on #ffffff = 5.74:1; sidebar text: #63666f on #fafafc = 5.50:1; notice text on notice fill: #16161a on #f4f4f7 = 16.44:1; team tile letter: #ffffff on #1d4ed8 = 6.70:1; chart series 1: #4f46e5 on #ffffff = 6.29:1; chart series 2: #0e7490 on #ffffff = 5.36:1; border: #8b8d96 on #ffffff = 3.31:1; focus ring: #4f46e5 on #ffffff = 6.29:1. ### Security RLS: `reports` select where the user belongs to the team (or team is not private and user is in the workspace); update by author/editors; `teams.private` hides rows from non-members. Metric API uses parameterised queries and validates ranges/segments against whitelists. Share links are signed read-only tokens, revocable. Tiptap schema prevents raw HTML. ### Performance & SEO Stream the document shell; load chart data in parallel per block; cache metric responses 60s. Lazy-load Recharts. App routes `noindex`. ### Guardrails - Invent every person, team and metric value. - Label demo data clearly. - Write fresh explanatory copy. Acceptance criteria: - [ ] Slash menu inserts a chart block that loads data - [ ] Filters re-query all charts - [ ] Private team reports invisible to non-members (RLS) - [ ] Charts offer a table view - [ ] Contrast pairs pass