Skip to main content
vibld

Template

Dani Okafor

A bold portrait photographer's portfolio whose home page is an accordion-style carousel of tall rounded portrait panels: one expanded with a caption, the rest as narrow slivers. Navigation leads to themed collections (portraits, candid life, people and passions) and an about page, with contact details always visible.

Portrait 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

The warm, friendly single geometric family is the design, and Poppins stays in only a few templates.

  • PoppinsHeadings: name ~38px bold at -0.96px
  • PoppinsBody: nav and contact 16-18px

Who it is for

  • Portrait photographers with a distinct voice
  • Photographers seeking brand collaborations
  • Artists submitting thematic collections
  • Clients booking portrait sessions

Layout

  1. Header: bold name top-left, collection links below (Portraits, Everyday, People & Craft, About); email and phone right-aligned
  2. Horizontal row of tall rounded portrait panels; the active one expands wide with a caption (name - role - year), others compressed into slivers
  3. Collection pages with gallery layouts and lightbox
  4. About page

Palette

Warm, human, vibrant, friendly, dynamic. It celebrates faces and personalities.

  • background#fcfaf8
  • text#2e251f
  • primary#3d3129
  • accent terracotta (fills, large text)#dd713c
  • terracotta text / links#b2501f
  • secondary#f2f0ed
  • decorative border#e5e0dc
  • nav gray / muted text#6f6760
  • caption scrim (black 60%)#00000099

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on background14.40:14.5:1
Aatext on secondary13.18:14.5:1
Aanav gray on background5.33:14.5:1
Aanav gray on secondary4.88:14.5:1
Aaterracotta link text on background4.97:14.5:1
Aaterracotta link text on secondary4.55:14.5:1
terracotta 24px+ accent / indicator on background3.10:13:1
Aawhite text on primary button12.58:14.5:1
Aacaption over 60% black scrim on white-worst-case photo5.74:14.5:1
focus ring (primary) on background12.08: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
Poppins bold, ~38px with -0.96px tracking for the name
Body
Poppins 16-18px regular for nav and contact

Single geometric family, friendly and approachable; hierarchy by weight

Spacing and imagery

40px margins; portrait panels ~480px tall with 20px gaps and ~24px corner radius; single-screen home

Close-up color and black-and-white studio portraits of diverse people with soft backgrounds, cropped into tall rounded panels

Components

  • Name + collection nav header
  • Contact info block
  • Expanding portrait panel carousel
  • Caption overlay
  • Collection gallery
  • Lightbox (LightGallery)
  • Swiper slider
  • About section
  • Scroll-triggered animations

Interactions

  • Hover/click expands a panel while others compress (flex-grow animation)
  • Auto-advance with pause on hover
  • Swipe on touch
  • Caption fades in on active panel
  • GSAP scroll-triggered reveals on collection pages
  • Lightbox zoom and navigation

Data

  • Portrait{src, alt, subject, role, year, collection}
  • Collection{slug, title, description, cover}
  • Contact{email, phone}

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 portfolio for a fictional portrait photographer called **Dani Okafor**. The audience is people booking portrait sessions, brands looking for a photographer with a warm human style, and galleries reviewing themed series. The home page is a single, lively screen: a row of tall rounded portrait panels where one expands to show a caption while the others shrink to slivers.

### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix Dialog), lucide-react, React Router, Framer Motion for the panel expansion (`layout` animations), Swiper for touch galleries on collection pages, and a lightbox built from Radix Dialog (or yet-another-react-lightbox). GSAP is optional for scroll reveals. No backend.

### Pages & layout
1. Header: the photographer's name in bold at top left, with collection links underneath (Portraits, Everyday, People & Craft, About); email and phone right-aligned as plain links.
2. Home: a full-width row of 6–8 tall panels (about 480px) with 20px gaps and large corner radius. The active panel grows to about 3× width and shows a caption (subject – role – year) bottom-right; the others compress. It advances every 5s and pauses on hover or focus.
3. Collection page: intro line and a responsive gallery (masonry or justified) opening a lightbox with captions.
4. About: portrait of the photographer, bio, services (sessions, editorial, brand), and booking info.

### Design system
Tokens: `--background: #fcfaf8; --foreground: #2e251f; --primary: #3d3129; --accent: #dd713c; --secondary: #f2f0ed; --border: #e5e0dc; --muted-foreground: #6f6760; --accent-text: #b2501f; --border: decorative only`. Font: Poppins 400/500/700. Scale: 38px name with -2.5% tracking; 18px nav; 16px body at line-height 1.6, max 70ch; 13px captions at weight 500. Radius: 24px panels, 12px gallery images. Spacing: 40px margins, 20px panel gap. Motion: 500ms `cubic-bezier(.22,1,.36,1)` flex-grow transitions; 300ms caption fade. Light only.

### Components & interactions
- Panel strip: each panel is a button; hover, focus or tap expands it; left/right arrow keys move the active panel; autoplay has a visible pause/play button.
- Caption: white text that fades in on the active panel over a bottom gradient reaching 60% black.
- Gallery: lazy images with blurred placeholders; the lightbox supports arrows, swipe, Esc, and returns focus.
- Mobile: the strip becomes a horizontal snap carousel with one active card and peeking neighbors.
- Error fallback tile for failed images; empty collection message.

### Data & state
`Portrait{src,alt,subject,role,year,collection}`, `Collection{slug,title,description,cover}`, `Contact{email,phone}`. Active index state lives in the strip component; the lightbox index is in the URL. Use fictional subject names and roles, with consent-friendly stock or generated faces.

### Accessibility
The nav gray is #6f6760 on #fcfaf8 (5.3:1). Caption text sits on a bottom gradient reaching 60% black, which holds white captions at 5.7:1 over any portrait. The terracotta #dd713c is only 3.1:1 on the background, so it is used for fills and 24px+ text; small terracotta text and links use #b2501f. Autoplay must be pausable (WCAG 2.2.2) and off under `prefers-reduced-motion`. Panels need accessible names ("Portrait of Alex, music teacher, 2024") and `aria-expanded` or `aria-current` on the active one. Phone and email should be real `tel:` and `mailto:` links. Verified contrast: body text on background: #2e251f on #fcfaf8 = 14.4:1; nav gray on background: #6f6760 on #fcfaf8 = 5.33:1; terracotta link text on background: #b2501f on #fcfaf8 = 4.97:1; caption over 60% black scrim on white-worst-case photo: #ffffff on #666666 = 5.74:1.

### Security
Static: no forms required; if booking is added, validate with zod on the client and server with a honeypot. Strip EXIF metadata from images. External links use `rel="noopener noreferrer"`. Don't publish a personal mobile number in demo data; use a clearly fake placeholder.

### Performance & SEO
Only the visible panels load at full size; others use smaller crops (they're slivers). Serve AVIF/WebP with `srcset`; lazy-load collection images; code-split the lightbox. Person and ImageGallery JSON-LD; per-collection meta and OG images.

### Guardrails
- Use licensed or generated portraits with fictional names and roles; never caption real people with made-up biographies.
- No fake testimonials.
- Collect no personal data.
- Keep components small and typed, never use `any`, and show failed images visibly.
- Done when: the strip expands smoothly by mouse, keyboard and touch; autoplay pauses via control and reduced motion; captions meet contrast; collections open a working lightbox; the home page has no layout shift during expansion.

Open the builderAll templatesThis palette on its own