Drysline — build guide

Generative Assets · Industry Demos XXIII · site 328

The section drawing that sells the membrane.

Drysline Waterproofing is a fictional Myaree wet-area and balcony membrane specialist. The page has one job: a booking with the wet area named and priced. This guide documents how it was designed, priced and hardened.

Concept

Waterproofing is invisible once it's done — the whole trade lives under someone else's tiles. So the page puts the hidden thing on display: a technical cross-section at bench height, drawn the way a section appears on a drawing set, with the price badged directly on the membrane layer. The audience is renovators mid-strip, owners with a balcony staining the ceiling below, and builders sequencing a tiler for Thursday. The page should read like a section drawing that happens to sell.

Palette — argued from slate, primer and the membrane tin

The wave's dark site: a workshop-lit drawing board, not a moody landing page. Every contrast figure below is measured from the shipped tokens with WCAG relative-luminance arithmetic (spec figures were hypotheses; Pass 1 measured them).

#1C232B wet slatePage ground — a slab still dark from the cure.
#EAEEF2 primer whiteBody ink. Measured 13.60:1 on slate, 12.25:1 on panels.
#4E8ED9 membrane blueThe liquid — coats, marks, chips. 4.68:1 on slate; button ink #10161D sits at 5.37:1 on it.
#8A97A5 screed greyLayer callouts, fall arrows, dimension rules — lines and marks, plus labels only on grounds where it clears 4.5:1.

Working tokens derived during the build: #7FB2E8 as the blue text ink (7.12:1 on slate, 6.42:1 on panels — the base hue is reserved for marks and fills), the pre-minted W33 pair #2F66AC as the badge/button fill under white labels (5.81:1 measured), and #9AA7B4 as a label ink minted in Pass 1 for the control boxes, where screed grey measured 4.23:1 on the lighter #2A3441 ground (the new token measures 5.14:1). Badge sub-copy was lightened from #C9DCF2 (4.15:1) to #E2ECFA (4.87:1).

Type

Archivo 650 for display — the confidence of a drawing title block, used for the wordmark, headings, prices and the sealed-area display line. Instrument Sans as the workhorse: neutral, legible at 16px+ on mobile, and comfortable in dense ledger rows. The typographic moment is the seal line — the selected area written out in words ("TWELVE SQUARE METRES, SEALED") over the cutaway, re-set as the stepper moves.

Signature — the Wet-Area Cutaway

A layered SVG cross-section built entirely in code, no raster anywhere. Each scene (shower recess, full bathroom, balcony) is a builder function returning ordered layers: substrate hatch, wall stubs, screed-to-fall with the outlet, translucent primer, bond-breaker fillets, then two membrane coats drawn as stroked paths, then the ghosted tile finish, callouts, the plan inset (balcony only — it resizes with the area stepper), and the price badge sitting on the membrane layer inside the thumbnail's first 750px.

The membrane coats paint on with a stroke-dash reveal (butt caps, stroke hidden until drawn length > 0 — the W15 round-cap dot lesson). A roller on a requestAnimationFrame loop repaints a fresh highlight pass on an 8.6s cycle: 6.2s painting, 1.4s hold at full length, 1s lift-off where opacity eases 0.9 → 0.55 — a never-invisible floor, so the 7-second thumbnail settle always catches the scene mid-work. The rAF delta is clamped at both ends (dt = min(max(now − last, 0)/1000, .05), the W29 rule). Redraws are dual-speed: the first render gets the full entrance choreography (130ms stagger, 1.3s cap), every later chip/stepper change redraws snappy (36ms stagger, 430ms cap). Every control handler bumps an animation token before the state swap, so an in-flight entrance can never index a stage array that no longer exists (the W25 race). State is exposed at window.__waterproofing — scenario, computed figures, and a settled flag QA gates on.

Second-read details: the drawing title block bottom-left ("DW-328 · BALCONY SECTION · 1:20 · AS 3740") re-titles itself per scene, and a droplet drips into the balcony outlet on a loop — position set on a parent <g transform>, animation on the child circle only, keeping SVG attribute state and CSS animation on separate elements (the W16/W17 split).

Honest pricing

Every figure is pinned from WA-market anchors and the estimator's cheapest output equals the services grid's matching floor. The clamp has one semantic, stated in the meter's own unit: minimum 8 m², billed at the selected balcony rate — membrane alone at $120/m², or the combined $176/m² when strip-out is on.

Pinned anchors and measured outputs
ScenarioArithmeticDisplayed
Rest — balcony 12.0 m², strip off, flood off12 × $120$1,440
Balcony 12.0 m² + strip-out12 × ($120 + $56)$2,112
Balcony 12.0 m² + flood test12 × $120 + $240$1,680
Full bathroom + flood test$1,150 + $240$1,390
Shower recess aloneflat$680
Boundary — balcony 8.0 m²8 × $120, no note$960
Boundary — balcony 7.5 m²billed at 8 m², note shows$960
Boundary — 7.5 m² + strip-out8 × $176, note shows$1,408

All eight scenarios were verified twice: driven on the running page through the real controls, and recomputed by an independently reimplemented oracle in the QA harness. The strip-out toggle is disabled with its reason on-page for shower recesses and full bathrooms ("wet-area strip-outs are quoted after inspection — the membrane price holds"), the flood test is flat $240 at every job type and says so on-card, and planter boxes carry a designed price-less line — "quoted after inspection — planters fail in their own creative ways" — because any printed number there would be the dishonesty.

Conversion decisions

Local SEO we'd implement for the real business

This demo page ships only WebPage and BreadcrumbList JSON-LD naming itself a demonstration — fictional structured data in a real index would be dishonest markup. For a real Drysline we would ship HomeAndConstructionBusiness (waterproofing has no exact schema.org subtype — this is the correct parent) plus a genuine review feed:

{
  "@context": "https://schema.org",
  "@type": "HomeAndConstructionBusiness",
  "name": "Drysline Waterproofing",
  "description": "Licensed wet-area and balcony membrane specialists.
   Installed to AS 3740, flood-tested on request, certified before tiling.",
  "telephone": "+61-8-XXXX-XXXX",
  "address": {
    "@type": "PostalAddress",
    "addressLocality": "Myaree",
    "addressRegion": "WA",
    "postalCode": "6154",
    "addressCountry": "AU"
  },
  "areaServed": "Perth metropolitan area",
  "openingHours": "Mo-Fr 06:30-16:00",
  "priceRange": "$$",
  "review": {
    "@type": "Review",
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "author": { "@type": "Person", "name": "— real customer —" },
    "reviewBody": "— pulled from the live review feed —"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9",
    "reviewCount": "— live count —"
  }
}

Plus suburb-level service pages, GBP with real job photography (membranes photographed before tiling are unusually good proof-of-work content), and review capture wired to the certificate email.

What changes for the real client

Their photos replace nothing — the cutaway stays, because it explains the job better than photography can — but their certificate gallery, their reviews feed, and their booking system replace the demo stubs. For a trade this shape we'd wire the form into AroFlo or ServiceM8 (job cards, scheduling, the certificate PDF attached to the job), and invoice through Xero. The estimator's anchors become a config the operator can edit when the membrane price moves.

Iteration log

Pass 1 — Craft

Measured every text token against its real ground: screed grey on the control-box ground #2A3441 came in at 4.23:1 — minted #9AA7B4 label ink for that surface (5.14:1). Badge sub-copy #C9DCF2 on the badge fill measured 4.15:1 — lightened to #E2ECFA (4.87:1). The drawing title block's #5E6C7B annotation sat at 3.22:1 on the well — lifted to screed grey (5.82:1). Bumped the seal line's max size from 1.55rem to 1.72rem so the display moment actually displays, and removed a dead .is-off rule.

Pass 2 — Depth

Gave the seal line a 160ms crossfade when the stepper re-sets it (token-guarded so rapid-fire chip taps can't interleave two pending swaps), and added the outlet drip — a droplet falling into the balcony drain on a 2.7s loop, position on the parent group, animation on the circle only. Verified the roller's lift-off floor by reading the driven opacity: it eases 0.9 → 0.55 and never below, so no capture instant shows a dead scene. Confirmed the dual-speed redraw: first draw settles in ~2.6s of choreography, every subsequent chip/stepper redraw settles under 800ms.

Pass 3 — Hardening

375px: masthead holds one line (wordmark right edge vs phone link left edge compared explicitly), the sticky call bar is present with 48px targets, and neither page scrolls horizontally. Drove the full oracle matrix through the real controls — all eight scenarios matched the independent reimplementation, including the 7.5 m² + strip-out cell billing 8 × $176 with the minimum note showing and the 8.0 m² cell showing no note. Empty submit raises validation with the confirmation panel still hidden; a filled submit via the real submit button reveals it. Rapid-fire drive (chip–stepper–toggle bursts in one evaluate, twice over) produced zero console errors — the handler-side token invalidation holds. Reduced motion renders the cutaway settled at 62% roller progress with all layers visible. Chanel rule: the dead CSS rule went, and the balcony scene's door-threshold micro-label was already marked minor (dropped on compact screens) rather than adding another callout.

Techniques