Skip to main content
vibld

Template

Grain Theory

A gallery-style artist site for an electronic musician that uses a dense masonry grid of black-and-white portraits as its home page. Each tile is a dated release or video entry; a fixed left sidebar links to music, videos, tour, news and bio, with streaming icons, a newsletter modal and user accounts for commenting.

Mosaic electronic artist site · Website · Portfolio · full-stack app (auth + DB)

A mock-up of the homepage, drawn from this design’s layout, palette and typefaces. A build follows the full prompt below.

Start from this templateRead the build prompt

Typefaces

Courier Prime is a typewriter mono, so the page reads like a typed archive index next to the black-and-white contact sheets.

  • Courier PrimeHeadings: titles and metadata, uppercase
  • Courier PrimeBody: body 16px throughout

Who it is for

  • Electronic musicians with strong visual identity
  • Photographers and visual artists wanting a dark editorial portfolio
  • Agencies delivering artist sites
  • Fans who want to follow and comment on releases

Layout

  1. Fixed left sidebar: small logo mark top, monospace uppercase nav (Music, Videos, Tour, News, Bio), icon links at bottom (streaming, Apple, Instagram, mail, account)
  2. Masonry/mosaic grid of grayscale photo tiles with varied spans; each tile has title and 'TYPE · DATE' metadata
  3. Release detail pages with cover, tracklist, metadata and streaming links
  4. Video grid with 16:9 thumbnails, durations and category labels
  5. Tour dates list
  6. News posts with comments
  7. Bio page
  8. Newsletter signup modal
  9. Auth modal for comments

Palette

Editorial, stark, archival, moody, gallery-like. Black-and-white portraits in a tight mosaic feel like a contact sheet.

  • background#ffffff
  • text#121212
  • muted/nav text#666666
  • primary#000000
  • secondary#f5f5f5
  • decorative border#e6e6e6
  • input border#8e8e8e
  • caption overlay (black 70%)#000000b3

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on background18.73:14.5:1
Aatext on secondary17.18:14.5:1
Aamuted nav/meta on white5.74:14.5:1
Aamuted meta on secondary5.27:14.5:1
Aawhite text on black primary button21.00:14.5:1
Aawhite caption text over black 70% band on white-worst-case photo8.45:14.5:1
comment/newsletter input border on white3.28:13:1
input border on secondary3.01:13:1
focus ring on white21.00: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
Courier Prime uppercase for titles and metadata; no large display headline
Body
Courier Prime 16px throughout

All-monospace system reads like an archive index and lets photos dominate

Spacing and imagery

Dense: 12px gutters between tiles, ~110px sidebar, square corners, grid fills viewport width

Monochrome portrait photography with motion blur and grain; grayscale-to-color on hover in listings

Components

  • Fixed sidebar navigation
  • Streaming/social icon stack
  • Masonry media grid
  • Tile with title + type/date meta
  • Release detail page
  • Tracklist
  • Video thumbnail grid with duration badge
  • Tour list
  • News article with comments thread
  • Newsletter modal
  • Auth dialog (sign in/up)
  • Mobile top bar with drawer

Interactions

  • Grayscale-to-color transition on hover
  • Tile hover reveals title overlay
  • Newsletter modal opens after delay or on click
  • Sign in to comment; optimistic comment post
  • Delete own comment
  • Sidebar collapses to drawer on mobile
  • Lazy-load tiles as you scroll

Data

  • Entry{id, slug, title, type: music|video|news, date, image, span}
  • Release{slug, title, cover, date, tracks[], links[]}
  • Video{title, thumbnail, duration, category, embedId}
  • TourDate{date, city, venue, ticketUrl}
  • Comment{id, entry_id, user_id, body, created_at}
  • Profile{id, display_name}
  • Subscriber{email, created_at}

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 a template'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 templates)
- 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.
### Goal
Build a gallery-style artist website for a fictional electronic producer called **Grain Theory**. The audience is fans, curators and press who respond to visual identity. The home page is a dense mosaic of monochrome portraits, where every tile is a dated release, video or news entry. Signed-in fans can comment on news and releases, and anyone can join a newsletter.

### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix Dialog, Sheet, Tabs, Toast), lucide-react, React Router, TanStack Query, react-hook-form + zod, date-fns, and Framer Motion. Use CSS grid with `grid-auto-flow: dense` (or a masonry library) for the mosaic. Supabase provides Auth (email magic link), Postgres for comments, profiles and subscribers, and Storage if editors upload images.

### Pages & layout
1. Fixed left sidebar (~110px): a small logo mark, monospace uppercase links (Music, Videos, Tour, News, Bio), and an icon stack at the bottom (streaming, Instagram, mail, account). On mobile this becomes a top bar with a drawer.
2. Home mosaic: tiles of varying spans (1×1, 2×1, 2×2) with 12px gaps; each has a caption with the title and `TYPE · MON DD, YYYY`.
3. Music: release grid; the detail page has the cover, tracklist, credits and platform links.
4. Videos: 16:9 thumbnails with duration badges and category tabs (live, studio, music video).
5. Tour: date, city, venue and ticket link.
6. News: posts with a comments thread.
7. Bio: portrait and text.
8. Newsletter modal and an auth dialog.

### Design system
Tokens: `--background: #ffffff; --foreground: #121212; --muted-foreground: #666666; --primary: #000000; --secondary: #f5f5f5; --border: #e6e6e6 (decorative); --input-border: #8e8e8e; --radius: 0`. Font: Courier Prime 400/500 for everything. Scale: 20 / 16 / 13 / 12px (nothing below 12px; 12–13px only for tile meta at weight 400+; body line-height 1.6, max 70ch); nav 16px uppercase with 0.05em tracking. Spacing: 12px grid gutter, 32px sidebar padding. No shadows and square corners. Images default to `grayscale(100%)` and transition to color over 400ms on hover or focus. Light theme only, so the photographs provide the darkness.

### Components & interactions
- Tile: hover and focus remove grayscale and show a caption overlay (white text on a black 70% band); skeleton blocks while loading; broken images show a neutral block with the title.
- Comments: list with author and relative time; the composer appears only when signed in; optimistic insert with rollback on error; users can delete their own comments with confirmation; empty state "No comments yet".
- Auth dialog: magic-link email, loading state, a "check your inbox" state, and error messages.
- Newsletter modal: opens from the mail icon and never auto-opens more than once per visit; success and error toasts.
- Video tabs filter by category and are reflected in the URL.

### Data & state
Static content: `Entry{slug,title,type,date,image,span}`, `Release`, `Video`, `TourDate`. Supabase tables: `profiles(id pk → auth.users, display_name)`, `comments(id, entry_slug, user_id, body, created_at)`, `subscribers(email unique, created_at)`.

### Accessibility
#666666 passes for nav and meta text on white and #f5f5f5; inputs use a #8e8e8e border; captions sit on a black 70% band. Captions must not rely on hover alone; show them on focus and always on touch. Use a real `<nav>` landmark for the sidebar and `<main>` for the grid. Give every portrait descriptive alt text. Dialogs trap focus and return it. The comment textarea needs a label and a character count announced politely. Disable color transitions under reduced motion. Verified contrast: muted nav/meta on white: #666666 on #ffffff = 5.74:1; white caption text over black 70% band on white-worst-case photo: #ffffff on #4d4d4d = 8.45:1; comment/newsletter input border on white: #8e8e8e on #ffffff = 3.28:1.

### Security
RLS: `comments` select for everyone; insert only when `auth.uid() = user_id`; delete only by the owner. `profiles` are readable publicly and updatable only by the owner. `subscribers` allow anonymous insert only, with no select. Validate comment bodies (1–1000 chars) with zod on the client and in a Postgres check constraint; render as plain text, never HTML. Rate-limit comment inserts per user with an edge function or trigger. Ship only the anon key.

### Performance & SEO
The mosaic is image-heavy: serve responsive AVIF with width hints, lazy-load below the first screen, and reserve aspect ratios to avoid layout shift. Per-release meta and OG images, plus MusicRecording JSON-LD.

### Guardrails
- Use placeholder or generated portraits and original copy; no real artist names or logos.
- Don't seed comments that pretend to be real fans; label demo comments.
- Store only email and display name.
- Keep components small and typed, never use `any`, and handle errors visibly.
- Done when: the mosaic has no gaps at three breakpoints; signed-out users cannot post comments (verified via RLS); users can delete only their own comments; the newsletter rejects duplicates; keyboard users can reach every tile and see its caption.

Open the builderAll templatesThis palette on its own