Skip to main content
vibld

Template

Inés Marlowe Photography

a site for a wedding and portrait photographer that lets the work speak first, then answers the three questions every client has (what does it cost, what is it like, is my date free) and takes the inquiry.

Photography portfolio site · Website · Portfolio · 5 pages

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.

Account setup
Add edit
Analytics
Appearance
Calendar
Chat
Checklist
Checkout
Confirmation
Dashboard
Delete account
Details
Discovery questions
Empty state
Flowchart
Import export
Inbox
Integrations
Invite team
Loading
Login
Onboarding
Plans
Playground
Product tour
Referral
Search
Settings
Share
Sign up
Success
Table
Team members
Text editor
Upgrade
Usage
Verification
Welcome

Start from this templateRead the build prompt

Typefaces

Gloock’s high-contrast serif sits beside photographs like a museum label; Gantari stays out of the way in light weights.

  • GloockHeadings: headlines and gallery titles
  • GantariBody: captions, packages and forms

Who it is for

  • Couples comparing photographers for their wedding
  • Families booking a portrait session
  • Planners and venues looking for a referral

Layout

  1. Minimal top bar: name, Galleries, Pricing, About, and Check your date
  2. Full-bleed hero: a horizontal film strip of twelve photos that drags and snaps, the headline set small over the first frame
  3. Short intro paragraph beside a portrait of the photographer
  4. Featured galleries: three large cards with couple names and place
  5. How it works: four steps from inquiry to gallery delivery
  6. Kind words: one quote at a time, large
  7. Inquiry form band and footer with Instagram and email

Palette

Dark, quiet and immersive, like a gallery after hours: near-black walls, warm bone type, one amber accent like a safelight.

  • gallery black#0f0e0d
  • bone#ece6dc
  • safelight amber#d9a35f
  • charcoal#1c1a18

Every checked pair, measured again

SampleWhereRatioNeeds
Aabody text on the page15.54:14.5:1
Aatext on cards13.98:14.5:1
Aasecondary text on muted surfaces7.61:14.5:1
Aasecondary text on the page8.94:14.5:1
Aalabel on the primary action8.59:14.5:1
Aalabel on secondary surfaces13.98:14.5:1
Aalabel on the accent13.98:14.5:1
Aaerror label5.46:14.5:1
primary action against the page8.59:13: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
Gloock, headlines and gallery titles
Body
Gantari, captions, packages and forms

Spacing and imagery

Wide: images edge to edge, text blocks narrow, 120px between sections.

The photographer’s own work, full bleed and uncropped where possible; never a frame or a filter added by the site.

Components

  • Film strip: horizontal scroll with scroll-snap, drag on desktop, arrow keys, and a frame counter
  • Gallery page: masonry grid with a keyboard-friendly lightbox
  • Package card with starting price and an inquire link that preselects it

Interactions

  • Lightbox opens from any gallery photo, closes on Escape, and moves with arrow keys and swipes
  • Inquiry form keeps a chosen package from the pricing page

Data

  • galleries: a gallery: title, kind (wedding, portrait), place, date, cover photo, photos with alt text, story in Markdown
  • packages: a package: name, starting price, hours, what is included, best for
  • form inquiry: names, email, phone, kind (wedding, portrait, other), date, venue or place, guest count, budget range (select), how you heard, 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
Each niche site prompt describes a whole small site for one kind of owner: its pages, the records it keeps, the forms it collects and the tools it links to. The rules below apply to every one of them.

### Niche site principles
- The site does the one job its owner needs most (a sign-up, an RSVP, a request, a visit) on the first screen, and every page leads back to it.
- Records the owner adds often (sermons, events, listings, galleries, menus, tour dates) are content collections in `src/content/`, each record with a slug, so a new record gets its page with no code change.
- Tools the owner already pays for or uses (giving, tickets, booking, listing search, ordering) are linked, or framed in an `<iframe>` whose address is in `content.ts`, never rebuilt. With no address, a plain button links out instead, and nothing pretends to work.
- Copy is written for the owner's visitors, specific and plain. Dates, times and places are real fields, never baked into prose.
- The design is this site's own. Its layout, type and motion follow the brief, not a shared template.

### Forms
Every form is a plain HTML form, `<form method="post" action="/__vibld/forms/<name>">`, named as the brief names it (lowercase letters, digits and hyphens). Fields are ordinary named inputs; checkboxes repeat their name; no file uploads. Add a hidden `_next` input with the path to land on, and show a thank-you there when the address has `?submitted=<name>`. Add an empty hidden `_gotcha` input. A script may post it instead with `fetch(form.action, { method: 'POST', body: new FormData(form), headers: { accept: 'application/json' } })`, which answers `{ ok: true }` or `{ ok: false, error }`. A form with limited places lists them in `public/vibld.forms.json` (`{"forms":{"<name>":{"slots":{"field":"<field>","options":{"<value>":<places>}}}}}`), the field a select or radio whose values match those keys exactly; `GET /__vibld/forms/<name>/slots` returns `{ field, options: [{ value, capacity, taken, left }] }`, which the page reads to show what is left and disable full options. vibld adds bot protection when the page is served.

### Engineering baseline
- TypeScript strict mode, small typed components, and one source of truth for design tokens (CSS variables consumed by Tailwind).
- All copy lives in typed content files, so the owner can edit words without touching layout.
- Every form validates on the client, says what went wrong in plain words beside the field, and never claims success it has not had.
- No secrets in the bundle and no third-party scripts beyond Google Fonts; an owner's tool comes in only as a link or a framed page.
### Goal
Build **Inés Marlowe Photography**: a site for a wedding and portrait photographer that lets the work speak first, then answers the three questions every client has (what does it cost, what is it like, is my date free) and takes the inquiry.

### Stack
React + TypeScript + Vite, Tailwind CSS and React Router, one route per page. Content collections in `src/content/`, forms posting to vibld (see Forms in the baseline). No backend of its own.

### Pages & layout
- `/` Home: film strip of selected work, intro, featured galleries, how it works, kind words, inquiry
- `/galleries` Galleries: weddings and portraits, each gallery with its page
- `/pricing` Pricing: packages with starting prices and what is included, FAQ
- `/about` About: portrait, short story, approach
- `/inquire` Inquire: inquiry form and what happens next

Home, top to bottom:
1. Minimal top bar: name, Galleries, Pricing, About, and Check your date.
2. Full-bleed hero: a horizontal film strip of twelve photos that drags and snaps, the headline set small over the first frame.
3. Short intro paragraph beside a portrait of the photographer.
4. Featured galleries: three large cards with couple names and place.
5. How it works: four steps from inquiry to gallery delivery.
6. Kind words: one quote at a time, large.
7. Inquiry form band and footer with Instagram and email.

Copy to start from, in `content.ts`: headline "Pictures of the day as it felt", subcopy "Weddings and portraits in the Hudson Valley and wherever you are going. Booking 2027 now.", primary action "Check your date".

### Design system
Mood: Dark, quiet and immersive, like a gallery after hours: near-black walls, warm bone type, one amber accent like a safelight.

Palette: gallery black `#0f0e0d`, bone `#ece6dc`, safelight amber `#d9a35f`, charcoal `#1c1a18`. Tokens: background `#0f0e0d`, foreground `#ece6dc`, primary `#d9a35f` with `#0f0e0d` labels, cards `#1c1a18`, accent `#1c1a18`.

Type: Gloock for headlines and gallery titles; Gantari for captions, packages and forms. Load both from https://fonts.googleapis.com/css2?family=Gloock&family=Gantari:wght@300;500&display=swap.

Corners: 2px on controls, 2px on cards, 2px on large panels. Spacing: Wide: images edge to edge, text blocks narrow, 120px between sections.

Imagery: The photographer’s own work, full bleed and uncropped where possible; never a frame or a filter added by the site. Use the owner's photos where the project has them. Without them, use the project's stock photos, or shapes and color from the palette; never a stock photo of people at desks.

### Components & interactions
- Film strip: horizontal scroll with scroll-snap, drag on desktop, arrow keys, and a frame counter
- Gallery page: masonry grid with a keyboard-friendly lightbox
- Package card with starting price and an inquire link that preselects it

Motion:
- Lightbox opens from any gallery photo, closes on Escape, and moves with arrow keys and swipes
- Inquiry form keeps a chosen package from the pricing page

The signature motion is: the film strip drifts slowly sideways on its own until touched, then follows the drag with momentum and snaps to a frame. Keep it to that one; everything else is a 150 to 300ms state change. With prefers-reduced-motion, it and every reveal are still.

### Data & state
Collections:
- `src/content/galleries.json`: a gallery: title, kind (wedding, portrait), place, date, cover photo, photos with alt text, story in Markdown; each has its page at `/galleries/<slug>`
- `src/content/packages.json`: a package: name, starting price, hours, what is included, best for

Forms:
- `inquiry`: A client asks about a date. Fields: names, email, phone, kind (wedding, portrait, other), date, venue or place, guest count, budget range (select), how you heard, message.

Linked tools:
- Client proofing and delivery: the photographer’s gallery service (Pixieset, Pic-Time or ShootProof), linked as Client login in the footer.
- Instagram profile link.

### Accessibility
WCAG 2.2 AA. Every control has a visible focus ring and a 44px target, every form field a visible label, and errors are announced. Dates and times are written out ("Sunday, March 8, 10:30 AM"), not only shown in a grid. Images have alt text that says what is in them. Verified contrast: body text on the page #ece6dc on #0f0e0d 15.53:1; text on cards #ece6dc on #1c1a18 13.97:1; secondary text on muted surfaces #b5b0a8 on #211f1e 7.61:1; secondary text on the page #b5b0a8 on #0f0e0d 8.94:1; label on the primary action #0f0e0d on #d9a35f 8.59:1; label on secondary surfaces #ece6dc on #1c1a18 13.97:1; label on the accent #ece6dc on #1c1a18 13.97:1; error label #ffffff on #c62f2f 5.45:1; primary action against the page #d9a35f on #0f0e0d 8.59:1.

### Security
No third-party scripts other than Google Fonts. Framed tools are `<iframe>`s from the owner's own address with a `title`, `loading="lazy"` and `referrerpolicy="strict-origin-when-cross-origin"`. Forms cap input length and carry the empty `_gotcha` field.

### Performance & SEO
Images as AVIF or WebP with width and height, the first one eager and the rest lazy. Preconnect to Google Fonts. Each page has its own title and description from content, and records' pages take theirs from the record. Structured data where it fits: Person and CreativeWork.

### Guardrails
- Avoid: Watermarks over the work, right-click blocking, autoplay music, cursive script logos, and white mats around photos.
- One primary action per screen, one accent color, at most six top-level links.
- No carousel in the hero, no mega menu, no emoji or clip-art icons, no hard offset shadows, no purple-to-blue gradient.
- The names in it are invented. Use no real organization's or person's name, logo or trademark.

Open the builderAll templatesThis palette on its own