Template
Sprigly
An invite-friends modal in a social reading tracker that nudges readers to bring friends in exchange for a free month of the premium tier. It explains the benefit in one line, shows progress toward the reward and offers one copy-link action.
Reading-app invite-friends modal with reward progress · App screen: referral · 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.
- Source Serif 4Headings: Source Serif 4 500, 24px headline (similar to the observed book serif)
- InterBody: Inter 400 16px body at 1.55; 15px reward title (500); 14px rule text; 14px nav
Patterns
- modal with hand-drawn line-art illustration header
- serif headline over sans body
- reward card with gift icon badge overlapping its top edge
- thin progress bar toward reward
- full-width neutral "Copy your link" button
- circular close button top-right
- monochrome sidebar with Library and Collections groups
- Friends / Everybody feed tabs
States it is designed for
- 0 of 3 friends (empty bar with "0 of 3" label)
- Partial progress (1-2 of 3)
- Reward unlocked (card shows "Premium month added" with date)
- Already premium (modal copy changes to gifting)
- Clipboard failure (show selectable link text)
- Link loading skeleton
Who it is for
- Readers tracking books and sharing reviews
- Book clubs
- Social reading communities
Layout
- Left sidebar (~135px): wordmark, Home, Feed, Explore, Highlights; Library (To read, Reading, Read); Collections (+ Create new); reading goal and user row at bottom
- Main: "Feed" title with Friends / Everybody tabs; empty-state illustration and copy behind the modal
- Modal (~300px): close circle; ink illustration across top; serif H2; two-line paragraph; reward card with overlapping gift badge, title, rule text and progress bar; "Copy your link" button
- Scrim 60% grey
- Mobile: modal as full-height sheet; sidebar as bottom tabs
Palette
literary, gentle, monochrome, friendly. Like a bookmark with a doodle.
- modal and page background
#ffffff - headline and strong text
#1f1f1f - paragraph and rule text
#5e5e5e - neutral button fill
#f2f2f2 - progress track
#8c8c8c - progress fill
#1f1f1f - reward card border (decorative)
#e3e3e3 - overlay (decorative)
#666666 - focus ring
#1f1f1f
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | headline | 16.48:1 | 4.5:1 |
| Aa | muted paragraph | 6.48:1 | 4.5:1 |
| Aa | button label on neutral fill | 14.72:1 | 4.5:1 |
| progress track on white | 3.36:1 | 3:1 | |
| progress fill on white | 16.48:1 | 3:1 | |
| focus ring | 16.48: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
- Source Serif 4 500, 24px headline (similar to the observed book serif)
- Body
- Inter 400 16px body at 1.55; 15px reward title (500); 14px rule text; 14px nav
The serif appears only for headlines to evoke print; everything else is a quiet sans. Numbers inside copy are bolded, not coloured.
Spacing and imagery
Airy and centered: 8px base; modal padding 16px, 24px between blocks; reward card padding 20px; radius 12px modal, 8px card and button, full on close; modal shadow 0 16px 48px rgba(0,0,0,.18).
Black ink line-art illustrations of animals carrying books, cropped at the modal edges; a small outline gift icon in a white circle; no colour photography.
Components
- Monochrome sidebar
- Feed tabs
- Invite modal
- Line-art header illustration
- Reward card with overlapping icon badge
- Progress bar with count label
- Copy link button
- Close circle button
- Copied toast
Interactions
- Copy link copies and swaps label to "Link copied" with a check for 2s
- Progress bar animates to new value when a friend joins (400ms)
- Close, Esc and scrim click dismiss; modal does not reappear for 30 days
- Hover on nav items tints background light grey
Data
Profile{id, username, is_premium, premium_until}InviteLink{user_id, code}Invite{id, inviter_id, invitee_id, joined_at}RewardGrant{id, user_id, kind (premium_month), granted_at}Book{id, title, author}Shelf{user_id, book_id, status (to_read|reading|read)}
Guardrails
Experience
- State the reward rule in one sentence with the number emphasised
- Show progress as "N of 3" text as well as a bar
- Offer a single primary action (copy link); sharing elsewhere is secondary
- Do not show the modal more than once per 30 days
- Change the message for users who already have premium
Accessibility
- Progress uses role="progressbar" with aria-valuenow/max and a visible text label
- Illustrations are decorative (alt="")
- Close button labelled "Close invite dialog"
- Serif headline is at least 24px; body 16px
- Focus trapped in modal and returned to the trigger
Security
- Invite codes random; joins attributed server-side on sign-up
- Reward grants written by a server function after verifying distinct, active new accounts
- RLS: users read only their own invites and grants
- Prevent self-referral and duplicate accounts (email/device checks)
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 **Sprigly**, an invite-friends modal for a social reading tracker. Over the Feed page, a monochrome modal with a line-art illustration says "Read with friends", explains that inviting three friends earns a free month of the premium tier, shows progress toward it and offers one "Copy your link" button.
### Stack
React + TypeScript + Vite, Tailwind CSS, shadcn/ui (Dialog, Tabs, Progress, Toast) on Radix, lucide-react, Motion for the progress animation, TanStack Query, Supabase for auth, profiles, invites and reward grants.
### Pages & layout
1. **Sidebar**: invented wordmark with a small sprout icon; Home, Feed (active), Explore, Highlights; Library: To read, Reading, Read; Collections with "Create new"; a reading-goal link and user row at the bottom.
2. **Feed**: title, Friends / Everybody tabs, an empty state ("Your friends' reviews will show up here") with a line-art reader illustration.
3. **Invite modal** (340px): circular close top-right; an ink illustration spanning the top edge; serif H2 "Read with friends"; a two-line paragraph; a bordered reward card with a gift icon in a white circle overlapping its top border, a title ("Get a free premium month"), the rule ("Invite **3** friends with your link") and a progress bar with "1 of 3" label; a full-width neutral "Copy your link" button.
4. Mobile: the modal is a full-height sheet and the sidebar becomes bottom tabs.
### Design system
- Colors: `--bg: #ffffff` (modal and page background), `--fg: #1f1f1f` (headline and strong text), `--muted: #5e5e5e` (paragraph and rule text), `--button: #f2f2f2` (neutral button fill), `--progress-track: #8c8c8c` (progress track), `--progress-fill: #1f1f1f` (progress fill), `--border: #e3e3e3` (reward card border (decorative)), `--scrim: #666666` (overlay (decorative)), `--focus: #1f1f1f` (focus ring).
- Fonts: Source Serif 4 500 for headlines (24px; similar to the observed book serif); Inter for body 16px/1.55, reward title 15px/500, rule text 14px (never below 14).
- Spacing: 8px scale; 16px modal padding; 24px block gap; 20px card padding.
- Radius: modal 12px; card and button 8px; close button full.
- Shadows: modal `0 16px 48px rgba(0,0,0,.18)`; the card has a border only.
- Motion: modal fade/scale 180ms; progress fill 400ms ease-out; none under reduced motion.
### Components & interactions
Sidebar, FeedTabs, EmptyFeed, InviteDialog, InkIllustration (decorative, cropped with overflow hidden), RewardCard (badge, title, rule, ProgressBar with text), CopyLinkButton (label becomes "Link copied" with a check for 2s; falls back to a selectable text field), Toast. Close, Esc and a scrim click dismiss the modal and record `last_invite_prompt_at`.
### Data & state
Tables: `profiles(id, username, is_premium, premium_until, last_invite_prompt_at)`, `invite_links(user_id, code)`, `invites(id, inviter_id, invitee_id, joined_at)`, `reward_grants(id, user_id, kind, granted_at)`, plus `books` and `shelves` for the app shell. Mock progress at 1 of 3. Handle states: 0/3, partial, unlocked (card says "Premium month added, ends 12 Nov"), already premium (gift framing), clipboard failure, loading skeleton.
### Accessibility
The progress bar is `role="progressbar"` with values and a visible "1 of 3" label. Illustrations are decorative. The close button is labelled. Focus is trapped and returned. The serif headline stays at 24px or larger; body text is 16px. Focus rings: 2px ink with a 2px white offset.
Verified contrast: headline: #1f1f1f on #ffffff = 16.48:1; muted paragraph: #5e5e5e on #ffffff = 6.48:1; button label on neutral fill: #1f1f1f on #f2f2f2 = 14.72:1; progress track on white: #8c8c8c on #ffffff = 3.36:1; progress fill on white: #1f1f1f on #ffffff = 16.48:1; focus ring: #1f1f1f on #ffffff = 16.48:1.
### Security
Invite codes are random. Attribution happens server-side at sign-up. A server function grants the reward only after three distinct, verified, active new accounts; self-referrals and duplicates are rejected. RLS: users read only their own invites and grants; nobody can insert grants from the client.
### Performance & SEO
Inline the line-art as optimised SVG you draw yourself, or use compressed PNGs with explicit sizes. Lazy-load the modal. The app is noindex.
### Guardrails
- Original illustrations and invented wordmark; no real brand names.
- Show the modal at most once every 30 days.
- Acceptance criteria: (1) Copy works, with a fallback; (2) progress reflects server data with text and bar; (3) the unlocked and already-premium variants render; (4) the dialog is fully keyboard accessible; (5) contrast pairs pass.