Template
Marisol Duarte
An elegant, warm portfolio for a food photographer. A full-bleed overhead food photo sits under a translucent header and a centered serif headline, followed by an asymmetric editorial grid of dishes with ingredient-style captions and lighting notes, an infinite carousel and a validated contact form. It is built for food photographers who shoot for restaurants, brands and editors.
Editorial food 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.
Add app screens
Pick up to 6 screens, such as a dashboard, settings or an empty state. Each is built in this design’s own palette and typefaces, with its states and guardrails.
Start from this templateRead the build prompt
Typefaces
Bodoni Moda is the high-contrast display serif the design wants in place of Boska, Cormorant's italic suits the recipe-card notes, and DM Sans stays as the friendly body.
- Bodoni ModaHeadings: hero 72px 400, section heads 48px 500 at -1.2px
- DM SansBody: body 16px, 14px ingredient captions
- CormorantAccent: italic serif lighting notes at 16px
Who it is for
- Food and still-life photographers
- Photographers pitching restaurants and food brands
- Creatives who tell stories with each image
- Editorial food stylists
Layout
- Translucent sticky header: serif name left; Home (active), Work, About, Styleguide, Contact right
- Full-bleed hero photo with soft overlay: tracked uppercase eyebrow, two-line light serif headline, one-line subtitle, thin vertical scroll line
- Intro statement
- Asymmetric 12-column editorial grid: offset images of different sizes, each with a sentence-case ingredient caption and an italic lighting note
- Full-width image breakouts
- Infinite carousel of work
- About with portrait
- Contact: 'Let's create something beautiful' with validated form
- Footer; living Styleguide page
Palette
Warm, appetizing, editorial, elegant, calm. It reads like a food magazine spread with natural light and generous margins.
- background (warm off-white)
#faf8f5 - text
#2b2b2b - accent (terracotta)
#9f563c - secondary surface
#efebe7 - muted
#e9e6e2 - border
#dedad3 - caption text
#5f5a55 - input border
#9a8e79
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on off-white | 13.36:1 | 4.5:1 |
| Aa | caption text on off-white | 6.43:1 | 4.5:1 |
| Aa | caption text on secondary surface | 5.75:1 | 4.5:1 |
| Aa | accent links on off-white | 5.12:1 | 4.5:1 |
| Aa | accent on secondary surface | 4.57:1 | 4.5:1 |
| Aa | white text on accent button | 5.43:1 | 4.5:1 |
| Aa | white hero text on 65% overlay (worst case) | 5.40:1 | 4.5:1 |
| Aa | header text on white/80 over darkest photo | 8.82:1 | 4.5:1 |
| input border on off-white | 3.04:1 | 3:1 | |
| Aa | dark mode body text | 15.02:1 | 4.5:1 |
| Aa | dark mode accent text | 4.86:1 | 4.5:1 |
As vibld’s tokens
The palette on the 15+ color tokens vibld styles a project with, each text color 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
- Boska serif 400, 72px hero (light, airy), 48px section headings at 500 with -1.2px tracking (Boska is not on Google Fonts; a vibld project loads Bodoni Moda)
- Body
- DM Sans 16px at 1.6 line-height; 14px sentence-case ingredient captions in #5f5a55; italic serif at 16px for lighting notes
High-contrast display serif with a friendly geometric sans; captions mimic a menu or recipe card.
Spacing and imagery
1120px container with 80px gutters, intentionally offset columns, big vertical gaps (~200px) between image rows, small 4px radius.
Bright overhead and 3/4-angle food photography (salads, pizza, burgers, pastries) on textured surfaces; hero washed with a dark warm overlay for legible text.
Components
- Translucent sticky header
- Full-bleed hero with overlay
- Tracked eyebrow label
- Serif headline
- Scroll line indicator
- Asymmetric editorial grid
- Ingredient caption + italic lighting note
- Full-width breakout image
- Infinite carousel
- About portrait section
- Validated contact form
- Styleguide page
- Mobile drawer nav
Interactions
- Images fade and rise into view on scroll with slight offsets
- Hover gently scales images and darkens caption
- Infinite carousel with drag and pause on hover
- Header gains a white/80 blurred background on scroll
- Contact form validation with inline errors and toast
- Mobile drawer navigation
Data
Shot{id, src, alt, ingredients, lighting, client?, layoutSpan}Series{slug, title, shots[]}Profile{name, city, bio, portrait}Inquiry{name, email, company, projectType, budget, 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 color 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, labeled 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 warm, editorial portfolio for a fictional food photographer, "Marisol Duarte". It targets restaurant owners, food brands and magazine editors. Each image should feel like a story: captions read like menu lines (the ingredients) and short italic notes describe the light. The site opens on a full-bleed hero, moves through an intentionally uneven grid, and ends with a friendly inquiry form.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix Form, Select, Toast via Sonner) and lucide-react. Use Framer Motion for scroll reveals, Embla Carousel with loop and drag for the infinite strip, Vaul for the mobile drawer, react-hook-form + zod and React Router for Home, Work, About, Styleguide and Contact. One serverless function relays inquiries.
### Pages & layout
1. Header, 80px, sticky: white/80 with `backdrop-blur`. A serif name on the left and five links on the right; the active link is darker.
2. Hero at 100vh: a photo with a 65% #1c1a18 overlay (effective #6b6a69 or darker behind white text), an eyebrow "FOOD PHOTOGRAPHY" in 14px with 0.3em tracking, a two-line headline at clamp(40px, 6vw, 72px) in light serif, a subtitle and a 64px vertical hairline scroll cue.
3. Intro: a centered 2-sentence statement in serif.
4. Editorial grid: 12 columns. Rows pair images with different spans and offsets (for example 7 plus 5 columns, then 4 columns starting at column 2 plus 6 columns starting at column 7, dropped 64px). Below each image: a 14px sentence-case ingredients line and an italic lighting note.
5. Breakout: one full-bleed image between grid rows.
6. Carousel: a looping strip of 6-10 square thumbnails.
7. Contact: a serif heading, the form and details. Footer with socials.
### Design system
- `--background: #faf8f5`, `--foreground: #2b2b2b`, `--accent: #9f563c`, `--secondary: #efebe7`, `--muted: #e9e6e2`, `--border: #dedad3` (decorative), `--muted-foreground: #5f5a55`, `--input-border: #9a8e79`, `--radius: 4px`.
- Dark tokens: background #1c1a18, foreground #f2eee9, accent #c07459.
- Fonts: Boska (Fontshare), or Bodoni Moda or Cormorant from Google Fonts, for display. DM Sans for body. Scale: 12 / 14 / 16 / 20 / 32 / 48 / 72px.
- Spacing: 8px base, 80px gutters, 160-200px between rows.
- Motion: 700ms cubic-bezier(0.22,1,0.36,1) fade-up with a 20px offset and 400ms image hover scale (1.03).
### Components & interactions
- Shot: skeleton (warm gray), then fade in. Hover or focus scales the image. The caption is always visible.
- Carousel: drag, arrows and a pause button; it pauses on hover and focus.
- InquiryForm: name, email, company, project type select, budget select and message. States for invalid, submitting, success and error.
- Styleguide page: swatches, type ramp and buttons in all states.
### Data & state
`Shot{id, src, alt, ingredients, lighting, client?, span}` and `Series{slug, title, shots}` are typed. The grid layout comes from the `span` and `offset` fields. Seed 10-14 placeholder food photos with invented dish descriptions and no real restaurant names.
### Accessibility
- The terracotta accent is deepened to #9f563c (5.12:1 on off-white, 4.57:1 on the secondary surface) so it works for links and small text; dark mode keeps #c07459 (4.86:1). Captions use #5f5a55.
- The 65% hero overlay keeps the white eyebrow, headline and subtitle at 5.4:1 even over a white plate.
- Asymmetric layout must not scramble reading order. Keep DOM order row by row.
- Alt text should describe the dish, not the ingredients list verbatim. The carousel is pausable and honors reduced motion.
- Verified contrast: body #2b2b2b on #faf8f5 = 13.36:1; captions #5f5a55 on #faf8f5 = 6.43:1; accent #9f563c on #faf8f5 = 5.12:1; hero text #ffffff on #6b6a69 = 5.4:1.
### Security
- zod on the client and in the function, with a honeypot and rate limit.
- Store only what the inquiry needs; don't collect phone numbers unless required.
- No secrets in client-side code. External links use `rel="noopener noreferrer"`.
### Performance & SEO
The hero image is the LCP, so preload it with AVIF `srcset`. Lazy-load the grid. Add Photographer/LocalBusiness JSON-LD and OG images per series.
### 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:
- [ ] Asymmetric grid renders intentionally offset rows and collapses cleanly on mobile
- [ ] Every image has ingredient caption and lighting note
- [ ] Carousel loops, drags, and pauses
- [ ] Inquiry form covers all states
- [ ] Accent color used only where contrast allows