Skip to main content
vibld

Template

Ursalane

A browse page for a public catalogue of machine-learning datasets. People narrow over a million entries by modality, size, format and type in a left facet panel, then scan compact cards showing owner/name, viewer availability, update time, size, downloads and likes.

Faceted dataset browser with card grid · App screen: table · Small tools and apps · front-end app (local state)

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.

  • Source Sans 3Headings: Source Sans 3 700, 20px section title; count 18px 400 muted
  • JetBrains MonoBody: Source Sans 3 400, 16px/1.5; card title in JetBrains Mono 15px 500; meta 14px

Patterns

  • left facet panel with tabs
  • chip filter groups
  • range slider facet
  • two-column result card grid
  • result count beside title
  • inline filter-by-name input
  • sort dropdown
  • stat row with icons per card

States it is designed for

  • Loading skeleton cards
  • No results with suggestion to remove last filter
  • Very long owner/name truncates with full title in tooltip
  • Error with retry
  • Gated dataset badge on card
  • Count abbreviations (1.2M, 853k) with exact value in title attr

Who it is for

  • ML researchers looking for training or evaluation data
  • Engineers comparing dataset formats and sizes
  • Students exploring public corpora

Layout

  1. Public header: logo, global search, nav links, avatar
  2. Left facet panel (~300px, subtle grey): tabs (Main, Tasks, Libraries, Languages, Licenses, Other), Modalities chip group with coloured icons, Size range slider (<1K to >1T), Format chips, Type chips with tinted fills
  3. Main: title 'Datasets' + big muted count, filter-by-name input, Full-text search button, Sort dropdown
  4. Two-column grid of bordered cards: icon + owner/name (mono-ish), meta row (viewer, updated, rows, downloads, likes)
  5. Pagination at bottom
  6. Below 1024px: facets move into a 'Filters' sheet; grid becomes one column

Palette

open, technical, browsable. A library catalogue for data, not a store.

  • page#ffffff
  • facet panel#fafbfc
  • primary text#111827
  • muted text#5b6472
  • card border#8b8f97
  • chip border#8b8f97
  • benchmark chip#fff7e6
  • benchmark chip text#b45309
  • traces chip#eef4ff
  • traces chip text#2563eb
  • env chip#e8f7ef
  • env chip text#047857

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on page17.74:14.5:1
Aamuted meta on page5.98:14.5:1
Aamuted on facet panel5.77:14.5:1
Aabenchmark chip text4.71:14.5:1
Aatraces chip text4.68:14.5:1
Aaenvironment chip text4.96:14.5:1
card border3.24:13:1
chip border on panel3.13:13:1
focus ring (blue) on page5.17: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
Source Sans 3 700, 20px section title; count 18px 400 muted
Body
Source Sans 3 400, 16px/1.5; card title in JetBrains Mono 15px 500; meta 14px

Humanist sans + mono for identifiers, similar to observed; facet group headings 14px 600.

Spacing and imagery

Dense but readable: cards 72px tall with 12px padding, 16px grid gap; chips 28px tall radius 8px; facet groups spaced 24px; container 1440px.

Small coloured square icons per modality chip; tiny line icons in card meta; no photos.

Components

  • Facet tabs
  • Modality chip group (toggle)
  • Size range slider with labels
  • Format chips
  • Type chips (tinted)
  • Result count
  • Filter-by-name input
  • Full-text search toggle
  • Sort dropdown (Trending, Most downloads, Most likes, Recently updated)
  • Dataset card
  • Pagination
  • Active filters bar with clear-all

Interactions

  • Toggling a chip updates results and URL query immediately; pressed chips show filled state + check
  • Range slider has two thumbs with keyboard steps
  • Filter-by-name debounced 200ms
  • Sort change keeps filters
  • Card hover raises border colour; whole card is a link

Data

  • Dataset{id, owner, name, modalities[], size_bucket, formats[], type (benchmark|traces|environment|other), updated_at, rows, downloads, likes, viewer_available, gated}
  • FacetCount{facet, value, count}

Guardrails

Experience

  • Keep facet state in the URL so results are shareable
  • Show the total count and update it as filters change
  • Pressed chips must look pressed beyond colour (check icon/fill)
  • Abbreviate big numbers but expose exact values
  • Offer one-click 'Clear all' when any filter is active

Accessibility

  • Chips are toggle buttons with aria-pressed
  • Slider thumbs have aria-valuetext like 'up to 1 million rows'
  • Result count lives in an aria-live region
  • Cards are single links with owner/name + stats as description
  • Facet panel sheet on mobile traps focus and returns it

Security

  • Sanitise query params before using in API calls; whitelist facet values
  • Escape user-supplied dataset names when rendering
  • Gated datasets never expose download URLs in listing responses
  • Rate-limit search API

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 **Ursalane**, a browse page for a public catalogue of about a million ML datasets. A left facet panel narrows by modality, size, format and type; a two-column grid of compact cards shows owner/name and key stats. Everything is URL-driven so a filtered view can be shared.

### Stack
Next.js (App Router) + React 18 + TypeScript, Tailwind CSS, shadcn/ui (Radix Slider, ToggleGroup, Select, Tabs), lucide-react, TanStack Query for client refetches, date-fns for relative times. Mock data from a local JSON of ~2,000 invented datasets with a search function; swap to an API later.

### Pages & layout
1. **Header**: logo, global search, nav (Models, Datasets active, Demos, Docs, Pricing), avatar.
2. **/datasets** desktop grid `300px 1fr`:
   - Facet panel: tabs Main/Tasks/Libraries/Languages/Licenses/Other; Modalities (3D, Audio, Document, Geospatial, Image, Tabular, Text, Time-series, Video) as chips with small coloured squares; Size (rows) dual slider <1K to >1T; Format chips (json, csv, parquet, imagefolder, audiofolder, webdataset, text, arrow-style columnar); Type chips (Benchmark amber, Traces blue, Environment green).
   - Main: 'Datasets' + muted count '1,056,961'; filter-by-name input; 'Full-text search' toggle; Sort select. Two-column card grid; pagination.
3. Active filter bar above the grid with removable chips and Clear all.
4. Mobile: 'Filters (3)' button opens a sheet; one-column cards.

### Design system
- Colors: `--bg: #ffffff` (page), `--panel: #fafbfc` (facet panel), `--fg: #111827` (primary text), `--muted: #5b6472` (muted text), `--border: #8b8f97` (card border), `--chip-border: #8b8f97` (chip border), `--amber-bg: #fff7e6` (benchmark chip), `--amber: #b45309` (benchmark chip text), `--blue-bg: #eef4ff` (traces chip), `--blue: #2563eb` (traces chip text), `--green-bg: #e8f7ef` (env chip), `--green: #047857` (env chip text).
- Fonts: Source Sans 3 400/600/700; JetBrains Mono 500 15px for owner/name; meta 14px; body 16px/1.5.
- Spacing: 4px base; grid gap 16px; card padding 12px/16px.
- Radius: cards 10px, chips 8px, inputs 8px.
- Shadows: none; cards `1px solid` border at 40% `--border`, full strength on hover.
- Motion: 120ms border; results fade 150ms on change; none under reduced motion.

### Components & interactions
`FacetTabs`, `ChipGroup` (ToggleGroup type=multiple, check icon when pressed), `RangeFacet` (Radix Slider, log scale buckets), `ActiveFilters`, `ResultHeader` (count aria-live), `NameFilter`, `SortSelect`, `DatasetCard` (icon, owner/name, meta row: viewer, updated relative, rows, downloads, likes; gated badge), `Pagination`, `FiltersSheet`.

### Data & state
`Dataset{id, owner, name, modalities[], size_bucket, formats[], type, updated_at, rows, downloads, likes, viewer_available, gated}`. Filtering and sorting in a pure function over the mock array (memoised); facet counts computed alongside. URL is the source of truth (`?modality=text&format=parquet&size=1k-1m&sort=trending`). Numbers formatted with Intl compact notation.

### Accessibility
Chips `aria-pressed`, groups labelled by their heading. Slider `aria-valuetext`. Count updates announced politely. Cards are links; stats have visually hidden labels ('downloads 853 thousand'). Tinted type chips all meet 4.5:1 text. Focus ring 2px blue.
Verified contrast: body text on page: #111827 on #ffffff = 17.74:1; muted meta on page: #5b6472 on #ffffff = 5.98:1; muted on facet panel: #5b6472 on #fafbfc = 5.77:1; benchmark chip text: #b45309 on #fff7e6 = 4.71:1; traces chip text: #2563eb on #eef4ff = 4.68:1; environment chip text: #047857 on #e8f7ef = 4.96:1; card border: #8b8f97 on #ffffff = 3.24:1; chip border on panel: #8b8f97 on #fafbfc = 3.13:1; focus ring: #2563eb on #ffffff = 5.17:1.

### Security
Whitelist facet params; ignore unknown values. Render names as text (no HTML). When a real API arrives: rate-limit search, never include download links for gated datasets in listings.

### Performance & SEO
Server-render the first page for SEO with canonical URLs per common facet; client refetch afterwards. Virtualise nothing (paginate 30 per page). Title and meta description reflect active filters.

### Guardrails
- Invent every owner and dataset name.
- Don't reproduce any real catalogue's logo or icons.
- Keep chip colours for type only; modalities use neutral chips with small icons.

Acceptance criteria:
- [ ] Filters, sort and page persist in the URL
- [ ] Count and results update on each change
- [ ] Chips and slider are keyboard operable
- [ ] Empty-result state offers removing the last filter
- [ ] Contrast pairs pass

Open the builderAll templatesThis palette on its own