Skip to main content
vibld

Template

Orsinth

In an automation platform's developer settings, a user creates an API key from a right-hand sheet that asks only for a name. After creation, the sheet swaps to a one-time reveal of the secret with copy and a warning, then the key list updates.

Create API key side sheet · App screen: add edit · 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.

  • GeistHeadings: Geist 600 20px sheet title
  • GeistBody: Geist 400 16px / 1.5

Patterns

  • right-side slide-over sheet for create
  • single-field form with example placeholder
  • footer action bar with cancel and primary
  • empty state behind the overlay
  • one-time secret reveal after creation

States it is designed for

  • empty key list
  • sheet pristine
  • name validation error (blank, too long, duplicate)
  • creating
  • secret revealed (shown once)
  • list with keys
  • revoke confirm / revoked
  • error creating key toast
  • member without permission: button hidden and note

Who it is for

  • developers integrating automation APIs
  • platform admins managing credentials

Layout

  1. Background: app shell (left sidebar with grouped nav, top workspace switcher) and an API keys page showing an empty state, dimmed by a 35% grey scrim
  2. Right sheet (~420px, full height, 12px left radius, 8px inset from edges): header with title, two-line description and close; body with Name field; footer bar with Cancel and Create key
  3. After create: body shows the key in a monospace read-only field with Copy, a warning callout, and a Done button
  4. Mobile: sheet becomes full-screen

Palette

Minimal and neutral; greys dominate so the one-time secret moment stands out.

  • sheet surface#ffffff
  • app bg#f5f5f5
  • text#171717
  • muted text#666666
  • input border#8a8a8a
  • primary#404040
  • on primary#ffffff
  • footer bg#fafafa
  • warning bg#fff7e0
  • warning text#7a4b00
  • focus ring#2563eb
  • scrim#a8a8a8

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on sheet17.93:14.5:1
Aamuted description5.74:14.5:1
AaCreate key label on dark10.37:14.5:1
input border on white3.45:13:1
Aawarning text on warning bg6.93:14.5:1
focus ring on white5.17:13:1
Aamuted text on footer5.50:14.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
Geist 600 20px sheet title
Body
Geist 400 16px / 1.5

Similar to the observed neo-grotesk; Geist Mono 15px for the revealed key.

Spacing and imagery

Comfortable; 20px sheet padding, 8px label gap, footer 64px tall; radius 8px inputs and buttons, 12px sheet; sheet shadow -8px 0 32px rgba(0,0,0,.12).

Empty state behind uses a faint key line icon; nothing else.

Components

  • AppSidebar
  • ApiKeysEmptyState
  • CreateKeySheet
  • NameField
  • SheetFooter
  • SecretReveal (read-only mono field + copy + warning)
  • ApiKeysTable (name, prefix, created, last used, revoke)

Interactions

  • Create API key button opens the sheet sliding in from the right (200ms); focus goes to Name
  • Enter in the Name field submits
  • Create key disabled until a name of 1-64 chars is entered
  • After creation, the Copy button copies the secret and changes to 'Copied'
  • Closing the reveal asks for confirmation if Copy was never pressed
  • Revoke in the table opens a confirm dialog

Data

  • ApiKey{id, workspace_id, name, prefix, hash, scope ('*'), created_by, created_at, last_used_at?, revoked_at?}
  • Member{user_id, workspace_id, role (admin|developer|viewer)}

Guardrails

Experience

  • Ask for the minimum (a name) and explain scope in the description
  • Give a realistic placeholder example
  • Make the one-time nature of the secret explicit
  • Keep Cancel visible in the footer at all times
  • Show key prefix in the table so users can recognise keys

Accessibility

  • Sheet is a dialog with aria-labelledby and focus trap; scrim click closes
  • Secret field is labelled and selectable; Copy announces 'Copied to clipboard'
  • Warning callout uses role=note and text, not only colour
  • Table has captions and row actions with key names in their labels
  • Visible blue focus ring on grey surfaces

Security

  • Store only a salted hash of each key; show the full secret exactly once
  • Only admins and developers can create or revoke keys (RLS and function check)
  • Rate-limit key creation per workspace
  • Log creation and revocation in an audit table
  • Never log the secret or send it to analytics

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 **Orsinth**, the API keys page of an automation platform with a right-side Create API key sheet and a one-time secret reveal. Include the app shell, an empty state, a populated table and all creation and revocation states.

### Stack
Use React 18, TypeScript, Vite, Tailwind CSS, shadcn/ui, Radix, lucide-react, TanStack Query, react-hook-form, zod, Supabase.  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/api-keys** with empty state and table.
2. Sheet as an overlay on that route (URL param `?new=1` so it can be linked).

Regions, in order:
- Background: app shell (left sidebar with grouped nav, top workspace switcher) and an API keys page showing an empty state, dimmed by a 35% grey scrim
- Right sheet (~420px, full height, 12px left radius, 8px inset from edges): header with title, two-line description and close; body with Name field; footer bar with Cancel and Create key
- After create: body shows the key in a monospace read-only field with Copy, a warning callout, and a Done button
- Mobile: sheet becomes full-screen

### Design system
- Colors: `--sheet-surface: #ffffff` (sheet surface), `--app-bg: #f5f5f5` (app bg), `--text: #171717` (text), `--muted-text: #666666` (muted text), `--input-border: #8a8a8a` (input border), `--primary: #404040` (primary), `--on-primary: #ffffff` (on primary), `--footer-bg: #fafafa` (footer bg), `--warning-bg: #fff7e0` (warning bg), `--warning-text: #7a4b00` (warning text), `--focus-ring: #2563eb` (focus ring), `--scrim: #a8a8a8` (scrim).
- Fonts: Geist 600 20px sheet title for headings; Geist 400 16px / 1.5 for body. Similar to the observed neo-grotesk; Geist Mono 15px for the revealed key.
- Spacing, radius and shadows: Comfortable; 20px sheet padding, 8px label gap, footer 64px tall; radius 8px inputs and buttons, 12px sheet; sheet shadow -8px 0 32px rgba(0,0,0,.12).
- 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: Minimal and neutral; greys dominate so the one-time secret moment stands out. Imagery: Empty state behind uses a faint key line icon; nothing else.

### Components & interactions
Build these components: AppSidebar; ApiKeysEmptyState; CreateKeySheet; NameField; SheetFooter; SecretReveal (read-only mono field + copy + warning); ApiKeysTable (name, prefix, created, last used, revoke).

- Create API key button opens the sheet sliding in from the right (200ms); focus goes to Name
- Enter in the Name field submits
- Create key disabled until a name of 1-64 chars is entered
- After creation, the Copy button copies the secret and changes to 'Copied'
- Closing the reveal asks for confirmation if Copy was never pressed
- Revoke in the table opens a confirm dialog

### Data & state
Model: `ApiKey{id, workspace_id, name, prefix, hash, scope ('*'), created_by, created_at, last_used_at?, revoked_at?}`; `Member{user_id, workspace_id, role (admin|developer|viewer)}`.

Keys are generated in an Edge Function: random 32 bytes, return the full secret once, store only a hash and a short prefix. TanStack Query refetches the list after create or revoke.

States to implement and demo:
- empty key list
- sheet pristine
- name validation error (blank, too long, duplicate)
- creating
- secret revealed (shown once)
- list with keys
- revoke confirm / revoked
- error creating key toast
- member without permission: button hidden and note

### Accessibility
- Sheet is a dialog with aria-labelledby and focus trap; scrim click closes
- Secret field is labelled and selectable; Copy announces 'Copied to clipboard'
- Warning callout uses role=note and text, not only colour
- Table has captions and row actions with key names in their labels
- Visible blue focus ring on grey surfaces
- 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 sheet: #171717 on #ffffff = 17.93:1; muted description: #666666 on #ffffff = 5.74:1; Create key label on dark: #ffffff on #404040 = 10.37:1; input border on white: #8a8a8a on #ffffff = 3.45:1; warning text on warning bg: #7a4b00 on #fff7e0 = 6.93:1; focus ring on white: #2563eb on #ffffff = 5.17:1; muted text on footer: #666666 on #fafafa = 5.50:1.

### Security
- Store only a salted hash of each key; show the full secret exactly once
- Only admins and developers can create or revoke keys (RLS and function check)
- Rate-limit key creation per workspace
- Log creation and revocation in an audit table
- Never log the secret or send it to analytics

RLS: `api_keys` - select for workspace members (columns: name, prefix, dates only via a view), insert via Edge Function, update `revoked_at` only for admin/developer roles; `members` - select within workspace.

### Performance & SEO
Tiny page; lazy-load the sheet contents. Noindex.

### Guardrails
- Ask for the minimum (a name) and explain scope in the description
- Give a realistic placeholder example
- Make the one-time nature of the secret explicit
- Keep Cancel visible in the footer at all times
- Show key prefix in the table so users can recognise keys
- Use the product name Orsinth 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:
- [ ] Key created, shown once, and listed by prefix
- [ ] Duplicate names rejected
- [ ] Revoke works with confirmation
- [ ] Viewers cannot create keys
- [ ] Sheet is full-screen at 390px

Open the builderAll templatesThis palette on its own