Template
Umbrive
The members page of a developer API product's per-application settings. It shows who has access to the application, their role and join time, and a separate invitations block that is empty until someone is invited. A prepaid balance with a Top up button stays visible in the header.
Application members and invitations settings · App screen: team members · 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, 22px section headings
- InterBody: Inter 400/500, 16px/1.5; table 15px; muted email 14px
Patterns
- top-level tabs (Overview / Billing / Configure)
- settings sub-nav with grouped sections
- members table in card with pagination
- invitations empty state with inbox icon
- prepaid balance + top-up in header
- command-K search shortcut
- sortable column header
States it is designed for
- Single owner member
- Invitations empty
- Pending invitations list with resend/revoke
- Invite validation errors
- Low balance warning chip next to balance
- Loading skeleton rows
- Non-owner: Invite hidden, explanatory note
Who it is for
- Developers running a usage-billed API account
- Team leads adding engineers to an application
- Finance-minded owners watching prepaid balance
Layout
- Header: logo, app switcher, service switcher (All services); right: 'Balance' label with mono amount, dark 'Top up' button, avatar
- Tabs row: Overview, Billing, Configure (active underline); right: search with ⌘K hint
- Settings sub-nav (160px): App (General, Members active), Security (Keys), Billing (Spending limit, Notifications)
- Main (max 560px): H2 'Members' + description, outlined 'Invite members' button right
- Members card: table with Member (sortable), Role, Joined; footer '1 member' left, 'Page 1 of 1' + Previous/Next right
- Invitations section: H2 and card with inner dashed box: inbox icon, 'No invitations yet', muted helper
- Mobile: sub-nav becomes a select; table becomes cards
Palette
quiet, developer-grade, tidy. An account page that stays out of the way.
- app background
#f9f9fb - card surface
#ffffff - primary text
#16171d - muted text
#5c6070 - border
#8a8e99 - dark button
#16171d - avatar/accent blue
#1d5fa8 - focus ring
#2563eb - low-balance warning
#b45309 - error
#b42318
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on card | 17.88:1 | 4.5:1 |
| Aa | muted text on app bg | 5.94:1 | 4.5:1 |
| Aa | muted email on card | 6.25:1 | 4.5:1 |
| Aa | dark button label | 17.88:1 | 4.5:1 |
| Aa | avatar initial on blue | 6.45:1 | 4.5:1 |
| Aa | low-balance text | 5.02:1 | 4.5:1 |
| card/dashed border | 3.28:1 | 3:1 | |
| focus ring | 5.17: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.
- background
- card
- muted
- primary
- secondary
- accent
- destructive
Type scale
- Display
- Inter 600, 22px section headings
- Body
- Inter 400/500, 16px/1.5; table 15px; muted email 14px
Balance and amounts in JetBrains Mono 14px; similar to observed grotesk + mono.
Spacing and imagery
Airy settings: page padding 32px, 24px card padding, 48px between Members and Invitations; card radius 8px with 1px border; empty box has dashed border radius 6px; buttons 32px tall.
Line icons (inbox, user plus, key); monogram avatars; no imagery.
Components
- App switcher
- Service switcher
- Balance readout
- Top up button + dialog
- Tabs row
- Command search trigger
- Settings sub-nav
- Members table with sortable header
- Pagination footer
- Invite members dialog (email, role)
- Invitations list / empty box
- Role select
Interactions
- Member column header toggles sort with arrow indicator
- Invite members dialog accepts multiple emails and a role; on success the invitation appears in the Invitations card
- Previous/Next disabled at bounds
- Top up opens a dialog with preset amounts and redirects to a hosted checkout provider
- ⌘K opens command palette
Data
App{id, name, owner_id, balance_cents, currency}AppMember{app_id, user_id, role (owner|admin|developer|viewer), joined_at}AppInvitation{id, app_id, email, role, status (pending|accepted|revoked|expired), sent_at}TopUp{id, app_id, amount_cents, status, provider_ref, created_at}
Guardrails
Experience
- Separate members from invitations so pending people are never mistaken for active ones
- Show join time as relative with exact time on hover
- Keep balance and Top up visible across settings
- Explain the empty invitations state and link to Invite
- Disable pagination buttons at bounds rather than hiding them
Accessibility
- Sortable header is a button with aria-sort
- Empty state text is real text, icon aria-hidden
- Balance has an accessible label 'Balance 0.00 euros'
- Disabled pagination buttons use aria-disabled and remain readable
- Command palette is a labelled dialog with keyboard navigation
Security
- RLS: app_members and invitations visible to app members; only owners/admins invite or change roles
- Top-ups created server-side; balance changes only via verified webhook from the checkout provider
- Invitation tokens hashed, single-use, 7-day expiry
- Audit role changes and top-ups
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 **Umbrive**'s application Members page for a usage-billed developer API. Owners see members with role and join time, invite teammates, and track pending invitations, while the prepaid balance and Top up stay in view.
### 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. Payments go through a hosted checkout provider; its webhook (verified server-side in an Edge Function) credits the balance.
### Pages & layout
1. **Header**: logo, app switcher ('Harbor Sandbox', invented), service switcher; right: 'Balance' + mono '€0.00', dark '+ Top up', avatar.
2. **Tabs**: Overview, Billing, Configure (active); search with '⌘K'.
3. **Sub-nav**: App (General, Members), Security (Keys), Billing (Spending limit, Notifications).
4. **/apps/:id/settings/members**: 'Members' + 'Manage your application members and invitations.'; outlined '+ Invite members'. Card with table Member (avatar, name, email), Role, Joined ('17m ago'); footer count and pagination.
5. **Invitations**: heading + card with dashed inner box (inbox icon, 'No invitations yet', muted helper) or a list of pending invitations with Resend/Revoke.
6. **Top up dialog**: presets (€10/€25/€50/custom) then redirect to hosted checkout.
7. Mobile: sub-nav as select; tables to cards.
### Design system
- Colors: `--bg: #f9f9fb` (app background), `--card: #ffffff` (card surface), `--fg: #16171d` (primary text), `--muted: #5c6070` (muted text), `--border: #8a8e99` (border), `--primary: #16171d` (dark button), `--accent: #1d5fa8` (avatar/accent blue), `--ring: #2563eb` (focus ring), `--warn: #b45309` (low-balance warning), `--danger: #b42318` (error).
- Fonts: Inter 400/500/600; headings 22px 600; body 16px/1.5; table 15px; JetBrains Mono 14px for money.
- Spacing: 4px base; page padding 32px; card padding 24px.
- Radius: cards 8px, buttons 6px, empty box 6px dashed.
- Shadows: cards `0 1px 2px rgb(22 23 29 / 0.05)`.
- Motion: tab underline 150ms; dialogs 150ms fade/scale.
### Components & interactions
`AppHeader`, `BalanceReadout` (warning chip when under a threshold), `TopUpDialog`, `SettingsTabs`, `CommandPalette`, `SettingsSubNav`, `MembersTable` (sortable Member), `Pagination`, `InviteDialog` (email chips, role select), `InvitationsCard` (empty or list), `RoleSelect` (owners/admins only).
### Data & state
`apps(id, name, owner_id, balance_cents, currency)`, `app_members(app_id, user_id, role, joined_at)`, `app_invitations(id, app_id, email, role, token_hash, status, sent_at, expires_at)`, `top_ups(id, app_id, amount_cents, status, provider_ref, created_at)`. Balance only changes in the webhook handler inside a transaction. Mock: one owner, zero invitations; toggle to three pending.
### Accessibility
`aria-sort` on Member header; pagination buttons `aria-disabled`; empty state text readable; balance labelled with currency name. Command palette: Radix Dialog + listbox semantics. Focus ring 2px `--ring`.
Verified contrast: body text on card: #16171d on #ffffff = 17.88:1; muted text on app bg: #5c6070 on #f9f9fb = 5.94:1; muted email on card: #5c6070 on #ffffff = 6.25:1; dark button label: #ffffff on #16171d = 17.88:1; avatar initial on blue: #ffffff on #1d5fa8 = 6.45:1; low-balance text: #b45309 on #ffffff = 5.02:1; card/dashed border: #8a8e99 on #ffffff = 3.28:1; focus ring: #2563eb on #ffffff = 5.17:1.
### Security
RLS: `app_members` select for members of the app; insert/update/delete only for owner/admin (owner row immutable except by transfer function). `app_invitations` same. `top_ups` select for owner/admin; insert only via server function; `apps.balance_cents` has no client update policy. Webhook signature verified with a server-side secret. Invitation tokens hashed, 7-day expiry. Audit role changes and top-ups.
### Performance & SEO
Small route; lazy-load command palette and top-up dialog. Settings routes `noindex`.
### Guardrails
- Invent app names, members and amounts.
- Refer to payments as a hosted checkout provider.
- Never trust client-sent amounts for balance changes.
Acceptance criteria:
- [ ] Inviting adds a pending row to Invitations
- [ ] Empty invitations state renders with helper
- [ ] Balance updates only after webhook
- [ ] Non-admins cannot invite (RLS)
- [ ] Contrast pairs pass