Template
Tokenmoor
The API keys management page in a developer workflow platform. Admins see every organisation key with masked value, scopes, creation date, last use, expiry and status, and can create or revoke keys.
Organisation API keys table · App screen: table · 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 H1
- InterBody: Inter 400/500, 15px table cells (dense table), 16px elsewhere; column headers 14px 500
Patterns
- persistent left sidebar with grouped nav
- page header with primary action
- bordered data table in card
- mono key chip
- status and expiry chips
- row-level destructive icon button
- theme switcher in sidebar footer
States it is designed for
- Loading skeleton rows
- Empty ('No API keys yet' with Create button)
- Never used ('Never' in Last used)
- Expired row (muted, Expired chip)
- Revoked row (strikethrough name, Revoked chip, no delete)
- Revoke failure toast with retry
- Non-admin view (no create/revoke, explanatory note)
Who it is for
- Developers wiring CLI, SDK or API access
- Platform admins auditing credentials
- Security reviewers checking stale keys
Layout
- Left sidebar 240px: logo + product name, primary nav (Trigger, Runs, Reviews, Monitoring), labelled groups (Agent, Workflow, Developer) with active item as dark pill; footer: docs link, theme segmented control, collapse
- Top bar: org avatar + name switcher, breadcrumb 'API keys'
- Page header: H1, one-line description, dark 'Create API key' button right
- Table card: Name (key icon + name), Key (mono chip), Scopes (chip), Created, Last used, Expires (chip), Status (chip), delete icon
- Below 900px: table becomes stacked cards; sidebar becomes a drawer
Palette
precise, calm, utilitarian. Credentials presented like inventory, nothing flashy.
- app background
#f7f7f7 - table card
#ffffff - primary text
#18181b - muted text
#5e5e66 - table/input border
#8a8a93 - chip fill
#f0f0f1 - dark button / active nav
#18181b - danger icon
#b42318 - danger icon hover fill
#fef3f2 - success status
#15803d - focus ring
#2563eb
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on card | 17.72:1 | 4.5:1 |
| Aa | muted text on app bg | 6.00:1 | 4.5:1 |
| Aa | chip text on chip fill | 15.56:1 | 4.5:1 |
| Aa | button label on dark | 17.72:1 | 4.5:1 |
| danger icon on hover fill | 6.05:1 | 3:1 | |
| Aa | status text on card | 5.02:1 | 4.5:1 |
| table border | 3.42: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, 24px H1
- Body
- Inter 400/500, 15px table cells (dense table), 16px elsewhere; column headers 14px 500
Keys and scopes in JetBrains Mono 13px inside chips; similar to the observed grotesk + mono pairing.
Spacing and imagery
Dense table: 52px rows, 16px cell padding; page padding 16px/24px; card radius 10px, chips radius 6px; sidebar item 36px tall radius 8px.
Line icons only (key, trash, external link); no imagery.
Components
- Grouped sidebar nav
- Org switcher
- Page header with action
- Data table
- Mono key chip
- Scope chip
- Expiry chip
- Status chip
- Row delete (revoke) icon button
- Revoke confirm dialog
- Create key dialog
- Empty state
Interactions
- Sortable Created and Last used columns
- Hover row tint; delete icon turns red-tinted on hover
- Revoke opens confirm dialog requiring the key name to be typed
- Create key opens dialog then the one-time reveal sheet
- Theme control switches system/light/dark
Data
ApiKey{id, org_id, name, prefix, scopes[], created_at, last_used_at, expires_at, status (active|expired|revoked), created_by}OrgMember{org_id, user_id, role (owner|admin|member)}AuditEvent{org_id, actor_id, action, target_id, at}
Guardrails
Experience
- Never show full keys in the table; prefix + ellipsis only
- Show 'Never' for unused keys so stale ones stand out
- Make revoke deliberate: typed confirmation
- Keep status as text chips, not dots
- Explain to non-admins why actions are missing
Accessibility
- Table uses real th with scope and aria-sort on sortable columns
- Icon-only delete button has an accessible name including the key name
- Chips contain text; colour is secondary
- Confirm dialog focuses the input and returns focus on close
- Dense 15px text only in table cells
Security
- Only a salted hash and prefix are stored; plaintext never retrievable
- RLS: members select via a view without the hash; owners/admins insert and revoke
- Revoke is an update to status; no hard deletes, for audit
- Audit create/revoke with actor and time
- Rate-limit create and revoke endpoints
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 **Tokenmoor**'s organisation API keys page for a developer workflow platform. Admins list, create and revoke keys; every row shows masked value, scopes, created, last used, expiry and status so stale or risky keys are obvious. ### 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. TanStack Table for sorting; an Edge Function for key creation. ### Pages & layout 1. **Sidebar** (240px, `--bg`): logo + product name; nav Trigger, Runs, Reviews, Monitoring; group 'Agent' (Agents, Shared resources); 'Workflow' (Workflows, Templates); 'Developer' (Webhooks, API keys active, Connect); Settings. Footer: Documentation (external icon), theme segmented control (system/light/dark), collapse button, user avatar. 2. **Top bar**: org avatar + name dropdown, breadcrumb 'API keys'. 3. **/settings/api-keys**: H1 'API keys', muted 'Create and manage organisation keys for CLI, SDK and API access', dark '+ Create API key'. 4. **Table card**: columns Name, Key (`tk_live_ab12...` mono chip), Scopes (`*` or named scopes), Created (`Sep 21, 2026, 1:22 PM`), Last used (`Never`/relative), Expires chip, Status chip, revoke icon. 5. Below 900px: rows become cards with label/value pairs; sidebar in a drawer. ### Design system - Colors: `--bg: #f7f7f7` (app background), `--card: #ffffff` (table card), `--fg: #18181b` (primary text), `--muted: #5e5e66` (muted text), `--border: #8a8a93` (table/input border), `--chip: #f0f0f1` (chip fill), `--primary: #18181b` (dark button / active nav), `--danger: #b42318` (danger icon), `--danger-bg: #fef3f2` (danger icon hover fill), `--success: #15803d` (success status), `--ring: #2563eb` (focus ring). - Fonts: Inter 400/500/600; H1 24px 600; body 16px/1.5; table cells 15px; JetBrains Mono 13px in chips. - Spacing: 4px base; row height 52px; page padding 24px. - Radius: card 10px, buttons 8px, chips 6px. - Shadows: none; card border 1px `--border` at 35% opacity. - Motion: 120ms hover tints; dialog fade/scale 150ms. ### Components & interactions `AppSidebar` (grouped nav, active dark pill), `OrgSwitcher`, `PageHeader`, `KeysTable` (TanStack Table, aria-sort), `KeyChip`, `ScopeChip`, `ExpiryChip`, `StatusChip`, `RevokeButton` (icon, aria-label 'Revoke key Backend service'), `RevokeDialog` (type key name to confirm), `CreateKeyDialog` (name, scopes, expiry: never/30/90/365 days), `EmptyState`, `ThemeControl`. ### Data & state `api_keys(id, org_id, name, prefix, key_hash, scopes text[], created_by, created_at, last_used_at, expires_at, status)`, `org_members(org_id, user_id, role)`, `audit_events`. Expose `api_keys_public` view without `key_hash`. Status derived: expired when `expires_at < now()`. Mock: one active never-used key, one expired, one revoked. ### Accessibility Real table semantics with `aria-sort`; icon buttons labelled with the key name; chips are text. Dialog focus management via Radix. Keyboard: Enter on a row opens key details; Delete key triggers revoke dialog for admins. Focus ring 2px `--ring`. Verified contrast: body text on card: #18181b on #ffffff = 17.72:1; muted text on app bg: #5e5e66 on #f7f7f7 = 6.00:1; chip text on chip fill: #18181b on #f0f0f1 = 15.56:1; button label on dark: #ffffff on #18181b = 17.72:1; danger icon on hover fill: #b42318 on #fef3f2 = 6.05:1; status text on card: #15803d on #ffffff = 5.02:1; table border: #8a8a93 on #ffffff = 3.42:1; focus ring: #2563eb on #ffffff = 5.17:1. ### Security RLS: `api_keys` select for members through the view; insert/update (status only) for owner/admin; no delete policy. `audit_events` insert only via security-definer function, select for admins. Keys generated server-side with a CSPRNG; hash with a per-key salt. Rate-limit create/revoke to 30/hour per org. ### Performance & SEO Server-side pagination beyond 50 keys. Lazy-load dialogs. App routes `noindex`. ### Guardrails - Invent key prefixes, names and org names. - No real vendor names in scopes. - Never render plaintext keys in this table. Acceptance criteria: - [ ] Sorting by Created and Last used works - [ ] Revoke requires typed name and flips status - [ ] Members without admin role cannot create or revoke (RLS) - [ ] Empty state appears with zero keys - [ ] Contrast pairs pass