Template
Nimbrel
A billing-settings page for a developer platform where a user on a free trial adds a card. A centered modal collects card information, name on card and a billing address without leaving the page, so credit balance and billing links stay in context.
Add payment details modal over billing settings · App screen: checkout · 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 page title, 18px modal title, -0.01em
- InterBody: Inter 400 16px / 1.5; field labels 16px 600
Patterns
- modal form over dimmed settings page
- persistent left sidebar with grouped nav
- tabbed settings sub-navigation
- combined card-number / expiry / CVC field
- stacked billing-address block with split city/postcode row
- note callout at top of modal
- icon-tile action list
States it is designed for
- trial with credit remaining and no card on file
- dialog pristine
- inline validation errors per field
- card declined / incomplete card number
- submitting (disabled footer, spinner)
- success toast and updated overview
- card already on file (dialog becomes Replace card)
- network error banner inside the dialog with retry
- permission-denied for members who are not billing admins (button hidden, explanatory note)
Who it is for
- developers on a free trial or credit plan
- team owners who manage API spend
Layout
- Left sidebar (~240px): workspace logo, product links (playground, assistants, fine-tuning, API keys, files, usage), Settings group with nested organisation, team, limits, billing (active), profile; bottom documentation, help and account switcher
- Main: page title Billing settings, tabs (Overview, Payment methods, Billing history, Preferences)
- Overview body: plan label, credit remaining with info icon and large amount, primary Add payment details + secondary View usage buttons, info note banner, list of three icon-tile links (payment methods, preferences, pricing)
- Modal (~420px, centered, 12px radius) over a 50% black scrim: title + close, grey note callout, Card information (single combined input), Name on card, Billing address (country select, line 1, line 2, city + postcode row, region), footer with Cancel and Continue
- Below 640px the modal becomes a full-height bottom sheet with a sticky footer; sidebar collapses into a hamburger drawer
Palette
Neutral, utilitarian and trustworthy; the payment step looks like part of the settings, not a sales page.
- page
#ffffff - sidebar
#f7f7f8 - text
#0d0d0d - muted text
#6e6e80 - note callout
#f4f4f5 - input border
#8e8ea0 - primary green
#0d8567 - on primary
#ffffff - scrim
#7f7f7f - danger
#d0342c - focus ring
#10a37f - tile magenta
#c23bb5
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on page | 19.44:1 | 4.5:1 |
| Aa | muted text on page | 4.99:1 | 4.5:1 |
| Aa | text on note callout | 17.68:1 | 4.5:1 |
| Aa | Continue label on green | 4.60:1 | 4.5:1 |
| input border on white | 3.22:1 | 3:1 | |
| focus ring on white | 3.20:1 | 3:1 | |
| Aa | error text on white | 4.99:1 | 4.5:1 |
| Aa | muted nav text on sidebar | 4.66: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.
- background
- card
- muted
- primary
- secondary
- accent
- destructive
Type scale
- Display
- Inter 600 - 24px page title, 18px modal title, -0.01em
- Body
- Inter 400 16px / 1.5; field labels 16px 600
Similar to the observed neo-grotesk. Credit amount 32px 600 with tabular numerals.
Spacing and imagery
Comfortable; 4px base, 16px between fields in the modal, 24px modal padding; container max 960px; radius 6px inputs/buttons, 12px modal, 8px icon tiles; modal shadow 0 16px 48px rgba(0,0,0,.18).
No photos. Rounded-square coloured icon tiles (green, magenta, orange) with white line icons mark the billing links; a small grey card glyph sits inside the card field.
Components
- AppSidebar with nested Settings group
- SettingsTabs
- CreditBalance stat
- NoteBanner
- IconTileLinkList
- AddPaymentDialog
- CombinedCardInput (hosted element)
- CountrySelect
- AddressFields with responsive city/postcode row
- DialogFooter with Cancel and Continue
Interactions
- Add payment details opens the dialog with focus on the card field; Escape, the close button and Cancel all close it and restore focus to the trigger
- Country change reorders and relabels address fields (postcode vs ZIP, region vs state) and resets region
- Continue is disabled until the hosted card element reports complete and required fields are valid
- On submit the button shows a spinner and the dialog locks; on success it closes and a toast confirms the card, and the Overview shows the card brand-neutral summary (ending 4242)
- Card errors from the provider render under the card field in #d0342c
Data
Organisation{id, name, plan (trial|pay_as_you_go), credit_balance}PaymentMethod{id, org_id, provider_ref, brand, last4, exp_month, exp_year, is_default}BillingAddress{org_id, name_on_card, country, line1, line2?, city, postcode, region?}Member{user_id, org_id, role (owner|billing|member)}
Guardrails
Experience
- Open the modal in place so the credit balance stays visible behind it
- Explain up front, in a short note, that the card is stored and can be removed later
- Put city and postcode side by side on desktop to shorten the form
- Label the primary button Continue (not Pay) because nothing is charged yet
- After success, show where the card now lives (Payment methods tab)
Accessibility
- Dialog uses role=dialog with aria-labelledby on the title and traps focus; the page behind gets inert
- Group the address inputs in a fieldset with legend Billing address
- Put autocomplete tokens on every field (cc-name, address-line1, postal-code, country)
- Errors are text, not only red borders, and are announced in a live region
- Visible 2px green focus ring on all inputs and buttons
- The coloured icon tiles have text labels beside them; never rely on colour to distinguish links
Security
- Never send card numbers to your own server; only the provider token and non-sensitive card metadata are stored
- Only owner and billing roles can create or remove payment methods, enforced in RLS and the Edge Function
- Validate and length-limit address fields server-side; strip control characters
- Rate-limit setup-intent creation per organisation
- Log payment-method changes to an audit table with actor and timestamp
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 **Nimbrel**, the billing settings screen of a developer platform with an Add payment details modal. Include the app shell (sidebar and settings tabs), realistic mock data (a trial organisation with 4.99 in credit) and every state of adding or replacing a card.
### Stack
Use React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, Radix, lucide-react, TanStack Query, react-hook-form, zod, Supabase. Card capture uses a hosted checkout provider's setup-intent flow and card element. Use Supabase for Auth, Postgres (row-level security on every table) and Storage where noted; keep only the anon key in the browser and run privileged work in Edge Functions.
### Pages & layout
1. **/settings/billing** (Overview tab) with the dialog triggered from the primary button.
2. **/settings/billing/payment-methods** listing saved cards with default badge and remove action.
3. **/settings/billing/history** stub table of invoices so the tabs are real routes.
Regions, in order:
- Left sidebar (~240px): workspace logo, product links (playground, assistants, fine-tuning, API keys, files, usage), Settings group with nested organisation, team, limits, billing (active), profile; bottom documentation, help and account switcher
- Main: page title Billing settings, tabs (Overview, Payment methods, Billing history, Preferences)
- Overview body: plan label, credit remaining with info icon and large amount, primary Add payment details + secondary View usage buttons, info note banner, list of three icon-tile links (payment methods, preferences, pricing)
- Modal (~420px, centered, 12px radius) over a 50% black scrim: title + close, grey note callout, Card information (single combined input), Name on card, Billing address (country select, line 1, line 2, city + postcode row, region), footer with Cancel and Continue
- Below 640px the modal becomes a full-height bottom sheet with a sticky footer; sidebar collapses into a hamburger drawer
### Design system
- Colors: `--page: #ffffff` (page), `--sidebar: #f7f7f8` (sidebar), `--text: #0d0d0d` (text), `--muted-text: #6e6e80` (muted text), `--note-callout: #f4f4f5` (note callout), `--input-border: #8e8ea0` (input border), `--primary-green: #0d8567` (primary green), `--on-primary: #ffffff` (on primary), `--scrim: #7f7f7f` (scrim), `--danger: #d0342c` (danger), `--focus-ring: #10a37f` (focus ring), `--tile-magenta: #c23bb5` (tile magenta).
- Fonts: Inter 600 - 24px page title, 18px modal title, -0.01em for headings; Inter 400 16px / 1.5; field labels 16px 600 for body. Similar to the observed neo-grotesk. Credit amount 32px 600 with tabular numerals.
- Spacing, radius and shadows: Comfortable; 4px base, 16px between fields in the modal, 24px modal padding; container max 960px; radius 6px inputs/buttons, 12px modal, 8px icon tiles; modal shadow 0 16px 48px rgba(0,0,0,.18).
- Motion: 150-200 ms ease-out for hover, focus and overlay transitions; overlays fade and scale from 98% to 100%; everything collapses to an instant change under prefers-reduced-motion.
- Mood: Neutral, utilitarian and trustworthy; the payment step looks like part of the settings, not a sales page. Imagery: No photos. Rounded-square coloured icon tiles (green, magenta, orange) with white line icons mark the billing links; a small grey card glyph sits inside the card field.
### Components & interactions
Build these components: AppSidebar with nested Settings group; SettingsTabs; CreditBalance stat; NoteBanner; IconTileLinkList; AddPaymentDialog; CombinedCardInput (hosted element); CountrySelect; AddressFields with responsive city/postcode row; DialogFooter with Cancel and Continue.
- Add payment details opens the dialog with focus on the card field; Escape, the close button and Cancel all close it and restore focus to the trigger
- Country change reorders and relabels address fields (postcode vs ZIP, region vs state) and resets region
- Continue is disabled until the hosted card element reports complete and required fields are valid
- On submit the button shows a spinner and the dialog locks; on success it closes and a toast confirms the card, and the Overview shows the card brand-neutral summary (ending 4242)
- Card errors from the provider render under the card field in #d0342c
Use shadcn Dialog on desktop and a Drawer (bottom sheet) under 640px via a small `ResponsiveDialog` wrapper so both share the same form component.
### Data & state
Model: `Organisation{id, name, plan (trial|pay_as_you_go), credit_balance}`; `PaymentMethod{id, org_id, provider_ref, brand, last4, exp_month, exp_year, is_default}`; `BillingAddress{org_id, name_on_card, country, line1, line2?, city, postcode, region?}`; `Member{user_id, org_id, role (owner|billing|member)}`.
A server call creates a setup intent; the client confirms it with the provider element, then an Edge Function stores only provider reference, brand, last4 and expiry. Address data is validated by a zod schema keyed by country. TanStack Query invalidates organisation and payment-method queries after success.
States to implement and demo:
- trial with credit remaining and no card on file
- dialog pristine
- inline validation errors per field
- card declined / incomplete card number
- submitting (disabled footer, spinner)
- success toast and updated overview
- card already on file (dialog becomes Replace card)
- network error banner inside the dialog with retry
- permission-denied for members who are not billing admins (button hidden, explanatory note)
### Accessibility
- Dialog uses role=dialog with aria-labelledby on the title and traps focus; the page behind gets inert
- Group the address inputs in a fieldset with legend Billing address
- Put autocomplete tokens on every field (cc-name, address-line1, postal-code, country)
- Errors are text, not only red borders, and are announced in a live region
- Visible 2px green focus ring on all inputs and buttons
- The coloured icon tiles have text labels beside them; never rely on colour to distinguish links
- Body text is 16px with line-height 1.5 (15px only inside dense tables), nothing renders below 12px, weights of 300 or lighter appear only at 24px and above, and uppercase is limited to short labels with at least 0.05em tracking.
Verified contrast: body text on page: #0d0d0d on #ffffff = 19.44:1; muted text on page: #6e6e80 on #ffffff = 4.99:1; text on note callout: #0d0d0d on #f4f4f5 = 17.68:1; Continue label on green: #ffffff on #0d8567 = 4.60:1; input border on white: #8e8ea0 on #ffffff = 3.22:1; focus ring on white: #10a37f on #ffffff = 3.20:1; error text on white: #d0342c on #ffffff = 4.99:1; muted nav text on sidebar: #6e6e80 on #f7f7f8 = 4.66:1.
### Security
- Never send card numbers to your own server; only the provider token and non-sensitive card metadata are stored
- Only owner and billing roles can create or remove payment methods, enforced in RLS and the Edge Function
- Validate and length-limit address fields server-side; strip control characters
- Rate-limit setup-intent creation per organisation
- Log payment-method changes to an audit table with actor and timestamp
RLS: `organisations` - members select; `payment_methods` - select for members, insert/update/delete only where the caller's membership role is owner or billing; `billing_addresses` - same as payment methods; `members` - users select their own org rows only.
### Performance & SEO
Lazy-load the provider script when the dialog first opens, not on page load. Settings routes are client-rendered and noindex. Keep dialog open time under 100ms by prefetching the country list.
### Guardrails
- Open the modal in place so the credit balance stays visible behind it
- Explain up front, in a short note, that the card is stored and can be removed later
- Put city and postcode side by side on desktop to shorten the form
- Label the primary button Continue (not Pay) because nothing is charged yet
- After success, show where the card now lives (Payment methods tab)
- Use the product name Nimbrel and fresh, generic copy throughout; all people, companies, amounts and IDs are invented, and no third-party brand, logo or wordmark appears.
- Keep components small and typed (no `any`), and surface every failure visibly instead of swallowing it.
Acceptance criteria:
- [ ] Card can be added, replaced and removed
- [ ] Country change adapts address fields
- [ ] Declined card shows an inline error and keeps other inputs
- [ ] Non-billing members cannot see the Add button or call the API
- [ ] Dialog becomes a bottom sheet at 390px
- [ ] Keyboard-only users can complete the flow