Template
Vesperdeck
An asset library table inside a headless content platform. Content editors switch between saved views of images (banners, product shots, logos), filter by asset type and locale, and scan thumbnails, file names, alt text, dimensions, size and publish status before uploading or opening an entry.
Headless CMS asset table with custom views · 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, 18px view title
- InterBody: Inter 400/500, 15px table cells (dense table), 16px panels; line-height 1.5
Patterns
- icon nav + secondary views sidebar
- saved custom views list
- filter bar with field/operator/value token
- table with image thumbnails
- list/grid view toggle
- mono numeric columns
- status chip column
- environment indicator in top bar
- collapsible views panel
States it is designed for
- Loading skeleton rows with grey thumbnail blocks
- Empty view ('No assets match this view') with edit filters
- Missing alt text flagged with a warning chip
- Upload errors per file (type/size)
- Unsaved view changes prompt on navigation
- Truncated alt text with full text on hover/focus
- Read-only role: Upload and Add entry hidden
Who it is for
- Content editors managing product imagery
- Developers checking asset metadata for the front end
- Localisation teams reviewing alt text per locale
Layout
- Top bar: project badge + name, environment pill with branch icon (tinted green), global search with shortcuts, AI assist button, help, feedback, notifications, avatar
- Primary nav (200px): wordmark, Home, Schema, Content, Assets (active), API playground
- Views panel (160px, collapsible): Schedule (Entries, Releases), Custom views (Banners, Product main images, 360 images, Product action shots active, Videos, Logos), Default views (Asset)
- Header row: model chip, view name, model id in muted mono, overflow; right: list/grid toggle, View menu with unsaved dot, Upload (indigo), Add entry (indigo)
- Filter row: locale select, search 'any item', active filter token 'Asset type is Product action' with clear, filter icon
- Table: preview thumbnail, file name (sortable), asset type, alt text (truncated), width, height (mono), size, status chip; column settings gear
- Below 1200px views panel collapses; below 768px table becomes cards with thumbnail left
Palette
organised, editorial, efficient. A tidy media shelf for content teams.
- app background
#ffffff - table header / panel
#f7f7fc - primary text
#1a1b25 - muted text
#6b6f80 - border
#8a8e99 - accent indigo (buttons, active nav)
#5b4fe0 - active nav fill
#f4f3ff - environment pill
#eaf7f0 - published chip text
#1f7a4d - draft chip text
#8a5a00 - draft chip fill
#fff5db
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on page | 17.11:1 | 4.5:1 |
| Aa | muted text on page | 4.99:1 | 4.5:1 |
| Aa | header text on panel | 16.02:1 | 4.5:1 |
| Aa | button label on indigo | 5.77:1 | 4.5:1 |
| Aa | active nav text | 5.25:1 | 4.5:1 |
| Aa | published chip text | 4.83:1 | 4.5:1 |
| Aa | draft chip text | 5.46:1 | 4.5:1 |
| table border | 3.28:1 | 3:1 | |
| focus ring (indigo) | 5.77: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. 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
- Inter 600, 18px view title
- Body
- Inter 400/500, 15px table cells (dense table), 16px panels; line-height 1.5
Numeric columns (width, height) in JetBrains Mono 14px with tabular figures; similar to observed grotesk + mono.
Spacing and imagery
Dense data UI: 56px rows with 40px thumbnails, 12px cell padding; panels separated by 1px borders; radius 8px buttons, 6px chips, 4px thumbnails; app frame has 12px radius inset from viewport.
Real image thumbnails (use invented placeholder photos) cropped square with 4px radius; line icons elsewhere.
Components
- Environment pill
- Global search with shortcut hints
- Primary nav
- Collapsible views panel with folders
- Model chip + view title
- List/grid toggle
- View menu (columns, sort, save view)
- Upload button
- Add entry button
- Locale select
- Filter token (field is value)
- Asset table with thumbnails
- Status chip
- Column settings
- Upload dialog with drop zone
Interactions
- Clicking a view loads its saved filters/sort/columns; editing shows an unsaved dot on View
- Filter token opens an editor popover; X clears it
- Sort by file name toggles asc/desc
- Row click opens asset detail side panel with larger preview and editable alt text
- Grid toggle switches to thumbnail grid
- Upload supports drag-drop and shows per-file progress
Data
Asset{id, project_id, environment, file_name, mime, width, height, size_bytes, alt_text_by_locale{}, asset_type (product_action|product_main|banner|logo|video|360), status (draft|published|changed), updated_at}SavedView{id, project_id, name, folder (schedule|custom|default), filters[], sort, columns[]}Locale{code, label}
Guardrails
Experience
- Show the active filter as a readable sentence token
- Flag missing alt text right in the table
- Keep numeric columns right-aligned in mono
- Make saved views one click and show when they are modified
- Keep Upload and Add entry distinct: one adds files, the other adds content
Accessibility
- Thumbnails use the asset's alt text; flag empty alt rather than inventing one
- Table header cells sortable via buttons with aria-sort
- Filter token has a labelled remove button
- Status chips have text
- Views panel is a nav landmark with aria-current on the active view
Security
- RLS: assets and views scoped to project members; editors write, viewers read
- Uploads: MIME sniffing, size cap (50MB), image re-encoding to strip metadata, private bucket with signed/CDN URLs
- Validate filters server-side against a field whitelist
- Environment writes restricted by role (e.g. only admins write to the main environment)
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 **Vesperdeck**'s asset library table for a headless content platform. Editors pick a saved view (for example 'Product action shots'), refine with locale and filters, scan thumbnails with alt text and dimensions, and upload or open assets. ### 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 columns/sorting; Supabase Storage for files. ### Pages & layout 1. **Top bar**: project badge + name; environment pill 'main' with branch icon on `--env-bg`; global search with shortcut buttons; 'Assist' button; help, feedback, bell, avatar. 2. **Primary nav** (200px): wordmark, Home, Schema, Content, Assets (active on `--accent-bg`), API playground. 3. **Views panel** (160px, collapse arrow): Schedule (Entries, Releases); Custom views (Banners, Product main images, Product 360 images, Product action shots, Videos, Logos); Default views (Asset). 4. **Assets view**: header with model chip 'Asset', title, muted `#Asset` id, overflow; right: list/grid toggle, View menu, Upload, + Add entry. Filter row: Locale select, 'Search for any item', token 'Asset type is Product action x', filter button. Table: Preview, File name, Asset type, Alt text, Width, Height, Size, Status, gear. 5. **Asset side panel**: large preview, file info, alt text per locale (editable), status, Publish. 6. Responsive: views panel collapses <1200px; card list <768px. ### Design system - Colors: `--bg: #ffffff` (app background), `--panel: #f7f7fc` (table header / panel), `--fg: #1a1b25` (primary text), `--muted: #6b6f80` (muted text), `--border: #8a8e99` (border), `--accent: #5b4fe0` (accent indigo (buttons, active nav)), `--accent-bg: #f4f3ff` (active nav fill), `--env-bg: #eaf7f0` (environment pill), `--success: #1f7a4d` (published chip text), `--draft: #8a5a00` (draft chip text), `--draft-bg: #fff5db` (draft chip fill). - Fonts: Inter 400/500/600; titles 18px 600; body 16px/1.5; table 15px; JetBrains Mono 14px for numbers and ids. - Spacing: 4px base; row 56px; cell padding 12px. - Radius: app frame 12px, buttons 8px, chips 6px, thumbnails 4px. - Shadows: side panel `-8px 0 24px rgb(26 27 37 / 0.1)`; none in table. - Motion: 150ms panel collapse; 200ms side panel slide; upload progress bars animate width. ### Components & interactions `EnvPill`, `GlobalSearch`, `PrimaryNav`, `ViewsPanel` (folders, aria-current), `ViewHeader`, `ViewToggle`, `ViewMenu` (columns, sort, Save view / Save as new), `FilterToken` + `FilterEditor` popover (field, operator, value), `LocaleSelect`, `AssetTable` (thumbnail cell, truncated alt with tooltip, mono numeric cells right-aligned, `StatusChip`), `MissingAltChip`, `UploadDialog` (drop zone, per-file progress and errors), `AssetSidePanel`. ### Data & state `assets(id, project_id, environment, file_path, file_name, mime, width, height, size_bytes, asset_type, status, updated_at)`, `asset_alt_texts(asset_id, locale, text)`, `saved_views(id, project_id, name, folder, filters jsonb, sort jsonb, columns text[])`, `project_members(project_id, user_id, role enum admin|editor|viewer)`. Current view state in URL; unsaved differences compared to the saved view. Seed 10 invented product photos. ### Accessibility Table semantics with `aria-sort`; thumbnails use stored alt text (empty alt flagged visually and in text). Filter token remove button labelled 'Remove filter Asset type'. Views nav uses `aria-current=page`. Upload dialog announces per-file results. Focus ring 2px `--accent`. Verified contrast: body text on page: #1a1b25 on #ffffff = 17.11:1; muted text on page: #6b6f80 on #ffffff = 4.99:1; header text on panel: #1a1b25 on #f7f7fc = 16.02:1; button label on indigo: #ffffff on #5b4fe0 = 5.77:1; active nav text: #5b4fe0 on #f4f3ff = 5.25:1; published chip text: #1f7a4d on #eaf7f0 = 4.83:1; draft chip text: #8a5a00 on #fff5db = 5.46:1; table border: #8a8e99 on #ffffff = 3.28:1; focus ring: #5b4fe0 on #ffffff = 5.77:1. ### Security RLS on every table via `project_members`: select for all roles; insert/update for editor+admin; writes to `environment = 'main'` only for admin. Uploads: server checks MIME by magic bytes, 50MB cap, re-encode images to strip EXIF, private bucket `project_id/` with signed URLs. Saved view filters validated against a field whitelist before building queries. ### Performance & SEO Server-side pagination and sorting; thumbnails via resized 80px WebP renditions; lazy-load the side panel. App routes `noindex`. ### Guardrails - Use invented product names, file names and photos. - Do not reuse any real platform's nav labels verbatim beyond generic terms. - Never auto-generate alt text silently; suggestions must be reviewed. Acceptance criteria: - [ ] Switching views applies saved filters and columns - [ ] Filter token edit and clear work - [ ] Missing alt text is flagged - [ ] Viewer role cannot upload (RLS + UI) - [ ] Contrast pairs pass