Template
Solenne Marsh
A clean, image-first website for an editorial and commercial photographer. A full-viewport hero with widely tracked light-weight name type leads into an about section, a grid of featured projects with hover previews and a lightbox, and a validated contact form. It is designed to help a photographer launch or prove an independent business.
Editorial photographer website · 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
Lexend Exa is drawn wide and has ultralight weights, so the widely spaced photographer's-signature caps come naturally.
- Lexend ExaHeadings: display 128px at 200 uppercase, 0.1em; headings 36px 300
- Lexend ExaBody: UI and body 16px, 400/500
Who it is for
- Photographers launching an independent business
- Editorial and commercial shooters
- Small studios presenting several services
- Creatives who need dark mode and a lightbox
Layout
- Transparent top nav over hero: name left; Home, Portfolio, About, Contact and a theme toggle right; becomes solid white with blur on scroll
- Full-viewport hero: soft gray gradient/photo, huge thin uppercase name with wide tracking, role subtitle, one-line tagline, 'Scroll' cue with chevron
- About My Work: centered heading and biography paragraph
- Featured Projects: two-column grid of large rounded image cards (e.g. landscape, street, architecture, fashion)
- Lightbox overlay for full-screen viewing
- Contact section with validated form
- Footer with social links
Palette
Clean, airy, modern, quiet, professional. It is a white-gallery feel where the photos supply all the color.
- background
#ffffff - text
#09090b - primary
#18181b - muted surface
#f4f4f5 - border
#e4e4e7 - hero overlay gray
#9a9a9a
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on background | 19.90:1 | 4.5:1 |
| Aa | muted text on background | 4.83:1 | 4.5:1 |
| Aa | primary button text on primary | 17.72:1 | 4.5:1 |
| input border on background | 4.83:1 | 3:1 | |
| focus ring on background | 17.72:1 | 3:1 | |
| Aa | white hero text on 45% black overlay over gray backdrop | 7.46:1 | 4.5:1 |
| Aa | white card caption on 60% black scrim | 5.74:1 | 4.5:1 |
| Aa | dark mode body text | 19.06:1 | 4.5:1 |
| Aa | dark mode muted text | 7.76:1 | 4.5:1 |
| Aa | dark mode primary button text | 16.97:1 | 4.5:1 |
| dark mode focus ring on background | 19.06:1 | 3:1 | |
| dark mode input border | 4.12: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
- System sans at weight 200, 128px, uppercase, 0.1em tracking (12.8px) (the system face is not on Google Fonts; a vibld project loads Lexend Exa)
- Body
- System sans 16px regular; section headings 36px weight 300 with slight tracking (the system face is not on Google Fonts; a vibld project loads Lexend Exa)
Ultra-light, widely spaced caps feel like a photographer's signature; a neutral sans keeps attention on photos.
Spacing and imagery
Full-bleed hero, then a centered container with 16px gaps between image cards; generous 96px section padding; cards have small radius (~6px).
Full-color landscape, street, architectural and fashion photography in large 3:2 cards; hero uses a desaturated gray gradient backdrop.
Components
- Scroll-aware transparent-to-solid navbar
- Active nav underline
- Light/dark theme toggle
- Full-screen typographic hero
- Scroll cue with bouncing chevron
- About section
- Featured project grid
- Hover project preview (zoom + caption)
- Lightbox with swipe and keyboard
- Contact form with validation
- Social footer
- Mobile menu sheet
Interactions
- Navbar gains background and blur after scrolling past hero
- Project cards zoom slightly and reveal title/category on hover
- Lightbox opens with fade, supports arrow keys, Esc and swipe
- Theme toggle switches light/dark with persisted preference
- Contact form inline errors and toast on submit
- Scroll cue chevron bounces; smooth scroll to sections
Data
Project{id, title, category, cover, images[], description}Photo{src, alt, width, height}ContactMessage{name, email, shootType, message}
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 photographer website for a fictional editorial and commercial photographer, "Solenne Marsh". Its audience is art buyers, editors and private clients who want to see the work quickly and then make contact. It needs a typographic hero, a short bio, a grid of featured series that open in a full-screen lightbox, and a contact form.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS with `darkMode: 'class'`, shadcn/ui (Radix) for Dialog, Sheet, Form, Toast and Button, and lucide-react. Use Embla Carousel inside the lightbox for swipe, Framer Motion for fades, and react-hook-form + zod for the form. No database is needed; send messages through a serverless function.
### Pages & layout
1. Navbar: the name in spaced caps on the left and links plus a sun/moon toggle on the right. It starts transparent over the hero, then turns white/80 with a backdrop blur and hairline border after 80px of scroll. On mobile, the links collapse into a Sheet.
2. Hero at 100vh: a soft neutral gradient or a muted photo, the name at 96-128px weight 200 (48px/300 minimum on mobile) with 0.1em tracking, a role line, a one-line tagline and "Scroll" with a chevron.
3. About: centered heading and short bio (640px column).
4. Featured Projects: a two-column grid (one column on mobile) of 3:2 cards with 6px radius and a 16px gap. Each card opens that series in the lightbox.
5. Contact: name, email, shoot type (select) and message.
6. Footer: name, social icons, copyright.
### Design system
- Light: `--background: 0 0% 100%`, `--foreground: 240 10% 4%`, `--primary: 240 6% 10%`, `--muted: 240 5% 96%`, `--border: 240 6% 90%` (decorative), `--muted-foreground`/`--input-border: 240 4% 46%`, `--ring: 240 6% 10%`.
- Dark: background 240 10% 4%; foreground, ring and primary 0 0% 98%; muted-foreground 240 5% 65%.
- Fonts: Lexend Exa from Google Fonts, 200 for display and 300 for headings, with 400/500 for UI. Scale: 12 / 14 / 16 / 20 / 36 / 64 / 128px. Body 16px/1.6.
- Spacing: 4px base, 96px sections; radius 6px.
- Motion: 300ms ease-out for fades, 500ms for image zoom, and 1.6s infinite bounce on the chevron.
### Components & interactions
- Navbar: states for transparent, scrolled and mobile-open. The active link gets an underline.
- ProjectCard: on hover, the image scales 1.05 and a caption on a black 60% gradient scrim (effective #666666 or darker) shows the title and category. The same happens on focus. Show a blurred placeholder while loading.
- Lightbox: Dialog plus carousel with previous/next buttons, arrow keys, Esc to close, swipe on touch and a counter such as "3 / 12". Return focus on close.
- ThemeToggle: defaults to system, persists in localStorage.
- ContactForm: states for idle, invalid (inline messages), submitting (disabled button with spinner), success (toast and reset) and error (toast with retry).
### Data & state
Store a typed `projects` array: `{id, title, category, cover, images: {src, alt, w, h}[], description}`. Lightbox state (open series, index) lives in a small context. The theme is kept in context and localStorage. Seed four fictional series with placeholder photos.
### Accessibility
- White hero text sits on a black 45% overlay (effective #555555 over the gray backdrop, 7.46:1).
- The lightbox is a labelled modal dialog that traps focus, and every slide has alt text.
- The toggle has `aria-pressed` and a label. Focus rings are 2px `--ring` with an offset, visible in both themes.
- Disable zoom and bounce under reduced-motion.
- Verified contrast: body #09090b on #ffffff = 19.9:1; muted #71717a on #ffffff = 4.83:1; button #ffffff on #18181b = 17.72:1.
### Security
- zod validation on the client and in the function. Add a honeypot, a rate limit and a max length of 2,000 characters for the message.
- No API keys in client-side code.
- External links use `rel="noopener noreferrer"`.
### Performance & SEO
Use responsive `srcset` with width/height attributes, lazy-loading and blur-up placeholders. Preload only the hero asset. Add Photographer/LocalBusiness JSON-LD, OG images from a cover and descriptive titles.
### Guardrails
- Do write all copy yourself and use placeholder imagery (Unsplash-style stock or generated); no trademarked names, client logos or real people.
- Don't present invented testimonials or awards as real; label sample content as sample.
- Keep components small and fully typed; no `any`. Surface errors visibly instead of failing silently.
- Don't collect or store more personal data than the contact form needs.
- Done when:
- [ ] Navbar transitions from transparent to solid on scroll and works as a mobile sheet
- [ ] Lightbox supports keyboard, swipe and focus return
- [ ] Dark mode persists across reloads
- [ ] Contact form covers invalid, submitting, success and error states
- [ ] Images lazy-load without layout shift