How this site
was built.
A short, honest account of the stack, decisions, and workflow behind this site — written so anyone can replicate the approach for their own brand.
Stack
Approach
Start from a real reference, not a vibe
The brief named 111west57th.com explicitly. Before writing any code, the goal was distilled into concrete traits: full-bleed cinematic hero, transparent nav that solidifies on scroll, oversized editorial serif type, restrained color, and scroll-scrubbed parallax rather than autoplay animation. Naming the traits up front kept later decisions (fonts, easing, section pacing) anchored to something specific instead of generic 'luxury site' defaults.
Build the palette from the brand, not a swatch library
The client's existing logo — a teal-to-cyan gradient monogram — became the single source of truth for color. Rather than inventing a new palette, the teal/cyan range was sampled directly and paired with a near-black ink base, warm porcelain for light sections, and a muted brass accent for hairlines and hover states. This is what makes the site read as one brand rather than a template with a logo dropped on top.
Use real assets wherever they exist
The client supplied two waterfront photographs and a short video. Those became the hero background and the flagship 'Featured Address' gallery — real material outranks generated material whenever it exists. AI image generation was intentionally skipped here since authentic assets were already on hand; the guidance is to reach for generation only to fill genuine gaps, not to replace real photography by default.
Typography does the 'legacy' work
A high-contrast display serif (Cormorant Garamond) paired with a tight geometric sans (Manrope) does most of the heavy lifting for perceived prestige — large italic serif accents on key words, uppercase tracked-out eyebrows, and generous line-height throughout. No decorative or novelty fonts: restraint reads as more expensive than variety.
Layer three motion systems, each with a job
Lenis handles inertial smooth-scroll globally. GSAP + ScrollTrigger drives anything tied to scroll position — the hero's word-mask headline reveal, parallax offsets on the gallery images, pinned/scrubbed transforms. Framer Motion handles anything viewport- or state-triggered — section reveals, the mobile menu's clip-path takeover, form label transitions. Keeping these responsibilities separate (position-driven vs. state-driven) avoids the jank that comes from fighting scroll libraries against each other.
Let 3D be an accent, not a stunt
A single React Three Fiber scene — an abstracted glass tower cluster with a transmission material — sits in the Phase 8 section. It's lazy-loaded with next/dynamic (ssr: false), gated behind a WebGL feature check with a silent no-op fallback, and kept small in geometry so it never becomes the site's bottleneck. One well-placed 3D moment reads as craft; three or four would read as showing off.
Design for reduced motion and slow connections from day one
prefers-reduced-motion is honored globally (Lenis and GSAP effects short-circuit), the custom cursor only renders on fine-pointer devices, and the hero video ships with a poster image fallback. None of this was retrofitted — building it in from the first section made every later section cheaper to make accessible.
Iterate with a real critique pass, not just a build-and-ship
After the initial build, the site went through structured review passes: one for structural/design cohesion, one for motion and interaction refinement (easing, timing, missing micro-interactions), and one for content, responsiveness edge cases, and performance. Treating polish as a distinct phase — not something squeezed into the original build — is what separates a finished site from a first draft.
Replicate it yourself
- Scaffold with
create-next-app(TypeScript, Tailwind, App Router). - Install
framer-motion gsap lenis three @react-three/fiber @react-three/drei. - Pull your palette from an existing brand asset (logo, packaging, signage) rather than inventing one.
- Pick one display font and one workhorse sans — commit to the pairing everywhere.
- Wire Lenis + GSAP ScrollTrigger globally; use Framer Motion for viewport/state-triggered moments.
- Add exactly one 3D or standout motion moment — lazy-loaded, with a graceful fallback.
- Respect
prefers-reduced-motionand test on a real mobile viewport before calling it done. - Run at least two or three critique passes before shipping — treat polish as its own phase.