Template
Ribbonet
A deliberately sparse settings page for a privacy-friendly web analytics tool, where the project owner adds collaborators by typing one email into a field with an inline Invite button. Everything fits in one card so managing a small team takes seconds.
Minimal project team settings with inline invite · App screen: invite team · 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 500, 26px page title
- InterBody: Inter 400, 16px, line-height 1.5
Patterns
- centred page title with subtitle
- icon text nav beside a single card
- inline input with embedded invite button
- owner row with avatar and email
- floating bottom pill toolbar
- project switcher next to logo
States it is designed for
- only owner: owner row plus hint 'Invite someone to share this dashboard'
- invalid email inline error
- sending: button spinner
- already a member notice
- invite limit on free plan with upgrade link
- error with Retry
- non-owner view: field hidden, read-only list
Who it is for
- indie makers and small teams tracking site traffic
- freelancers sharing analytics with clients
Layout
- top bar: round logo mark, project switcher with chevron, account glyph on the right
- centred heading 'Settings' with subtitle
- two columns: narrow icon nav (General, Usage, Team, Security) and a ~340px card
- card: title, description, email field with inline Invite button, 'Owner' label and owner row, then Members and Pending lists
- floating dark pill toolbar bottom-centre showing a live counter and a settings button
- below 720px the nav becomes a horizontal segmented row above the card
Palette
Minimal, friendly, almost empty; the product respects your time.
- bg
#ffffff - field
#f3f3f4 - text
#1b1b1f - muted
#66666e - border
#8a8a93 - toolbar
#15151c - live
#a78bfa - accent
#5b3fd1
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text | 17.17:1 | 4.5:1 |
| Aa | muted text | 5.69:1 | 4.5:1 |
| Aa | placeholder on field | 5.13:1 | 4.5:1 |
| Aa | toolbar text | 18.17:1 | 4.5:1 |
| live glyph on toolbar | 6.68:1 | 3:1 | |
| input border | 3.42:1 | 3:1 | |
| Aa | focus ring / link | 6.83: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 500, 26px page title
- Body
- Inter 400, 16px, line-height 1.5
Nav items 15px with 16px icons; card title 17px 500; no uppercase anywhere.
Spacing and imagery
Density: Very airy. Grid: 8px; card padding 12-16px. Container: page max 560px centred; nav 170px + card 340px. Radius: 10px card, 8px input, 6px inline button, full pill toolbar. Shadows: Card 0 1px 3px rgba(0,0,0,0.06); toolbar 0 8px 24px rgba(0,0,0,0.25).
Small outline icons; avatars are monogram circles or uploaded images.
Components
- TopBar with ProjectSwitcher
- SettingsNav
- TeamCard
- InlineInviteField
- MemberRow (avatar, name, email, role menu)
- PendingInviteRow (Resend, Revoke)
- FloatingToolbar
- Toast
Interactions
- Enter in the field submits; Invite button enabled only for a valid email
- Member row hover reveals a role menu (Viewer, Admin, Remove)
- Pending rows show 'Invited 2d ago' with Resend and Revoke
- Toolbar counter updates via polling every 15s
Data
Project{id, name, owner_id, plan (free|paid)}ProjectMember{project_id, user_id, role (owner|admin|viewer)}ProjectInvite{id, project_id, email, role, status (pending|accepted|revoked), created_at}
Guardrails
Experience
- Keep the whole team flow inside one card
- Default new invites to Viewer
- Show the owner first and label it clearly
- Never hide the Invite button inside a menu
- Keep the toolbar from covering content: add bottom padding equal to its height
Accessibility
- Email input has a visible or programmatic label, not just placeholder
- Invite button text stays 'Invite' (not icon-only)
- Role menu uses Radix DropdownMenu with keyboard support
- Toast is announced politely
- Toolbar buttons have accessible names
Security
- Only owner/admin can invite (RLS)
- Validate and normalise emails server-side
- Rate-limit invites per project
- Revoked invite tokens are invalid immediately
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 **Ribbonet**, the team section of a lightweight analytics tool's project settings. The owner types an email into one field and presses Invite; members and pending invites appear below. Build the top bar, the centred settings layout with its four-item nav, the team card and the floating toolbar.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui on Radix primitives and lucide-react icons. TanStack Query for server state, react-hook-form + zod for every form, date-fns for relative times. Supabase for Auth, Postgres with row-level security and Edge Functions for anything that needs a secret.
### Pages & layout
1. **Top bar**: round logo mark, project switcher, account glyph.
2. **Settings**: centred title and subtitle; left nav with General, Usage, Team (active), Security.
3. **Team card**: title, description, grey email field with inline Invite button, Owner label and owner row (logo avatar, name, email right-aligned), Members and Pending lists.
4. **Floating toolbar** bottom-centre with live counter and settings button.
### Design system
- Colors: `--bg: #ffffff` (page and card), `--field: #f3f3f4` (input fill), `--text: #1b1b1f` (primary text), `--muted: #66666e` (subtitle and descriptions), `--border: #8a8a93` (card outline and input focus-free border), `--toolbar: #15151c` (floating pill), `--live: #a78bfa` (live counter glyph on dark), `--accent: #5b3fd1` (focus ring and links).
- Fonts: Inter 500, 26px page title for headings; Inter 400, 16px, line-height 1.5 for body. Nav items 15px with 16px icons; card title 17px 500; no uppercase anywhere.
- Spacing: Very airy. 8px; card padding 12-16px. Container: page max 560px centred; nav 170px + card 340px.
- Radius: 10px card, 8px input, 6px inline button, full pill toolbar.
- Shadows: Card 0 1px 3px rgba(0,0,0,0.06); toolbar 0 8px 24px rgba(0,0,0,0.25).
- Motion: Invite button shows a check morph on success (200ms); new member row fades in; toolbar counter ticks without layout shift.
### Components & interactions
InlineInviteField is a filled rounded input with a small bordered button docked inside its right edge. MemberRow aligns name left and email right on desktop, stacking under 480px.
- Enter in the field submits; Invite button enabled only for a valid email
- Member row hover reveals a role menu (Viewer, Admin, Remove)
- Pending rows show 'Invited 2d ago' with Resend and Revoke
- Toolbar counter updates via polling every 15s
### Data & state
Seed an owner with an example.com address, one viewer and one pending invite. TanStack Query caches members; the toolbar counter uses its own polling query.
Entities: `Project{id, name, owner_id, plan (free|paid)}`; `ProjectMember{project_id, user_id, role (owner|admin|viewer)}`; `ProjectInvite{id, project_id, email, role, status (pending|accepted|revoked), created_at}`.
States to build and show in a dev-only state switcher:
- only owner: owner row plus hint 'Invite someone to share this dashboard'
- invalid email inline error
- sending: button spinner
- already a member notice
- invite limit on free plan with upgrade link
- error with Retry
- non-owner view: field hidden, read-only list
### Accessibility
- Email input has a visible or programmatic label, not just placeholder
- Invite button text stays 'Invite' (not icon-only)
- Role menu uses Radix DropdownMenu with keyboard support
- Toast is announced politely
- Toolbar buttons have accessible names
Verified contrast: body text: #1b1b1f on #ffffff = 17.17:1; muted text: #66666e on #ffffff = 5.69:1; placeholder on field: #66666e on #f3f3f4 = 5.13:1; toolbar text: #ffffff on #15151c = 18.17:1; live glyph on toolbar: #a78bfa on #15151c = 6.68:1; input border: #8a8a93 on #ffffff = 3.42:1; focus ring / link: #5b3fd1 on #ffffff = 6.83:1.
### Security
- Only owner/admin can invite (RLS)
- Validate and normalise emails server-side
- Rate-limit invites per project
- Revoked invite tokens are invalid immediately
RLS detail: `project_members` select for members, insert/update/delete for owner/admin; `project_invites` owner/admin only; viewers can read the member list but not emails of pending invites.
### Performance & SEO
Tiny page: aim for <80KB JS. Private route, noindex.
### Guardrails
- Keep the whole team flow inside one card
- Default new invites to Viewer
- Show the owner first and label it clearly
- Never hide the Invite button inside a menu
- Keep the toolbar from covering content: add bottom padding equal to its height
- Write fresh, generic copy and invented sample data; no real brands, logos, product names or people.
- Keep components small and typed (no `any`); surface every error visibly with a way to recover.
Acceptance criteria:
- Inviting adds a pending row
- Role changes persist
- Non-owners cannot invite
- Toolbar never overlaps the last row
- Keyboard works end to end