Template
Adaeze Bello
A monochrome, monospace photography site built around horizontal snap-scrolling galleries. A tiny header with a hamburger and surname sits above a row of tall editorial fashion and landscape photographs that bleed off the right edge. Categories (ceremonies, portraits, details) each have their own landing page, which suits wedding, event and editorial photographers who want a modern, image-led site.
Horizontal-scroll wedding photography site · 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
Inconsolata is a narrow, elegant mono that stays a whisper in black and white, so the facades read like contact prints.
- InconsolataHeadings: one-word wordmark 16px uppercase at 0.08em
- InconsolataBody: paragraphs 16px at 1.6
Who it is for
- Wedding and event photographers
- Editorial and fashion photographers
- Photographers building a personal brand
- Agencies presenting client work in custom galleries
Layout
- Minimal header: hamburger icon + uppercase surname in mono, top-left
- Fixed sidebar navigation revealed by the hamburger (categories, about, contact, theme toggle)
- Horizontal snap-scroll gallery of tall 2:3 images with ~24px gaps, the last one cut off to invite scrolling
- Category landing pages (ceremonies, portraits, details) each with its own horizontal gallery
- Project detail gallery
- About and contact pages
Palette
Monochrome, editorial, cinematic, restrained, modern. The site is a white gallery corridor lined with tall fashion prints.
- background
#ffffff - text
#000000 - primary
#000000 - secondary / muted
#f5f5f5 - dark mode bg
#000000 - photo tone (ochre)
#c9a15a
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | text on white | 21.00:1 | 4.5:1 |
| Aa | text on muted surface | 19.26:1 | 4.5:1 |
| Aa | dark mode text | 21.00:1 | 4.5:1 |
| progress bar fill on muted track | 19.26:1 | 3:1 | |
| prev/next icon on white circle button over photos | 21.00:1 | 3:1 | |
| input border on white | 21.00:1 | 3:1 | |
| focus ring on white | 21.00: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 monospace 16px uppercase with 0.08em tracking, for the one-word wordmark only (the system face is not on Google Fonts; a vibld project loads Inconsolata)
- Body
- Monospace 16px at 1.6 line-height, sentence case (Monospace is not on Google Fonts; a vibld project loads Inconsolata)
Pure monospace in black and white; typography is kept to a whisper so images read like contact prints.
Spacing and imagery
40px left margin, 24px gaps, images ~700px tall filling the viewport, zero radius, no borders.
Warm, cinematic editorial photography (models in suits in wheat fields, rocky coasts, mountain vistas) as tall uncropped portraits.
Components
- Hamburger + wordmark header
- Slide-out fixed sidebar nav
- Horizontal snap gallery
- Category landing pages
- Project gallery page
- Theme toggle (color inversion)
- Scroll hint / progress indicator
- About page
- Contact form
- Schema markup for SEO
Interactions
- Horizontal scroll with snap; wheel and trackpad mapped; drag on desktop
- Staggered image load animations
- Sidebar slides in from the left, Esc to close
- Dark mode inverts the palette
- Arrow keys move one image
- Progress bar tracks position in gallery
Data
Category{slug, title, description}Project{slug, title, category, date, location, images[]}Image{src, alt, width, height}Inquiry{name, email, eventDate, 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 monochrome, monospace photography portfolio for a fictional wedding and editorial photographer, "Adaeze Bello". It serves couples, planners and magazines who want to feel the photographer's style in seconds. The main experience is a horizontal, snap-scrolling strip of tall photographs. A hidden sidebar organizes the work into three categories (ceremonies, portraits, details) and links to about and contact.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS with class dark mode, shadcn/ui (Radix Sheet for the sidebar, Form, Toast) and lucide-react. Use Embla Carousel (drag-free with snaps) or native CSS scroll-snap for the galleries, Framer Motion for staggered loads, react-hook-form + zod and React Router. Supabase is optional: use it as a read-only content store (categories, projects and image metadata in Postgres, images in Storage) and for inquiry inserts. Everything else is static.
### Pages & layout
1. Header, 48px: a hamburger icon and the uppercase surname in 16px mono, top-left. Nothing else.
2. Sidebar (Sheet from the left): category links, About, Contact and a theme toggle, with the social handle at the bottom.
3. Home gallery: fills the viewport height minus the header. Images are 2:3 at about 88vh tall with 24px gaps. The row starts at a 40px margin and overflows to the right. A thin progress bar sits at the bottom.
4. Category page: a small mono title and one-line description, then that category's horizontal gallery.
5. Project page: title, date and location in mono, then the gallery.
6. Contact: name, email, event date (optional), message.
7. Mobile: galleries switch to vertical stacks.
### Design system
- Light: `--background: #fff`, `--foreground: #000`, `--muted: #f5f5f5`, `--input-border: #000`, `--radius: 0`. Dark is a pure inversion (#000/#fff). Prev/next buttons are solid white circles with black icons so they stay legible over photos.
- Font: Inconsolata 400 from Google Fonts at 14/16px (16px for paragraphs, 1.6 line-height), uppercase with 0.08em tracking for short navigation labels only, with no other sizes.
- Spacing: 8px base, 40px margin, 24px gaps.
- Motion: 500ms fade with 12px upward rise, a 70ms stagger and 250ms sheet slide.
### Components & interactions
- HorizontalGallery: scroll-snap, wheel-to-horizontal mapping (only when deltaY dominates), drag, ←/→ keys and prev/next buttons that appear on hover or focus. States: loading (gray placeholders), loaded, error tile.
- ProgressBar: shows scroll position, with `aria-hidden` (the buttons announce position instead).
- Sidebar: focus trap and Esc. The active category is underlined.
- ThemeToggle: persisted and system-aware.
- InquiryForm: states for invalid, submitting, success and error.
### Data & state
`Category{slug, title, description}`, `Project{slug, title, category, date, location, images}` and `Image{src, alt, w, h}` come from JSON or Supabase via TanStack Query. The theme is stored in localStorage. Seed three categories with 8-12 placeholder images each and invented locations.
### Accessibility
- The gallery is a labelled, focusable region with visible prev/next buttons, and its position is announced ("Image 3 of 12").
- Mono uppercase is used only for short labels.
- The hamburger has `aria-expanded` and a label. All images have alt text.
- Reduced motion disables staggered entrances and smooth scrolling.
- Verified contrast: text #000000 on #ffffff = 21.0:1; dark text #ffffff on #000000 = 21.0:1; progress bar #000000 on #f5f5f5 = 19.26:1.
### Security
- If you use Supabase: enable RLS with public `select` on content tables, `insert` only on `inquiries` (anon, with zod-validated fields via an Edge Function) and no public select on inquiries. Only the anon key goes in the client.
- Honeypot plus rate limiting on inquiries. Collect only name, email, date and message.
### Performance & SEO
Preload the first three images and lazy-load the rest with explicit sizes and AVIF `srcset`. Add Photographer and ImageGallery schema, per-category meta and OG images.
### 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:
- [ ] Galleries snap horizontally and work with wheel, drag, keys and buttons
- [ ] Mobile falls back to vertical stacks
- [ ] Sidebar is keyboard-accessible with focus trap
- [ ] Dark mode inverts cleanly and persists
- [ ] Inquiries cannot be read with the anon key