Template
Cantilever House
A dark, cinematic portfolio for an architecture and interiors studio where typography carries the imagery: the hero headline is masked over playing video, and project cards fill their names with each project's photograph. Project pages, team bios and a contact form complete the site.
Architecture and interiors 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
Noto Sans Display has a heavy, tightly fitting bold that works when imagery sits inside the letterforms, and it stays neutral in text.
- Noto Sans DisplayHeadings: hero ~96px bold, card titles ~56px, tight leading
- Noto Sans DisplayBody: body 16px
Who it is for
- Architecture and interior design studios
- Studios bidding for commercial work
- Firms showcasing residential renovations
- Practices expanding into new cities
Layout
- Scroll-reactive header: bold wordmark left, Work / About / Our Team / Contact right with underline on active
- Full-bleed architectural video/photo hero with two-line bold headline; second line filled with the video through text masking
- Black 2-column project grid separated by hairline rules: each cell has a two-line project descriptor in white and project name filled with its photo (image-masked text)
- Project detail pages: full-bleed hero, year/location/scope metadata, description
- Team bios page
- Contact page with form
- Footer with nav and copyright
Palette
Cinematic, bold, sophisticated, dark, architectural. It turns project names into windows onto the buildings.
- page background (light pages)
#ffffff - work grid background
#000000 - text on dark
#ffffff - foreground
#09090b - muted text (light pages)
#52525b - primary
#18181b - decorative grid rule
#3f3f46 - muted surface
#f4f4f5 - input border
#8c8c99 - dark mode muted text
#a1a1aa - hero overlay (black 60%)
#00000099
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | light: body text | 19.90:1 | 4.5:1 |
| Aa | light: muted text on white | 7.73:1 | 4.5:1 |
| Aa | light: muted text on muted surface | 7.03:1 | 4.5:1 |
| Aa | light: white on primary button | 17.72:1 | 4.5:1 |
| light: input border | 3.32:1 | 3:1 | |
| Aa | work grid descriptor text on black | 21.00:1 | 4.5:1 |
| masked project name stroke (56px+) on black | 21.00:1 | 3:1 | |
| focus ring on black grid | 21.00:1 | 3:1 | |
| Aa | hero solid line over 60% black overlay on white-worst-case footage | 5.74:1 | 4.5:1 |
| Aa | dark: body text | 19.06:1 | 4.5:1 |
| Aa | dark: muted text on card | 6.91:1 | 4.5: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
- Bold system sans (~96px hero, ~56px card titles), tight leading, sentence case with period endings (the system face is not on Google Fonts; a vibld project loads Noto Sans Display)
- Body
- System sans 16px (the system face is not on Google Fonts; a vibld project loads Noto Sans Display)
One heavy sans; the imagery lives inside letterforms rather than beside them
Spacing and imagery
Full-bleed hero; project grid of two equal columns with 1px rules, ~360px tall cells, 52px inner padding; square corners
Architectural photography and video (curved concrete, glazing, stairs) shown through text masks and full-bleed on project pages; slow drift animation
Components
- Scroll-reactive header
- Video-masked hero headline
- Canvas video renderer
- Image-masked project title cards
- Hairline project grid
- Project detail hero
- Metadata row (year, location, scope)
- Team member cards
- Contact form with toast
- Theme provider
- Footer nav
Interactions
- Video plays behind masked hero text
- Masked card images slowly drift
- Header background changes on scroll
- Card hover brightens and reveals arrow
- Route transitions
- Form submission toast
- Light/dark theme support
Data
Project{slug, descriptor, name, year, location, scope, heroImage, images[], description}TeamMember{name, role, bio, photo}ContactMessage{name, email, company, 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 portfolio site for a fictional architecture and interiors studio called **Cantilever House**. The audience is developers, hospitality groups and homeowners looking for a high-end studio. The signature idea: imagery lives inside the typography. The hero headline is filled with moving footage and each project's name is filled with its own photograph, set in a black grid. Project pages, team bios and a contact form complete the site.
### Stack
React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix), lucide-react, TanStack Router (or React Router) for typed routes, Framer Motion for drift and route transitions, Sonner for toasts, next-themes for theme handling, and react-hook-form + zod for the contact form. Text masking uses `background-clip: text` for images and a `<canvas>` or `mix-blend-mode` approach for video. The contact form posts to one serverless function.
### Pages & layout
1. Header: bold wordmark left and four links right with an underline on the active link; transparent over the hero, solid black with a bottom rule after scroll.
2. Hero (90vh): full-bleed architectural photo or video under a 60% black overlay, with the headline on two lines: the first solid white, the second filled with the moving footage.
3. Work grid: black background, two equal columns separated by 1px rules; each cell holds a two-line descriptor in white bold ("Double height") and the project name below, filled with that project's photo and ending in a period.
4. Project page: full-bleed hero, a metadata row (year, location, scope, area), narrative text and a gallery, with next and previous links.
5. About and Team: studio text; team cards with portrait, name, role and short bio.
6. Contact: form plus studio address and email; footer repeats the navigation.
### Design system
Tokens: `--background: #ffffff; --foreground: #09090b; --primary: #18181b; --muted: #f4f4f5; --border: #e4e4e7 (decorative); --input-border: #8c8c99; --muted-foreground: #52525b; --work-bg: #000000; --work-rule: #3f3f46 (decorative)`. Dark mode for content pages: `--background: #09090b; --foreground: #fafafa; --muted-foreground: #a1a1aa; --card: #18181b`. Font: Noto Sans Display 700/800 for display and Noto Sans Display 400 for body. Scale: clamp(56px, 8vw, 104px) hero; 56px card names; 20 / 16 / 14px (16px body at line-height 1.6, max 70ch). Spacing: 52px cell padding, 360px minimum cell height, 8px base. Square corners. Motion: masked images drift `background-position` over 20s linear alternate; header transition 200ms; route fade 300ms. Dark work grid, light content pages; dark mode flips content pages.
### Components & interactions
- MaskedText: props for image or video source; falls back to solid text when `background-clip: text` is unsupported or the image fails.
- Video mask: pauses when off-screen (IntersectionObserver) and when the tab is hidden.
- Project cell: hover increases image brightness and shows an arrow; focus ring; the whole cell is one link.
- Contact form: labelled fields, inline errors, submit spinner, success and error toasts.
- Empty state for team if no members; 404 for unknown project slugs.
### Data & state
`Project{slug,descriptor,name,year,location,scope,area?,hero,images[],body}`, `TeamMember{name,role,bio,photo}`. Static typed data. Seed nine projects with invented names and cities.
### Accessibility
Image-filled text can have poor contrast, so every masked name (56px and up) gets a 1.5px #ffffff text stroke that guarantees the letter edges reach 3:1 against black whatever the photo, and each link takes its accessible name from plain data. The solid first hero line sits over a 60% black overlay (5.7:1 on a white-worst-case frame). Light pages use #52525b for muted text and #8c8c99 input borders. The masked hero line must exist as real text for screen readers. Provide a pause control for the hero video and stop it under `prefers-reduced-motion`, which also disables drift. Keep a visible focus ring on black. Verified contrast: light: muted text on white: #52525b on #ffffff = 7.73:1; masked project name stroke: #ffffff on #000000 = 21.0:1; hero solid line over 60% black overlay on white-worst-case footage: #ffffff on #666666 = 5.74:1.
### Security
Validate the contact form with zod on the client and in the function; add a honeypot and rate limit. Keep API keys server-side. External links use `rel="noopener noreferrer"`.
### Performance & SEO
Keep the hero video short and compressed with a poster; lazy-load project images used as masks (use small, 800px-wide crops). Code-split project pages. Per-project titles, descriptions and OG images; Organization JSON-LD. Watch CLS by giving cells fixed heights.
### Guardrails
- Use original copy and placeholder or generated architecture imagery; don't claim real buildings.
- No fabricated client quotes.
- Store only contact form fields.
- Keep components small and typed, never use `any`, and handle media failures visibly.
- Done when: masked text falls back gracefully without image support; the hero video pauses off-screen and under reduced motion; each project cell has a clear accessible name; project pages resolve by slug; the contact form shows success and error states.