Concept & direction
The brief was a home-services / handyman / renovation company — a category that's almost always styled in generic corporate blue-and-white with stock-photo drills and clip-art hammers. The direction chosen here goes the other way: warm, tactile, organic-modern craftsmanship. The reference points were boutique renovation studios and Dwell-style architecture editorial, not contractor-directory templates.
That meant three commitments going in:
- A palette pulled from the materials themselves — clay, oak, moss, linen — instead of a "trust blue" arbitrarily assigned to a trade business.
- A headline serif with actual character (Fraunces), not a geometric sans that reads like every SaaS landing page from the last five years.
- A real, well-built before/after interaction, since that's the one home-services trope worth keeping — it's the single most persuasive piece of content this kind of business can show, so it deserved custom engineering rather than a plugin embed.
Palette & type
Every color in the system is named after the material it's standing in for, defined once as CSS custom properties in css/style.css and referenced everywhere else — no hard-coded hex values in component rules.
Typography pairs Fraunces (a warm, soft-serif display face with real optical-size character — used italic for accent words like "ground" and "life") against Work Sans, a clean humanist grotesque for body copy, labels, and UI chrome. The pairing does the classic job of a characterful display face against a quiet workhorse body face, but the specific choice of Fraunces over something like Playfair or Canela is deliberate: Fraunces leans rounder and warmer at the terminals, closer to hand-cut lettering than a fashion-magazine serif, which matches "craftsmanship" better than something more austere.
Craftsmanship that holds its ground.
Work Sans — Kitchens, baths, decks, repairs, and paint, built with real materials and honest timelines.
Asset generation
All photography on the site is AI-generated via the Higgsfield MCP, using the nano_banana_pro model at 2K resolution, then re-encoded to compressed JPEGs (resized with macOS sips) to keep the asset folder light — the eight source PNGs totaled roughly 76 MB; the shipped JPEGs total about 3 MB. Every prompt was art-directed specifically for this palette: golden-hour or soft window light, the same clay/oak/moss materials called out by name, and an explicit "no people, no text" instruction to keep the images usable as clean backgrounds and card imagery.
Each of the eight images — the hero exterior, the before/after kitchen pair, the four service shots, and the process detail — was generated from a carefully art-directed brief specifying the lighting setup, lens character, palette, composition, and mood, then iterated until it sat naturally inside the brand system. The full prompt library behind the set is proprietary to the studio and available to clients on request.
Note on the before/after pair: the two kitchen images were generated separately rather than as a true single-source edit, so the camera angle and cabinet layout are close but not pixel-identical. For a production client build, the more honest approach is a real photographed before/after pair, or an image-to-image edit pass that preserves the exact room geometry — flagged here rather than hidden.
Build techniques
Plain HTML/CSS/JS, no build step. Structure lives in index.html, all styling in css/style.css using CSS custom properties for the full palette, type stack, spacing, and easing curves — so the whole visual system can be re-themed by editing the :root block once.
- Fluid type & spacing with
clamp()— the hero headline, section headings, and hero vertical rhythm all scale withclamp(min, vw, max)rather than fixed breakpoint jumps, so type never explodes on ultra-wide monitors or collapses on small laptops. - CSS Grid for the services grid and process rail, collapsing from 4 → 2 → 1 columns via
@mediaqueries rather than a JS masonry library. - Film-grain texture via an inline SVG filter (
feTurbulence+feColorMatrix) as a fixed, low-opacity,mix-blend-mode: multiplyoverlay across the whole page — a texture cue that reinforces the "tactile materials" concept without shipping a single grain image asset. - Context-aware header — the nav sits transparent with light text over the dark hero photo, then swaps to a blurred cream background with dark ink text once scrolled, via a
ScrollTrigger-driven class toggle rather than a scroll-position hack in plain JS. - Semantic, accessible markup —
<article>for service and testimonial cards, real form<label>s, arole="slider"with keyboard support on the before/after comparison, and descriptivealttext on every generated photo.
The before/after slider & motion
The centerpiece interaction is a hand-built draggable comparison slider (js/main.js), not a plugin. Two full-bleed images stack in a fixed-aspect-ratio frame; the "before" image sits inside a wrapper whose width is driven by pointer position, clipping it against the "after" image underneath. A pointer handle with a brass-toned grip icon tracks the divider line. It supports mouse drag, touch drag, click-to-jump anywhere on the frame, and full keyboard control (arrow keys move the divider in 5% steps) with proper role="slider" and aria-value* attributes. On first scroll into view it runs a one-time automated "sweep" from 12% → 68% → 50% so visitors immediately understand it's interactive before they've touched it.
Beyond the slider, the motion system (GSAP 3 + ScrollTrigger, loaded from CDN) is built from a handful of techniques chosen to match the brand rather than default "fade up on scroll" everywhere:
- Line-mask headline reveal — each line of the hero headline sits inside an
overflow: hiddenwrapper and animates up fromtranslateY(120%), a cleaner, more editorial entrance than a plain opacity fade. - Parallax hero image — the hero photo scales and drifts slightly on scroll via a scrubbed
ScrollTriggertween, giving the top of the page a sense of depth. - Count-up stats — the "14 years / 360 projects / 4.9 rating" row animates from zero using a tweened proxy object rather than static numbers.
- Magnetic tilt on service cards — a lightweight
mousemovelistener rotates each card a few degrees toward the cursor in 3D, released with an elastic-out return onmouseleave— a small, tactile detail meant to feel like handling a physical card, not a UI element. - Infinite marquee strip — a duplicated flex track scrolling at a constant linear speed lists every service, a nod to painted contractor-van signage rather than a generic logo carousel.
Iteration passes
After the first complete build, the site went through three explicit fresh-eyes review passes — each one looking specifically for spacing problems, weak type pairing, color balance, generic-feeling animation, responsive breakage, contrast issues, and any remaining "template" tells. The short version:
- Pass 1 caught a real above-the-fold bug: the hero headline was sized for wide monitors only and pushed the CTA buttons and stat row off-screen on common 1280×720–800 laptop viewports; also found the header nav rendering dark text over the dark hero photo before scroll, killing contrast. Both fixed — hero type scale and vertical rhythm now use tighter
clamp()ranges, and the header swaps between light and dark text states based on scroll position. - Pass 2 reviewed color balance and rhythm section-by-section (services → showcase → process → testimonials → contact → footer), checked the mobile stack at 375px for overflow and grid collapse, and confirmed the before/after slider's drag math and keyboard support behaved correctly under simulated pointer events.
- Pass 3 did a detail sweep — hover/focus states, icon consistency, footer credit-line styling, and a final pass on alt text and semantic structure for accessibility.
Deployment
This is a static site with zero build step — no bundler, no package.json, no server-side code. It deploys as-is:
- Push the
anchor-home/folder to a Git repository (or drag-and-drop the folder directly). - Connect the repo in Netlify (or drag-and-drop deploy for a one-off), with the publish directory set to the folder root — no build command is needed since there's nothing to compile.
- Netlify serves
index.htmlat the root andguide/index.htmlat/guide/automatically from the folder structure — no redirects or rewrites required. - Point a custom domain at the Netlify site and Netlify issues the TLS certificate automatically.
Fonts load from Google Fonts and GSAP loads from the jsDelivr CDN at runtime, so there are no local font or script dependencies to manage post-deploy.