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
- Fixed left sidebar: small logo mark top, monospace uppercase nav (Music, Videos, Tour, News, Bio), icon links at bottom (streaming, Apple, Instagram, mail, account)
- Masonry/mosaic grid of grayscale photo tiles with varied spans; each tile has title and 'TYPE · DATE' metadata
- Release detail pages with cover, tracklist, metadata and streaming links
- Video grid with 16:9 thumbnails, durations and category labels
- Tour dates list
- News posts with comments
- Bio page
- Newsletter signup modal
- 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
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on background | 18.73:1 | 4.5:1 |
| Aa | text on secondary | 17.18:1 | 4.5:1 |
| Aa | muted nav/meta on white | 5.74:1 | 4.5:1 |
| Aa | muted meta on secondary | 5.27:1 | 4.5:1 |
| Aa | white text on black primary button | 21.00:1 | 4.5:1 |
| Aa | white caption text over black 70% band on white-worst-case photo | 8.45:1 | 4.5:1 |
| comment/newsletter input border on white | 3.28:1 | 3:1 | |
| input border on secondary | 3.01:1 | 3:1 | |
| focus ring on white | 21.00: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
- 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.