Skip to main content
vibld

Template

Quellkey

The organisation API keys page of an AI workflow automation platform, shown before any key exists. It explains what keys are for (CLI, SDK and API access to trigger workflows) and gives one clear path to create the first key, which is then shown exactly once.

API keys page with empty state and create action · App screen: empty state · 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/1.3, -0.01em
  • InterBody: Inter 400 16px/1.5 (15px in the keys table)

Patterns

  • persistent left sidebar with uppercase group labels
  • dark filled active nav pill
  • org switcher in breadcrumb bar
  • page header with title, subtitle and right-aligned primary
  • bordered empty-state panel with line icon
  • system/light/dark theme segmented control in sidebar footer
  • reveal-once secret dialog

States it is designed for

  • empty (no keys)
  • list with keys
  • creating (button spinner)
  • secret revealed once
  • secret dialog closed without copying (warning)
  • revoked key row greyed with 'Revoked' tag
  • expired key flagged
  • non-admin view (Create hidden, read-only list)
  • load error with retry

Who it is for

  • developers integrating workflows
  • platform admins
  • DevOps engineers

Layout

  1. left sidebar (~240px): logo, top group (Trigger, Runs, Reviews, Monitoring), labelled groups Agent, Workflow, Developer (Webhooks, API keys active, Connect), Settings; footer: Documentation link, avatar, theme segmented control, collapse button
  2. top bar: org switcher chip with avatar initial and chevron, slash, page name
  3. page header: 24px title, one-line subtitle; 'Create API key' dark button top-right with plus icon
  4. empty panel spanning content width: key icon, bold 'No API keys', helper line
  5. populated: table of keys (name, prefix, created, last used, created by, revoke)
  6. mobile: sidebar to drawer; header button becomes full width under the title

Palette

Monochrome, precise, trustworthy; almost all contrast comes from near-black on soft grey.

  • app background#f7f7f7
  • panel surface#fafafa
  • panel border#e0e1e1
  • primary text#171717
  • secondary text#5c5c5c
  • group label#666666
  • primary button / active nav#1c1c1c
  • brand mark green#15803d
  • danger (revoke)#c42b1c
  • empty icon#8a8a8a

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text16.73:14.5:1
Aasubtitle / helper6.41:14.5:1
Aagroup label5.36:14.5:1
Aaprimary button label16.33:14.5:1
Aarevoke text5.42:14.5:1
empty icon3.31:13:1
brand mark4.68: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.

  • background
  • card
  • muted
  • primary
  • secondary
  • accent
  • destructive

Type scale

Display
Inter 600, 24px/1.3, -0.01em
Body
Inter 400 16px/1.5 (15px in the keys table)

Sidebar 14px 500; group labels 12px uppercase 500, 0.06em tracking; key prefixes in JetBrains Mono 14px.

Spacing and imagery

Compact: sidebar rows 32px; content padding 24px; empty panel 40px vertical padding, 12px radius, 1px border; buttons 32px tall, 8px radius.

Single outlined key icon (48px, grey) in the empty panel; lucide line icons throughout; a small green asterisk-like brand glyph.

Components

  • Sidebar with GroupLabel
  • OrgSwitcher
  • PageHeader
  • EmptyPanel
  • CreateKeyDialog (name, scope, expiry)
  • SecretRevealDialog (copy once)
  • KeysTable
  • RevokeConfirmDialog
  • ThemeSegmentedControl

Interactions

  • Create opens a dialog with name, scope (read / trigger / admin) and expiry select
  • after create, the full secret shows once with Copy and a 'I've stored it' confirm
  • copy shows a check for 1.5s
  • revoke requires typing the key name
  • theme control switches system/light/dark instantly
  • sidebar collapse to icons with tooltips

Data

  • ApiKey{id, org_id, name, prefix, hash, scope (read|trigger|admin), expires_at, last_used_at, created_by, revoked_at}
  • Org{id, name, slug}
  • Membership{org_id, user_id, role (owner|admin|member)}
  • AuditEvent{id, org_id, actor_id, action, target_id, created_at}

Guardrails

Experience

  • Say what keys unlock in the subtitle and the empty helper line.
  • Show the secret once, with a clear warning it cannot be shown again.
  • List only prefixes afterwards (e.g. 'wf_live_3k…'), never full keys.
  • Default new keys to the least powerful scope and a 90-day expiry.
  • Keep one primary action per view; the header button is the only dark button.

Accessibility

  • Active nav item has aria-current='page' plus the filled pill.
  • Group labels render as visually small headings, not decorative text.
  • Secret field is read-only with a labelled Copy button and live-region confirmation.
  • Theme control is a radio group with labels, not icons alone.
  • Dialogs trap focus and return it to the trigger.

Security

  • Store only a salted hash of each key; generate secrets server-side with a CSPRNG.
  • RLS: keys readable by org members, insert/revoke only by admins and owners.
  • Revocation takes effect immediately and is written to the audit log.
  • Rate-limit key creation and require recent re-authentication for admin-scope keys.

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 **Quellkey**, the API keys settings page for a workflow automation platform. It must look complete when empty, make creating the first key obvious, reveal the secret exactly once, and support listing, expiring and revoking keys with an audit trail.

### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Dialog, Select, Table, Tooltip, ToggleGroup), lucide-react, TanStack Query, react-hook-form + zod, date-fns. Supabase Auth, Postgres, and an Edge Function that mints and hashes keys.

### Pages & layout
1. **App shell**: grouped sidebar, breadcrumb top bar with org switcher.
2. **/settings/api-keys**: header + empty panel or keys table.
3. **Create / reveal / revoke dialogs**.
4. Responsive: drawer sidebar <1024px; table becomes stacked cards <640px.

### Design system
- Colors: `--bg: #f7f7f7` (app background), `--panel: #fafafa` (panel surface), `--border: #e0e1e1` (panel border), `--fg: #171717` (primary text), `--muted: #5c5c5c` (secondary text), `--label: #666666` (group label), `--primary: #1c1c1c` (primary button / active nav), `--brand: #15803d` (brand mark green), `--danger: #c42b1c` (danger (revoke)), `--icon: #8a8a8a` (empty icon).
- Fonts: Inter 600, 24px/1.3, -0.01em for headings; Inter 400 16px/1.5 (15px in the keys table) for body. Sidebar 14px 500; group labels 12px uppercase 500, 0.06em tracking; key prefixes in JetBrains Mono 14px.
- Spacing: Compact: sidebar rows 32px; content padding 24px; empty panel 40px vertical padding, 12px radius, 1px border; buttons 32px tall, 8px radius.
- Radius: 8px buttons and nav pill, 12px panel and dialogs.
- Shadows: dialogs only.
- Motion: 150ms fades; no motion on the empty panel.
- Dark theme: swap bg/panel to #111111/#1a1a1a and text to #ededed; recheck pairs.

### Components & interactions
Sidebar, GroupLabel, OrgSwitcher, PageHeader, EmptyPanel, CreateKeyDialog, SecretRevealDialog, KeysTable (sortable by created and last used), RevokeConfirmDialog, ThemeSegmentedControl.

Interactions: Create opens a dialog with name, scope (read / trigger / admin) and expiry select; after create, the full secret shows once with Copy and a 'I've stored it' confirm; copy shows a check for 1.5s; revoke requires typing the key name; theme control switches system/light/dark instantly; sidebar collapse to icons with tooltips.

States to build: empty (no keys); list with keys; creating (button spinner); secret revealed once; secret dialog closed without copying (warning); revoked key row greyed with 'Revoked' tag; expired key flagged; non-admin view (Create hidden, read-only list); load error with retry.

### Data & state
The Edge Function returns the plaintext secret once and stores `prefix` + `hash`. The table reads keys without hashes via a view. Theme preference in localStorage (try/catch). Seed an org 'Northwind Labs' with an empty key list and a second org with three keys.

Model: `ApiKey{id, org_id, name, prefix, hash, scope (read|trigger|admin), expires_at, last_used_at, created_by, revoked_at}`; `Org{id, name, slug}`; `Membership{org_id, user_id, role (owner|admin|member)}`; `AuditEvent{id, org_id, actor_id, action, target_id, created_at}`.

### Accessibility
Active nav item has aria-current='page' plus the filled pill. Group labels render as visually small headings, not decorative text. Secret field is read-only with a labelled Copy button and live-region confirmation. Theme control is a radio group with labels, not icons alone. Dialogs trap focus and return it to the trigger.
Verified contrast: body text: #171717 on #f7f7f7 = 16.73:1; subtitle / helper: #5c5c5c on #fafafa = 6.41:1; group label: #666666 on #f7f7f7 = 5.36:1; primary button label: #fafafa on #1c1c1c = 16.33:1; revoke text: #c42b1c on #fafafa = 5.42:1; empty icon: #8a8a8a on #fafafa = 3.31:1; brand mark: #15803d on #f7f7f7 = 4.68:1.

### Security
Store only a salted hash of each key; generate secrets server-side with a CSPRNG. RLS: keys readable by org members, insert/revoke only by admins and owners. Revocation takes effect immediately and is written to the audit log. Rate-limit key creation and require recent re-authentication for admin-scope keys. RLS per table: `api_keys` select for org members (through a view that omits `hash`), insert/update only via the Edge Function (service role) after checking admin role; `memberships` select own org; `audit_events` insert by service role, select by admins.

### Performance & SEO
Split dialogs into lazy chunks. Settings routes are noindex. Keep the key list query paginated at 50.

### Guardrails
- Say what keys unlock in the subtitle and the empty helper line.
- Show the secret once, with a clear warning it cannot be shown again.
- List only prefixes afterwards (e.g. 'wf_live_3k…'), never full keys.
- Default new keys to the least powerful scope and a 90-day expiry.
- Keep one primary action per view; the header button is the only dark button.
- Write all copy fresh; use invented people, companies and numbers only. No real brands, logos, wordmarks or third-party product names anywhere in the UI or seed data.
- Do not trace or copy any existing product's layout assets, icons or illustrations; draw generic ones.
- Acceptance criteria:
  - [ ] Empty state shows title, helper and Create
  - [ ] A created key is visible in full only once
  - [ ] The list shows prefix, scope, expiry and last used
  - [ ] Revoking requires typing the name and logs an audit event
  - [ ] Non-admins cannot create or revoke

Open the builderAll templatesThis palette on its own