A cinematic landing page for a fictional 82-floor luxury tower — Vanilla JS ES Modules, Tailwind CSS v4, and Vite.

Apex is a cinematic marketing site for a fictional 82-floor luxury residential tower. Every visual decision — the full-bleed video background, the film-grain overlay, the staggered scroll-reveals — was made to match the tone of a real high-end real estate brand. No frameworks were touched. This is Vanilla JS doing serious work.
Overview
The challenge was simple on paper: build something that looks like it cost a lot, using the least possible runtime weight. Apex loads a single JavaScript entry point, registers ES Module components for each section, and lets IntersectionObserver handle all the scroll choreography. Tailwind v4 handles every style decision through a CSS-first config — no tailwind.config.js file anywhere.
The result is a page that feels premium from the first frame: a looping cinematic video fades in beneath a noise-grain texture, the headline animates in as the background settles, and the scroll journey downward reveals floor plans, amenities, and pricing with staged timing that never feels mechanical.
Apex ships with no JavaScript frameworks and no animation libraries. Every transition — from the stat counters to the marquee to the scroll-reveals — is implemented in under 600 lines of vanilla ES Modules.
Features
| Feature | Details |
|---|---|
| Cinematic hero | Looping video background with multi-stop gradient overlay and a CSS film-grain texture layer |
| Scroll-reveal system | IntersectionObserver-based, with configurable stagger delay per section |
| Animated stat counters | Count up from zero on first viewport entry, with locale-formatted numbers |
| Infinite publication marquee | CSS animation with will-change: transform — zero JS scroll listeners |
| Pricing cards | Three-tier layout with highlighted featured tier and hover lift effect |
| Contact form | Client-side validation with accessible inline error messages |
| Responsive navigation | Mobile hamburger with smooth open/close, sticky on scroll with backdrop blur |
Tech Stack
| Tool | Role |
|---|---|
| Vite | Dev server, HMR, and optimized production build |
| Tailwind CSS v4 | CSS-first configuration, no JS config file |
| Vanilla JS (ES Modules) | Component architecture, scroll logic, counter animation |
| Google Fonts — Urbanist + Inter | Display and body typefaces |
Architecture
The codebase is organized as a set of single-responsibility ES modules — one per page section — imported by a root main.js. There is no virtual DOM, no state management library, and no component lifecycle. Each module exports an init() function that wires up its own DOM interactions.
src/main.js
import { initHero } from "./components/hero.js"
import { initCounters } from "./components/counters.js"
import { initMarquee } from "./components/marquee.js"
import { initScrollReveal } from "./components/scroll-reveal.js"
import { initNav } from "./components/nav.js"
document.addEventListener("DOMContentLoaded", () => {
initNav()
initHero()
initCounters()
initMarquee()
initScrollReveal()
})The scroll-reveal module registers a single shared IntersectionObserver instance and maps data-reveal attributes to animation classes, keeping all scroll logic in one place rather than scattered across components.
src/components/scroll-reveal.js
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry, i) => {
if (!entry.isIntersecting) return
const delay = i * 80
setTimeout(() => entry.target.classList.add("is-visible"), delay)
observer.unobserve(entry.target)
})
},
{ threshold: 0.15 }
)
export function initScrollReveal() {
document
.querySelectorAll("[data-reveal]")
.forEach((el) => observer.observe(el))
}Visual Design Decisions
The film-grain effect is a pure CSS technique: a high-frequency noise SVG is embedded as a background-image on a pseudo-element, animated to shift position every frame. It adds tactile depth without a single canvas draw call.
The color palette is deliberately restrained — near-black backgrounds, warm off-white type, and a single gold accent used only for interactive states and pricing highlights. Typography pairs Urbanist (all-caps display headings) with Inter (body and UI), a combination that reads as polished without being generic.
Tailwind v4 eliminated the JavaScript config file. Design tokens — colors,
spacing scale, font families — are declared directly in CSS using @theme.
This keeps the styling layer self-contained and makes the project trivial to
inspect without a build step.