Iconic Centre
Modular homes website with scroll-wipe hero effects, 171+ suburb landing pages, Sanity CMS, and an eligibility assessment flow.
GSAP Hero Experience
Two-phase scroll-wipe hero — project showcase with stats transitions into brand messaging with ScrollTrigger-driven animation
171+ Suburb Pages
Dynamically generated landing pages for every LMR-eligible suburb in NSW, each with localised content and eligibility data
Sanity CMS + Blog
Content-managed blog with MDX components (callouts, comparison tables, key stats) and automatic article feeds
Lead Qualification
Multi-step eligibility assessment flow that qualifies prospects before the sales call — suburb lookup, site assessment, consultation booking
The Challenge
Iconic Centre operates in NSW’s modular construction sector — a market created by the state government’s Low & Mid-Rise (LMR) housing policy. The policy enables faster residential development through pattern book compliance and CDC (Complying Development Certificate) fast-track approval: instead of a traditional DA process that takes 12-18 months and involves council negotiation, eligible projects achieve approval in weeks through private certification.
The market opportunity is substantial but unusually complex for a website to communicate. Three specific problems made this project different from a standard construction site:
Eligibility is suburb-specific. LMR policy designates which suburbs qualify for fast-track development. A landowner in Pagewood might be eligible; a landowner in Mosman might not. The website needed to answer “does my suburb qualify?” before anything else — because if the answer is no, nothing else on the site matters.
The product requires education. Modular construction with regulatory compliance isn’t intuitive. Most property owners have never heard of CDC approval, pattern book compliance, or factory-built construction. The site needed to move prospects from “what is this?” to “my site might qualify” to “I should talk to someone” — and that’s a content challenge, not just a design challenge.
The sales team’s time is expensive. Not every enquiry is a real prospect. A site qualification call takes 30-45 minutes. Spending that time on a landowner whose suburb isn’t eligible, or whose site doesn’t meet setback requirements, is wasted capacity. The website needed to filter leads before they reached a human.
Why This Tech Stack
Astro 5 + Svelte 5 + Sanity CMS
The decision to use Astro with Svelte instead of Next.js or WordPress came down to one requirement: 171+ suburb pages generated at build time without a Node.js server or ongoing compute costs.
| Factor | Next.js | WordPress | Astro + Svelte |
|---|---|---|---|
| 171+ dynamic pages | ISR or SSR — requires server, cold starts, Vercel costs scale with page count | Would need a plugin for bulk page generation, each page as a WP post | getStaticPaths() generates all 171+ at build time — zero runtime cost |
| Interactive components | React runtime ships to client (~40KB+ min) | Plugin dependency for interactive elements | Svelte 5 islands hydrate independently — only interactive components ship JS |
| Content management | Headless CMS needed anyway | Built-in but fragile with this many pages | Sanity Studio embedded at /studio — same domain, content-managed |
| Build cost | Vercel pricing scales with ISR revalidation | Hosting + plugins + managed updates | Cloudflare Pages free tier — 171+ static pages cost nothing to serve |
Svelte 5 specifically (not Svelte 4) was chosen because runes ($state, $derived, $effect) provide reactive state without stores or subscriptions. The interactive components — project gallery cards, form analytics, custom cursor, cookie consent — are cleaner and more predictable with runes than with Svelte 4’s reactive declarations.
GSAP Over CSS Animations
The homepage hero uses GSAP ScrollTrigger, not CSS scroll-driven animations. The decision was practical: the hero requires a two-phase pin-and-wipe sequence where one full-viewport section pins, then wipes horizontally to reveal a second section. CSS scroll-timeline can handle simple parallax but breaks down with pinning, coordinated multi-element staggering, and scroll-position-dependent state changes. GSAP handles this natively with pin: true and timeline sequencing.
The trade-off: GSAP is a 27KB dependency. For a site with one complex scroll interaction, that’s acceptable. If the animation were simpler (fade-in on scroll, basic parallax), CSS would have been the right choice.
The GSAP Scroll-Wipe Hero
This is the site’s signature interaction — the first thing a visitor experiences, designed to communicate three things in under 10 seconds: “we build premium projects”, “we deliver fast”, and “here’s how to start.”
Phase 1: Project Showcase
A featured development render fills the viewport. Three statistics animate in — residences count, construction timeline (6-9 months), and approval type (Fast Track CDC). The image is pulled from the typed project data system, so it updates automatically when the featured project changes. The stats use the formatProjectStats() helper to parse timeline strings consistently.
Phase 2: Brand Messaging
As the user scrolls, the project showcase wipes horizontally to reveal the value proposition underneath. Three badges stagger in (“Pattern Book Compliant”, “Private Certification Pathway”, “Modular Delivery in 6-9 Months”), followed by two CTAs: “Check If Your Site Qualifies” (primary) and “Book a Free Site Feasibility Call” (secondary).
Custom Cursor
A custom cursor (Cursor.svelte) tracks across both phases, replacing the default pointer with a branded element. This is a small detail that signals design sophistication — the kind of thing a property developer notices, even subconsciously, when evaluating whether this company delivers premium work.
Motion Accessibility
The entire hero sequence respects prefers-reduced-motion. If the OS-level setting is enabled, the wipe transition is replaced with a simple crossfade, and statistics appear without stagger delays. The content is identical; the motion is removed.
171+ Dynamic Suburb Pages
The SEO Architecture
This is the site’s acquisition engine. When a landowner searches “modular homes Pagewood” or “townhouse development Epping”, they need to land on a page that speaks directly to their suburb.
Every LMR-eligible suburb in NSW gets its own page, generated at build time from src/data/suburbs.ts. The data file contains suburb names, postcodes, eligibility status, and any suburb-specific notes. Astro’s getStaticPaths() generates 171+ routes in a single build — each suburb becomes /suburbs/[suburb-name] with its own <title>, <meta description>, and structured content.
Each suburb page includes:
| Section | Content | Source |
|---|---|---|
| Eligibility status | Whether the suburb qualifies for LMR development | suburbs.ts data |
| Development types | What can be built (house-and-land, townhouse, knockdown-rebuild) | Template + data |
| Google Maps embed | Map centred on the suburb | Google Maps API per-client project (isolated 10K free loads) |
| CTA | ”Check if your site qualifies” → Get Started flow | Static |
| Related suburbs | Links to nearby eligible suburbs | Computed from data |
Why This Approach Works
At 171+ pages, manual maintenance is impossible. When NSW policy changes (and it does — suburb eligibility can shift with legislative updates), one edit to suburbs.ts and a rebuild updates every suburb page simultaneously. No one touches 171 individual pages.
The Google Maps integration uses a per-client Google Cloud project to isolate API usage within the free tier (10K loads). This was a deliberate infrastructure decision: Google Maps on 171+ pages with shared API keys would quickly exceed free tier limits.
Typed Project Portfolio
Schema Design
The project data system demonstrates a specific architectural choice: structured JSON data with TypeScript types, rather than free-form CMS content.
src/data/
├── projects.json # Source of truth (data)
├── projects.ts # Types + helper functions
├── PROJECTS_README.md # Schema documentation
└── suburbs.ts # Suburb eligibility dataEach project record in projects.json has 12 typed fields:
| Field | Type | Purpose |
|---|---|---|
id / internalId | string | URL slug + internal reference |
name | string | Display name |
type | enum | house-land, townhouse, knockdown-rebuild, resort, multi-dwelling |
status | enum | planning, in-progress, completed, on-hold |
address | object | street, suburb, state, postcode, full formatted |
headline / description | string | Marketing copy |
specifications | object | Ceiling height, glass type, flooring, kitchen, heating, included features |
stats | object | Residence count, timeline, approval type |
features | string[] | Feature list |
partners | array | Name + role (architect, interior, landscape) |
images | object | Hero path, gallery array, total count |
Helper functions in projects.ts provide filtered views:
getProjectsByStatus('completed')— for the portfolio gridgetProjectsByType('townhouse')— for type-specific pagesgetFeaturedProjects(3)— for the homepageformatProjectStats(project)— parses “6-9 months” into{ value: "6-9", label: "Months" }for consistent stat renderinggetProjectImagePath(id, index)— generates responsive image paths from project IDs
This system was chosen over Sanity CMS for projects because project data changes infrequently (new projects are added quarterly, not daily) and the typed JSON schema provides compile-time validation. A typo in a project type field is caught at build time, not discovered by a user on the live site.
Lead Qualification Flow
Funnel, Not Form
The Get Started page (/get-started) is designed to qualify prospects, not just collect contact details. The flow:
- Suburb eligibility check — is their suburb LMR-eligible?
- Site characteristics — lot size, current structure, development goals
- Consultation booking — qualified leads book a feasibility call
Form analytics (FormAnalytics.svelte) instrument every step: where prospects drop off, which suburbs generate the most interest, which development types are most requested. This data feeds back into marketing — if 40% of prospects come from 5 suburbs, those suburbs get more targeted content.
A Quick Lead Capture component (QuickLeadCapture.astro) appears on content pages for prospects who want to skip the full flow. Both pathways route through cookie consent management (CookieConsent.svelte) and page-level analytics (PageAnalytics.astro).
Blog with MDX Components
Authority Through Education
In an emerging market, the company that educates wins the customer. Iconic Centre’s blog targets the knowledge gap: most landowners don’t know what CDC approval means, how modular construction differs from traditional, or whether their suburb qualifies for LMR development.
The blog is powered by Sanity CMS with 7 custom MDX components that enable richer content than standard markdown:
| Component | Purpose | Example Use |
|---|---|---|
| Callout | Highlighted boxes for key takeaways | ”CDC approval can reduce your timeline from 18 months to 6 weeks” |
| ComparisonTable | Side-by-side structured comparisons | Modular vs. traditional: cost, timeline, quality |
| KeyStats | Prominent stat blocks | ”171 suburbs now eligible for LMR development” |
| ArticleCTA | Contextual calls to action | ”Check if your suburb qualifies” mid-article |
| Alert | Time-sensitive notices | Policy deadline reminders, application windows |
| Checklist | Actionable step lists | ”5 things to check before applying for CDC approval” |
| Highlight | Emphasized text blocks | Key policy changes or requirements |
A LatestArticles component on the homepage automatically surfaces the most recent posts, keeping the site current without manual homepage updates.
Analytics and Infrastructure
Layered Measurement
| Layer | Tool | Purpose |
|---|---|---|
| Traffic | Google Analytics (GA4) | Page views, acquisition channels, user demographics |
| Behaviour | Microsoft Clarity | Session recordings, heatmaps, rage clicks |
| Funnel | Custom FormAnalytics.svelte | Get Started flow progression, drop-off points, suburb interest |
| Page-level | PageAnalytics.astro | Per-page engagement metrics |
| Privacy | CookieConsent.svelte | GDPR/Privacy Act compliance, consent management |
Deployment
Cloudflare Pages via Wrangler CLI, with edge functions configured through wrangler.toml. Sanity Studio embedded at /studio — content editors access the CMS at the same domain, no separate admin URL. Builds triggered by content changes via Sanity webhook.
Technical Specifications
| Category | Detail |
|---|---|
| Framework | Astro 5 (Static Site Generation) |
| Interactive layer | Svelte 5 (Runes: $state, $derived, $effect) |
| CMS | Sanity CMS v5 (embedded studio at /studio) |
| Animation | GSAP + ScrollTrigger (27KB, scroll-wipe hero) |
| Styling | Tailwind CSS v4 |
| Typography | Clash Display (headings) + Inter (body) |
| Hosting | Cloudflare Pages + Edge Functions |
| Maps | Google Maps API (per-client Cloud project, isolated free tier) |
| Blog | MDX with 7 custom components |
| Analytics | GA4 + Clarity + custom form analytics |
| Pages | 14 core + 171+ dynamic suburb + blog |
| Project data | Typed JSON schema + TypeScript helpers |
The Result
A modular construction platform where every architectural decision serves a specific business purpose. The GSAP hero creates premium positioning within seconds. 171+ suburb pages capture the long-tail search traffic that a manual approach could never maintain. The typed project system ensures portfolio data is consistent and validated at build time. The qualification flow filters leads before they reach the sales team.
- 171+ suburb landing pages generated from structured data, each with localised eligibility content and Google Maps integration
- 2-phase GSAP scroll-wipe hero with ScrollTrigger, stat overlays, and custom cursor — accessible with reduced-motion fallback
- Typed project system with JSON schema, TypeScript interfaces, 5 helper functions, and Svelte 5 gallery cards
- Sanity CMS with embedded studio, MDX blog with 7 custom components, and automatic homepage article feeds
- Lead qualification funnel with suburb eligibility check, form analytics instrumentation, and quick capture alternative
- Design system — Clash Display + Inter typography, terracotta/walnut/cream/sage palette drawn from construction materials
- Full analytics stack — GA4, Clarity session recordings, custom form funnel tracking, cookie consent management
- Edge-delivered via Cloudflare Pages with isolated Google Maps API project per client
- Zero server maintenance — 171+ pages are static HTML, no Node.js runtime, no database, no security patches
Screenshots




Tech Stack
Project Highlights
Scroll-Wipe Hero
GSAP ScrollTrigger powers a two-phase hero: a project render with key stats pins on scroll, then wipes away to reveal brand messaging with badge animations. Custom cursor tracks across both phases.
2-phase animationDynamic Suburb SEO
171+ suburb landing pages generated from a structured data file at build time. Each page includes localised content, eligibility data, and Google Maps integration — targeting the long-tail of 'modular homes [suburb]' searches.
171+ pagesTyped Project System
Projects defined in a JSON schema with TypeScript types, helper functions, and responsive image pipelines. Gallery cards render from data with stats formatting, partner credits, and specification details.
Typed JSON + TS helpersBlog with MDX Components
Sanity-powered blog with custom MDX components — callouts, comparison tables, key stats blocks, and article CTAs. Latest articles feed automatically into the homepage.
Form Analytics & Lead Capture
Get Started eligibility flow with form analytics tracking, quick lead capture components, and cookie consent management. Every interaction is instrumented.
Full funnel trackingAutomated Deployment
Cloudflare Pages with Wrangler CLI deployment, edge functions support via wrangler.toml, and a Sanity Studio embedded at /studio for content editing.
Edge-deliveredReady to build something like this?
Let's discuss your project requirements and how we can help.
Start a Project