Skip to main content
vibld

Template

Cedarglow

A starter for selling a per-seat subscription: a dark pricing page that shows prices in the visitor's local currency, a hosted checkout hand-off, and a signed-in area where customers see their plan, next payment and past invoices. It is for small SaaS teams that want billing wired end to end before they build the product itself.

Localized subscription pricing starter with customer billing portal · App · Small tools and apps · full-stack app (auth + DB)

A mock-up of the homepage, drawn from this design’s layout, palette and typefaces. A build follows the full prompt below.

Add app screens

Pick up to 6 screens, such as a dashboard, settings or an empty state. Each is built in this design’s own palette and typefaces, with its states and guardrails.

Account setup
Add edit
Analytics
Appearance
Calendar
Chat
Checklist
Checkout
Confirmation
Dashboard
Delete account
Details
Discovery questions
Empty state
Flowchart
Import export
Inbox
Integrations
Invite team
Loading
Login
Onboarding
Plans
Playground
Product tour
Referral
Search
Settings
Share
Sign up
Success
Table
Team members
Text editor
Upgrade
Usage
Verification
Welcome

Start from this templateRead the build prompt

Typefaces

Mozilla Text is a clean, calm grotesk with a firm 500, so the lit featured plan and its large tabular prices look premium on the near-black teal.

  • Mozilla TextHeadings: headline 80px / 44px 500 at -0.02em, prices 56px 500 tabular
  • Mozilla TextBody: body 16px/1.5 400/500, per-unit labels 12px

Patterns

  • dismissible announcement bar with region selector
  • centered two-line hero headline
  • segmented control monthly/annual
  • three-tier pricing cards with highlighted middle tier
  • radial glow behind featured card
  • faint grid backdrop
  • check-icon feature lists
  • built-with logo row
  • persistent left sidebar in account area
  • stat cards row
  • subscription detail with line-item table

States it is designed for

  • price loading shimmer per card
  • unsupported region falls back to USD with a notice
  • checkout closed without paying (no change, gentle toast)
  • checkout success confirmation page
  • webhook not yet received: 'activating your plan' pending state with polling
  • empty dashboard for free users with upgrade prompt
  • past-due subscription with update-payment banner
  • cancelled but active until period end
  • error loading invoices with retry

Who it is for

  • indie SaaS founders
  • small product teams adding paid plans
  • developers evaluating a merchant-of-record style billing flow

Layout

  1. Top announcement strip: globe icon, 'preview local prices' label, country combobox, dismiss button; collapses away on mobile
  2. Header: wordmark with small sub-label left, translucent 'Sign in' button right
  3. Hero: two-line centered headline (80px desktop, 44px mobile), one-line subhead, monthly/annual segmented control
  4. Pricing: three cards in a row (stacked on <768px), middle card lifted by a soft green radial glow and a 'most popular' badge
  5. Built-with row: five greyscale partner marks spaced evenly, wraps to two rows on mobile
  6. Footer: hairline divider with a centered glow, credit line and three external links
  7. Signed-in app: 240px left sidebar (Dashboard, Subscriptions, Payments) + main column; sidebar becomes a sheet under 1024px
  8. Dashboard main: stat cards row (seats, storage, projects) above an active-subscriptions list
  9. Subscription detail: plan header with price and status pill, next-payment card, line-item table, cancel action

Palette

Dark, calm, premium. A near-black teal canvas with a single green-yellow bloom makes the featured plan feel lit from behind.

  • page background#0f1515
  • card surface#182222
  • raised control / segmented thumb#272f30
  • primary text#f4f4f5
  • secondary text#8c8f8f
  • translucent button fill (composited)#464e4e
  • badge text and focus ring#fff800
  • decorative radial glow#3d5a2c
  • input and card border#5f6969
  • active status#4ade80
  • past-due status#f87171

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text16.78:14.5:1
Aabody text on card14.80:14.5:1
Aamuted text on card4.99:14.5:1
Aabutton label8.54:14.5:1
Aabadge / accent text14.47:14.5:1
card and input border3.26:13:1
focus ring16.41:13:1
active status9.34:13:1
past-due status5.88:13: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
Mozilla Text 500, 80px/1.0 desktop and 44px mobile, tracking -0.02em
Body
Mozilla Text 400/500, 16px/1.5

Prices in Mozilla Text 500 at 56px with tabular numerals; per-unit labels 12px; Mozilla Text stands in for the observed face.

Spacing and imagery

Comfortable density; 1200px container with 24px gutters; 12-column grid; cards 32px padding; radius 14px on cards and buttons, 8px on segmented thumb; no drop shadows, depth from the glow and 1px borders.

No photography. Line icons in 32px circular chips on each plan, a very faint square grid backdrop, greyscale partner marks, and a soft radial gradient; the app area uses small plan icons only.

Components

  • AnnouncementBar with country Combobox
  • SegmentedControl (Monthly / Annual)
  • PricingCard with icon chip, price, per-unit label, description, CTA, check list
  • PopularBadge
  • PartnerLogoRow
  • AppSidebar
  • StatCard
  • SubscriptionRow
  • StatusPill
  • LineItemTable
  • NextPaymentCard
  • CancelSubscriptionDialog
  • Toast

Interactions

  • Toggling monthly/annual cross-fades prices in 200ms and announces the new total via aria-live
  • Changing country refetches localized prices and shows a shimmer on each price while loading
  • Card CTA opens the hosted checkout overlay; on success route to /checkout/success
  • Hover on cards raises border opacity; focus shows a 2px accent ring with 2px offset
  • Sidebar items show an active pill; Escape closes the mobile sheet
  • Cancel flow: dialog with plan end date and a typed confirmation

Data

  • Plan{id, name, tier (starter|pro|advanced), monthly_price_id, annual_price_id, features[]}
  • Customer{id, user_id, provider_customer_id, email, country}
  • Subscription{id, customer_id, plan_id, status (active|trialing|past_due|paused|canceled), interval (month|year), seats, current_period_end, cancel_at}
  • Transaction{id, subscription_id, amount, currency, status (completed|failed|refunded), billed_at, invoice_url}
  • WebhookEvent{id, provider_event_id, type, received_at, processed_at}

Guardrails

Experience

  • Show the billing interval and currency next to every price, never a bare number
  • Keep the featured card the same size as the others; emphasise with glow and badge only
  • Annual toggle states the saving in words (e.g. '2 months free') rather than colour alone
  • Never show a plan as active until the webhook confirms it; show a pending state instead
  • Put cancel at the bottom of the subscription page with the end date spelled out
  • Let visitors change country without losing their interval choice

Accessibility

  • Segmented control is a radiogroup with arrow-key navigation
  • Price changes are announced in a polite live region
  • Plan feature lists are real lists with visually hidden 'included' text for the check icons
  • The glow and grid are aria-hidden decoration
  • Status pills pair colour with text labels
  • Focus ring uses the accent at 3:1 or better against the canvas

Security

  • RLS on customers, subscriptions and transactions: select only where user_id = auth.uid(); no client inserts or updates
  • Webhook endpoint verifies the provider signature and is idempotent on provider_event_id
  • Only a client-side token for price previews in the browser; server API key lives in environment secrets
  • Never trust client-sent price ids: map plan and interval to price ids on the server
  • Rate-limit the checkout-session endpoint per user and IP

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 **Cedarglow**, a subscription-billing starter for a per-seat creative tool. Visitors land on a dark pricing page that shows each plan in their local currency, pick monthly or annual billing, and pay through a hosted checkout overlay. Signed-in customers get a small account area to see their plan, seats, next payment and invoices, and to cancel. The billing plumbing (webhooks, customer sync, entitlements) is the real product here; the UI must make every billing state legible.

### Stack
Next.js (App Router) with React and TypeScript, Tailwind CSS, shadcn/ui on Radix, lucide-react icons. TanStack Query for client data, react-hook-form + zod for the few forms, date-fns for period dates. Supabase for Auth (email magic link + one OAuth provider), Postgres tables and an Edge Function that receives billing webhooks. Use a hosted checkout provider that supports localized price previews via a client-side token.

### Pages & layout
1. **/pricing** (also the home page): dismissible announcement strip with a country combobox; header with wordmark and translucent "Sign in"; centered two-line headline and subhead; monthly/annual segmented control; three plan cards (Starter, Pro, Advanced), the middle one with a "Most popular" badge and a green radial glow behind it; a built-with row of generic partner marks; hairline footer.
2. **/checkout/success**: confirmation card that polls until the subscription row exists, then links to the dashboard.
3. **/login**: single card on the same canvas.
4. **/app**: left sidebar (Dashboard, Subscriptions, Payments). Dashboard shows a stat cards row (seats used, storage, projects) and an active-subscriptions list.
5. **/app/subscriptions/[id]**: plan header with price and status pill, next-payment card, line-item table, "Cancel subscription" at the bottom.
6. **/app/payments**: transactions table with status and invoice link.
Cards stack to one column below 768px; the sidebar becomes a sheet below 1024px.

### Design system
- Colors: `--canvas: #0f1515` (page background), `--surface: #182222` (card surface), `--surface-2: #272f30` (raised control / segmented thumb), `--fg: #f4f4f5` (primary text), `--muted: #8c8f8f` (secondary text), `--btn: #464e4e` (translucent button fill (composited)), `--accent: #fff800` (badge text and focus ring), `--glow: #3d5a2c` (decorative radial glow), `--border: #5f6969` (input and card border), `--success: #4ade80` (active status), `--danger: #f87171` (past-due status).
- Fonts: Mozilla Text from Google Fonts. Headline 500 weight at 80px (44px mobile), -0.02em tracking; body 16px/1.5; prices 56px 500 with tabular numerals; smallest text 12px.
- Spacing: 4px base; card padding 32px; grid gap 24px; section spacing 96px desktop, 64px mobile.
- Radius: 14px cards and buttons, 8px segmented thumb, full for pills.
- Shadows: none. Depth comes from 1px borders, the radial glow (`#3d5a2c` at 40% opacity, 600px blur) and a 24px square grid at 4% white.
- Motion: 200ms ease-out for price cross-fades and hover; disable under `prefers-reduced-motion`.

### Components & interactions
AnnouncementBar (country Combobox, dismiss stored in localStorage), SegmentedControl as a Radix ToggleGroup, PricingCard (icon chip, name, badge, price, "per user/month", description, CTA, check list), PartnerLogoRow, AppSidebar, StatCard, SubscriptionRow, StatusPill, LineItemTable, NextPaymentCard, CancelSubscriptionDialog (typed confirmation, shows the end date), Toast. Changing country refetches price previews and shows a shimmer on each price. CTA opens the hosted checkout overlay with the plan's server-resolved price id.

### Data & state
Tables: `plans`, `customers(user_id, provider_customer_id, country)`, `subscriptions(customer_id, plan_id, status, interval, seats, current_period_end, cancel_at)`, `transactions(subscription_id, amount, currency, status, billed_at, invoice_url)`, `webhook_events(provider_event_id unique, type, processed_at)`. The Edge Function upserts customers, subscriptions and transactions from webhook payloads. Price previews are client state keyed by country + interval in TanStack Query. Seed a demo customer with one Pro subscription and six invented transactions.

### Accessibility
Segmented control is a radiogroup with arrow keys; price changes announce through a polite live region. Check icons carry visually hidden "Included" text. Glow and grid are `aria-hidden`. Status pills always include text. Focus ring is 2px `#fff800` with 2px offset. Tables use proper headers and captions.
Verified contrast: body text: #f4f4f5 on #0f1515 = 16.78:1; body text on card: #f4f4f5 on #182222 = 14.8:1; muted text on card: #8c8f8f on #182222 = 4.99:1; button label: #ffffff on #464e4e = 8.54:1; badge / accent text: #fff800 on #182222 = 14.47:1; card and input border: #5f6969 on #0f1515 = 3.26:1; focus ring: #fff800 on #0f1515 = 16.41:1; active status: #4ade80 on #182222 = 9.34:1; past-due status: #f87171 on #182222 = 5.88:1.

### Security
- RLS on `customers`, `subscriptions`, `transactions`: `select` where the row's `user_id = auth.uid()` (join through customers); no insert, update or delete from the client. `plans` is public read. `webhook_events` has no client access.
- Webhook handler verifies the provider signature, rejects stale timestamps, and dedupes on `provider_event_id`.
- The browser only holds the client-side preview token; the server API key and webhook secret live in environment secrets.
- Resolve price ids on the server from plan + interval; rate-limit checkout creation.

### Performance & SEO
Statically render /pricing with USD defaults and hydrate localized prices after load. Preload Mozilla Text 400/500 only. Keep the glow as a CSS radial gradient, not an image. Title, description and OG image on /pricing; noindex /app routes.

### Guardrails
- Invent plan names, prices and features; no real brands or customer logos (use neutral placeholder marks).
- Never mark a subscription active from the client; wait for the webhook.
- Keep types strict; surface billing errors in plain language.
- Acceptance criteria: (1) switching country and interval updates all three prices; (2) a test purchase creates customer, subscription and transaction rows; (3) replaying a webhook is a no-op; (4) another user cannot read your invoices; (5) keyboard users can complete checkout hand-off and cancellation.

Open the builderAll templatesThis palette on its own