Skip to main content
vibld

Template

Saga Lindqvist

A spare, gallery-like portfolio for a fashion and product photographer. A large name heads the page, category tiles (luxury fashion, premium product, editorial, lifestyle) lead to galleries, and an about section with contact and newsletter links sits beside a long bio.

Fashion photographer portfolio · Website · Portfolio · static site

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

Bricolage Grotesque at regular weight has the contemporary character of Cabinet Grotesk, and Kumbh Sans stands in for Satoshi, quiet and Nordic.

  • Bricolage GrotesqueHeadings: name ~123px regular, link list 48px
  • Kumbh SansBody: body 16-20px

Who it is for

  • Fashion and product photographers
  • Photographers pitching editorial and brand commissions
  • Campaign photographers
  • Creatives moving to digital-first marketing

Layout

  1. Large centered name as masthead (no conventional nav bar)
  2. Row of square category tiles with photo and white caption (Luxury Fashion, Premium Product, Editorial, Lifestyle)
  3. About block: bulleted link list (About, Contact, Newsletter) in large type left, long bio paragraph right
  4. Gallery pages per category with fullscreen viewer
  5. Contact and newsletter pages

Palette

Quiet, contemporary, Nordic, restrained, premium. It feels like a clean exhibition wall.

  • background#ffffff
  • text#000000
  • muted text (captions, meta)#5e5e5e
  • accent orange (active link, focus)#ce3a00
  • muted surface#f5f5f5
  • border#000000
  • caption on photo (over 60% black scrim)#ffffff

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on white21.00:14.5:1
Aatext on muted surface19.26:14.5:1
Aamuted captions on white6.48:14.5:1
Aamuted text on muted surface5.95:14.5:1
Aaactive link (orange) on white4.97:14.5:1
orange focus outline on white4.97:13:1
orange focus outline on black lightbox4.23:13:1
Aalightbox caption on black21.00:14.5:1
Aatile caption over 60% black scrim on white-worst-case photo5.74:14.5:1
input border 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
Cabinet Grotesk regular at ~123px for the name; 48px for link list (with a system sans fallback) (Cabinet Grotesk is not on Google Fonts; a vibld project loads Bricolage Grotesque)
Body
Satoshi 16-20px (Satoshi is not on Google Fonts; a vibld project loads Kumbh Sans)

Contemporary grotesk pair; large regular-weight type rather than bold keeps it quiet

Spacing and imagery

32px outer margin, tiles ~380px square with 24px gaps, zero radius, two-column about block with wide left column indent

Saturated fashion and still-life product photography in square crops with white captions overlaid bottom-left on a dark gradient scrim

Components

  • Oversized name masthead
  • Category photo tiles with captions
  • Large-type link list
  • Bio text block
  • Gallery grid
  • Fullscreen lightbox viewer
  • Contact details/form
  • Newsletter signup
  • Toasts
  • Mobile drawer

Interactions

  • Tile hover zoom or darken
  • Lightbox with swipe and keyboard arrows
  • Lazy-loaded images with fade-in
  • Newsletter submit toast
  • Smooth page transitions
  • Drawer menu on mobile

Data

  • Category{slug, title, cover}
  • Photo{src, alt, width, height, category, caption, client?}
  • Profile{name, city, bio, email}
  • Subscriber{email}

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 minimal portfolio for a fictional photographer called **Saga Lindqvist**, who shoots fashion portraits and still-life product work. The audience is art directors and brand managers commissioning campaigns. The site should feel like a white gallery wall: an oversized name, a row of category tiles leading to galleries, a fullscreen viewer, a bio, and ways to get in touch or subscribe.

### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix Dialog), lucide-react, React Router, Embla Carousel for the lightbox slider, Sonner for toasts, Vaul for the mobile drawer, and react-hook-form + zod for the newsletter and contact forms. Newsletter and contact post to one serverless function each.

### Pages & layout
1. Masthead: the photographer's name in very large regular-weight type, centered, acting as the home link.
2. Category row: 4 square tiles (Fashion, Product, Editorial, Lifestyle) with the photo filling the tile and a white caption bottom-left on a 60% black gradient scrim; wraps to 2 columns on tablet and 1 on mobile.
3. About block: a large-type link list (About, Contact, Newsletter) on the left and a long bio paragraph on the right.
4. Category page: title and a justified or masonry grid of photos with a client and year caption on hover or focus; clicking opens a fullscreen viewer.
5. Lightbox: black background, image centered, caption, counter, close, and previous/next.
6. Contact: email, studio city, representation agent, short form. Newsletter: single field.

### Design system
Tokens: `--background: #ffffff; --foreground: #000000; --accent: #ce3a00; --muted: #f5f5f5; --border: #000000; --muted-foreground: #5e5e5e; --radius: 0`. Fonts: Cabinet Grotesk (display, from Fontshare) or a Google fallback like Kumbh Sans, plus Satoshi or Kumbh Sans for body. Scale: clamp(56px, 9.6vw, 124px) name; 48px link list; 20 / 16 / 13px (16–20px body at line-height 1.6, max 70ch; 13px only for captions at weight 400+ in #5e5e5e). Spacing: 32px outer margin, 24px grid gaps, 120px section gaps. No shadows. Accent orange is used only for focus rings and the active link. Motion: 400ms image fade-in; tile hover scale 1.03; lightbox 200ms fade.
- Typeface: Bricolage Grotesque (Google Fonts) for name ~123px regular, link list 48px.

### Components & interactions
- Tile: hover and focus darken the image slightly and move the caption up; the link has a clear accessible name.
- Lightbox: arrow keys, swipe, Esc to close, focus returns to the thumbnail; preload the adjacent images.
- Link list: use a real `<nav>` list without default bullets; active item in accent.
- Forms: labelled inputs, inline errors, loading, success toast, error toast.
- Loading skeleton blocks with fixed aspect ratios; a broken-image fallback.

### Data & state
`Category{slug,title,cover}`, `Photo{src,alt,w,h,category,caption,client?,year}`, `Profile{name,city,bio,email,agent?}`. Typed static data; lightbox index lives in the URL (`?photo=12`) so images are shareable.

### Accessibility
Write real alt text for every photograph (subject, setting, mood). White captions on photos sit on a bottom gradient scrim reaching 60% black, which keeps them at 5.7:1 over the brightest image. The orange accent is #ce3a00 (deepened from a lighter orange that was only 2.9:1 on white), which gives 4.9:1 on white and 4.2:1 on the black lightbox, so it works for the active link text and for 3px focus outlines. Lightbox is a labelled dialog. Honor reduced motion. Ensure the large name reflows without horizontal scrolling at 320px. Verified contrast: body text on white: #000000 on #ffffff = 21.0:1; muted captions on white: #5e5e5e on #ffffff = 6.48:1; active link: #ce3a00 on #ffffff = 4.97:1; tile caption over 60% black scrim on white-worst-case photo: #ffffff on #666666 = 5.74:1.

### Security
Validate forms with zod on the client and in functions; add a honeypot and rate limit; use double opt-in for the newsletter; keep provider keys server-side. Strip EXIF location data from published images. External links use `rel="noopener noreferrer"`.

### Performance & SEO
Photography sites live or die by images: generate AVIF/WebP at multiple widths, use `srcset`/`sizes`, lazy-load below the fold, and reserve dimensions. Code-split category and lightbox routes. ImageGallery and Person JSON-LD, per-category OG images. Target LCP under 2.5s on mobile with the name as the LCP element.

### Guardrails
- Use placeholder or licensed stock imagery; don't show real brand products or logos as your clients' work.
- Don't list real luxury houses as clients.
- Store only email for the newsletter and contact form fields.
- Keep components small and typed, never use `any`, and handle failed image loads visibly.
- Done when: category tiles, galleries and lightbox work with keyboard and touch; custom fonts load with a sensible fallback (no default list bullets or unstyled nav); every image has alt text; forms handle success and error; CLS stays under 0.05.

Open the builderAll templatesThis palette on its own