Build notes · industry demonstration
A bulk landscape supplies yard, priced by the cubic metre
Gnangara Sand & Soil is a fictional Wangara sand-and-soil yard selling brickie's sand, screened topsoil, jarrah mulch, blue metal and crushed limestone to builders, landscapers and anyone doing their verge on a Saturday. The page has one job: turn "how much do I need and what's it going to cost" into a phone call with the volume, the product and the delivery already named. Everything on it is drawn — no photographs, no stock imagery, no build step.
The signature — the Load
The centrepiece is an SVG drawing of the yard apron in elevation. Five bunkered stockpiles sit at the back with the selected one lit and the rest dropped to 34% opacity; a loader arm enters from the top-left with its bucket tipped, trickling product; a tipper body is drawn in section on the weighbridge and fills to the selected volume; and a one-cubic-metre cube is outlined on the apron in the foreground at a constant scale on every state, so the volume always has an honest reference.
Three controls drive it. The product chip re-colours the lit pile, the bucket load, the falling grains and the fill. The volume stepper raises the fill — the body holds 6 m³ at full, and a second tipper body draws in beside the first at 7 m³ and above, filling proportionally with the remainder. The delivery chip swaps the vehicle: on pick-up the tipper is replaced by a 6×4 box trailer and a tally of trips appears on the apron.
The price badge is drawn as a weighbridge docket — a paper slip clipped to the scene, carrying the total set enormous, the volume and product beneath it at roughly a quarter of the size, the delivery line, any active notes, and the two standing declarations. It sits on the scene above 640 px and docks directly beneath the drawing below it.
Keeping the docket off the drawing at every width
The docket is an HTML overlay in the same CSS grid cell as the SVG, so the card's height is simply the taller of the two and a long docket never overflows. Nothing is drawn to the right of scene x 800, and the viewBox width is recomputed on resize as max(1080, 800 ÷ (1 − docketFraction)) — as the docket takes a larger share of a narrower card, the drawing's coordinate space widens to match, so the badge always lands over empty apron rather than over the truck, the bucket or the reference cube. The SVG is bottom-aligned in its cell and the card's background is the sky's top colour, so any surplus height reads as more sky rather than as a gap.
Drawn caps, and the counts they can't draw
- Two tipper bodies = 12 m³ drawn. The stepper maxes at 10 m³, so every deliverable value is fully drawable and the truck path never needs an overflow count.
- Six trailer marks on the pick-up path. One mark is one trip. A 6×4 trailer safely carries about half a cubic metre of sand, so 10 m³ is twenty trips — well past what the apron can draw. Above six marks the scene draws six and prints the honest count beside them ("20 TRIPS (6 SHOWN)"). The minimum reachable trip count is two, so the singular never appears.
- The 1 m³ cube never scales. It is the constant reference mark against a fill that changes on every step.
The pricing model
Every figure is GST-inclusive, plausible for the Perth market, and computed in the page rather than written into the copy. Product is priced per whole cubic metre; delivery is priced per load, not per metre — one tipper load carries up to six.
| Line | Rate | What it multiplies |
|---|---|---|
| Brickie's sand | $48/m³ | the actual cubic metres |
| Crushed limestone | $58/m³ | the actual cubic metres |
| Screened topsoil | $62/m³ | the actual cubic metres |
| Jarrah mulch | $78/m³ | the actual cubic metres |
| Blue metal 10 mm | $96/m³ | the actual cubic metres |
| Northern corridor delivery | $90/load | the load count, ⌈volume ÷ 6⌉ |
| Perth metro delivery | $140/load | the load count, ⌈volume ÷ 6⌉ |
| Beyond the metro | Quoted | one load only, cartage quoted on the run |
| Pick up at the yard | Free | nothing — the trailer is yours |
Two rules shape the stepper. Every delivery zone carries a 2 m³ minimum — a forced floor, not a billed minimum, so the product line always multiplies the actual volume and there is never a phantom charge. Beyond the metro is capped at 6 m³, one load and one run. The stepper keeps a single memory slot holding the last volume the visitor set explicitly: chipping a zone that invalidates it forces the nearest valid value and remembers the old one, the memory is restored the moment it becomes valid again, and any explicit step while the value is forced clears the memory for good. On a partial unwind — the constraint relaxes but the remembered value is still invalid — the forced value holds.
| Scenario | Arithmetic | Total |
|---|---|---|
| Rest state — jarrah mulch, 4 m³, northern corridor | 78 × 4 = 312, + 90 × 1 | $402 |
| Brickie's sand, 1 m³, picked up | 48 × 1, no delivery | $48 |
| Blue metal, 7 m³, Perth metro | 96 × 7 = 672, + 140 × 2 = 280 | $952 |
| Screened topsoil, 10 m³, northern corridor | 62 × 10 = 620, + 90 × 2 = 180 | $800 |
| Crushed limestone, 5 m³, beyond the metro | 58 × 5 = 290, cartage quoted | $290 + quoted |
| Jarrah mulch, 2 m³, Perth metro | 78 × 2 = 156, + 140 × 1 | $296 |
| Crushed limestone, 10 m³, picked up | 58 × 10, twenty trailer trips | $580 |
| Brickie's sand, 6 m³, northern corridor | 48 × 6 = 288, + 90 × 1 (the exact one-load boundary) | $378 |
The cheapest output the estimator can produce is $48 — one cubic metre of brickie's sand, picked up — which is exactly the floor printed in the products grid. The grid and the calculator cannot disagree, because both are the same five rates.
Palette
Argued from the material: a washed-concrete apron, screened loam, jarrah mulch and yard olive. It deliberately avoids the cream-and-terracotta default — the ground is a cool grey concrete rather than a warm cream, and the accent is a product colour off the mulch bay, not decorative terracotta. Every ratio below was measured with WCAG arithmetic from the shipped tokens.
#E8E7E1Page ground. The apron slab, hosed down and dried out.#FDFDFBCards and the weighbridge docket. Hairlines #D4D3CC.#231F1CBody ink — 13.20:1 on the ground, 16.06:1 on cards, 10.89:1 on the hairline tint. Primary button fill under white labels at 16.35:1 (hover #171310, 18.47:1).#8E3B1EMarks, rules, chip outlines, the Sapience band. 6.07:1 as text on the ground and 7.38:1 on cards, so no separate ink was needed; white on it measures 7.51:1 (hover #743017, 9.64:1).#5E6B4AScene fill only — the loader arm, the weighbridge plate, the dust drift. 4.61:1 on the ground, so it never sets small text and never labels the map; #46503A (6.86:1) does that.#56504ASecondary copy and unlit bay labels. 6.42:1 on the ground, 7.80:1 on cards.The products colour the drawing
Each stockpile, bucket load, falling grain and truck fill takes its own product colour, and each carries a pinned mark colour so the graduation ticks never vanish into the fill: brickie's sand #D9B871 with loam marks (8.60:1), screened topsoil #4A4038 with ground marks (8.14:1), jarrah mulch #8E3B1E — the accent itself, so its marks are the ground colour rather than the accent (6.07:1) — blue metal #5C6470 with white marks (5.98:1), and crushed limestone #DCDAD0 with loam marks (11.67:1). Product names never sit on the product fill: they sit on the concrete below the bay with a leader line to the pile.
Type
Big Shoulders Display
A condensed industrial face, argued from yard signage, bin-wall stencils and the lettering sprayed on a bunker wall. It carries the headline, every price, the docket total, the section heads and the worn stencil painted on the apron. Set in caps with tight leading and a little tracking, it is the whole personality of the page.
Source Sans 3
The workhorse: body copy at 16.5 px with tabular lining numerals so the ledger's dollar column never shifts as the figures change, plus every form label, FAQ answer and scene annotation. Legible beats expressive here — the reader is a builder checking a number on a phone.
The typographic bravery is the docket: the total set at up to 76 px in condensed 800 weight with the volume and product stacked beneath it at roughly a quarter of the size, printed on a paper slip laid over a drawing of the yard. It is the biggest thing on the page and it is the only number that matters.
Techniques
- Hand-built SVG, no libraries. The apron, the bays, the loader, both tipper bodies, the trailer, the tally and the service-area map are all inline SVG. The bays and the graduation ticks are generated in JavaScript from the product table so there is exactly one source of truth for colour, rate and mark.
- Two clip paths do the filling. The fill rect grows from the floor of the body; a duplicate set of graduation ticks in the product's mark colour is clipped to that same rect, so the m³ marks appear to light up as the load rises while the ticks above the line stay a neutral grey.
- One rAF loop. Eighteen ellipses trickle from the bucket lip to whichever vehicle is parked below, and a dust drift crosses the apron. The delta is clamped at zero as well as capped (
min(max(now − last, 0) ÷ 1000, 0.05)) so a vsync timestamp that precedes the scheduling call can never drive a negative elapsed time into the geometry. The loop pauses onvisibilitychangeand the grain opacity never drops below 0.60, so the signature is visibly alive at any capture instant without any phase arithmetic. - Dual-speed redraw. The first draw earns a staggered entrance (26 ms per scene group, capped at 1.5 s); every later state change redraws snappy (11 ms, capped at 520 ms) behind a
firstDrawflag, so tapping a chip reads as response rather than as lag. A draw token is bumped in each handler before the state swap, so a rapid burst of clicks settles exactly once. - No CSS animation on any SVG presentation attribute. Opacity is set as an inline style everywhere in the drawing and the
opacityattribute is never used, so a stylesheet rule can't silently discard a drawn element's own state. SVG groups are hidden withstyle.display, never thehiddenIDL property, and the page carries a[hidden]{display:none !important}guard. - State probe.
window.__landscapesuppliesexposes the scenario, the computed figures, the note flags, the drawn counts and asettledflag, so verification gates on state rather than on elapsed seconds.
Conversion decisions
- The price is inside the artwork. A ledger below the fold is invisible in a preview and on a phone. The docket puts the total on the drawing, and at 1200 px the whole signature — piles, loader, truck, docket, chips and ledger — sits inside the first 750 px alongside the headline and the call button.
- Two calls to action, one of them a phone number. Bulk supplies are bought by phone, at 6 am, from a ute. The number is in the header, in the hero, in the sticky bar at the bottom of every phone screen, in the callback promise beside the form and in the footer.
- The form asks four things and pre-fills the fifth. Name, mobile, delivery suburb and driveway access — access because it is the one question that decides whether a tipper can do the job at all, and asking it here saves a phone call. The load itself arrives pre-filled from the Load with the product, the volume, the zone and the estimate, and stops updating the moment the visitor edits it.
- Controls that can't move the price say why. At the 2 m³ delivered floor and at the beyond-metro ceiling the stepper button is disabled with the reason printed beneath it, and when the floor is holding a smaller remembered volume a note says so on all three surfaces. Nothing silently no-ops.
- The confirmation is local and honest. Submitting a valid form reveals a designed panel that names the load, the suburb and the estimate, promises a call back within one business hour, and says plainly that nothing was sent anywhere. There is no fake network round-trip, and the panel is hidden on load and after an empty submit.
Local SEO we'd implement for the real business
The live page emits only WebPage and BreadcrumbList structured data, because marking up a fictional yard as a real business — with fictional reviews and a fictional rating — would put dishonest data into a real index. For an actual client we would ship the block below, with their verified details, their real reviews feed and their real hours. GardenStore is the precise schema.org organisation type for a landscape supplies yard (a Store subtype); the bulk-supply specialty is named through additionalType, knowsAbout and a hasOfferCatalog of the products, which is what actually earns the "sand delivery near me" panels.
{
"@context": "https://schema.org",
"@type": "GardenStore",
"@id": "https://example.com.au/#yard",
"name": "Gnangara Sand & Soil",
"additionalType": "https://www.wikidata.org/wiki/Q1934969",
"description": "Bulk landscape supplies by the cubic metre - sand, soil, mulch, blue metal and crushed limestone, delivered across Perth's northern corridor.",
"url": "https://example.com.au/",
"image": "https://example.com.au/img/yard-apron.jpg",
"telephone": "+61 8 XXXX XXXX",
"priceRange": "$$",
"currenciesAccepted": "AUD",
"address": {
"@type": "PostalAddress",
"streetAddress": "Unit 0, Example Road",
"addressLocality": "Wangara",
"addressRegion": "WA",
"postalCode": "6065",
"addressCountry": "AU"
},
"geo": { "@type": "GeoCoordinates", "latitude": -31.79, "longitude": 115.83 },
"openingHoursSpecification": [
{ "@type": "OpeningHoursSpecification", "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"], "opens": "06:30", "closes": "16:30" },
{ "@type": "OpeningHoursSpecification", "dayOfWeek": "Saturday", "opens": "07:00", "closes": "12:00" }
],
"knowsAbout": ["brickie's sand", "screened topsoil", "jarrah mulch", "blue metal aggregate", "crushed limestone", "bulk delivery by the cubic metre"],
"areaServed": [
{ "@type": "City", "name": "Wanneroo" }, { "@type": "City", "name": "Joondalup" },
{ "@type": "City", "name": "Butler" }, { "@type": "City", "name": "Ellenbrook" },
{ "@type": "City", "name": "Ballajura" }, { "@type": "City", "name": "Osborne Park" }
],
"hasOfferCatalog": {
"@type": "OfferCatalog",
"name": "Bulk products by the cubic metre",
"itemListElement": [
{ "@type": "Offer", "itemOffered": { "@type": "Product", "name": "Brickie's sand" },
"priceSpecification": { "@type": "UnitPriceSpecification", "price": "48.00", "priceCurrency": "AUD", "unitCode": "MTQ" } },
{ "@type": "Offer", "itemOffered": { "@type": "Product", "name": "Jarrah mulch" },
"priceSpecification": { "@type": "UnitPriceSpecification", "price": "78.00", "priceCurrency": "AUD", "unitCode": "MTQ" } }
]
},
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.8", "reviewCount": "126" },
"review": [{
"@type": "Review",
"author": { "@type": "Person", "name": "Real verified customer" },
"datePublished": "2026-07-14",
"reviewRating": { "@type": "Rating", "ratingValue": "5", "bestRating": "5" },
"reviewBody": "Eight metres of brickie's sand on site by half seven, two loads, and the concrete truck never waited."
}]
}
Alongside it: a verified Google Business Profile with the yard's real hours and a Saturday-morning flag, product pages for each material carrying the UnitPriceSpecification in MTQ (cubic metres), and suburb-level delivery pages built from the real run sheet rather than spun.
What would change for the real yard
- Their photographs. Every pile, truck and apron here is drawn. A real yard has better assets than we could draw — the loader mid-lift, the bays at 6 am — and the drawing becomes the diagram beside them rather than the whole art direction.
- Order and pay online — Shopify. Bulk product sells well as a Shopify catalogue with the cubic metre as the unit and delivery zones as shipping rates; the Load becomes the configurator that fills the cart.
- The run sheet — MyTrucking or simPRO. The enquiry lands as a job with the product, volume, zone and access note attached, and the loads get dispatched against the actual truck diary instead of a whiteboard.
- Xero. Weighbridge dockets reconcile against invoices, and account customers (the fortnightly landscaper) get billed monthly rather than per load.
- Twilio. The single highest-value message a yard can send is "your load's on the truck, driver's twenty minutes away" — one SMS that removes most of the ring-backs.
- Google Business Profile. Reviews feed in from the profile rather than being written as demo copy, and the Saturday hours are the thing most often searched.
Iteration log
-
Pass 1 — Craft
The scene was fighting itself, and a dead regex
Screenshot review at native resolution caught four real defects that every state assertion sailed past. Bay labels collided: "Crushed limestone", "Screened topsoil" and "Jarrah mulch" overlapped each other under a 110-unit bay pitch — fixed by shortening the scene labels to trade shorthand and re-pitching the bays. The trickle crossed the bay row: grains fell from a bucket parked above the stockpiles, straight through the label band, which read as a mistake — the loader moved to a clear corridor left of the bays, tipping into the truck below it. The reference cube was drawn far too large against a six-metre body, so it was rebuilt at a plausible edge and moved clear of the apron stencil. The heaps were flat: a quadratic control point at y 288 puts the actual apex at y 344, so the piles read as puddles until the control moved to 208.
The zero-console assertion earned its keep: Chrome compiles
patternattributes asv-flag regexes, and the unescaped/in the driveway-access field's character class threw atcheckValidity— the field looked fine while its validation was a parse error.The biggest change was structural. The hero originally stacked the copy above a full-width apron, which pushed the truck, the piles and the fill below 750 px — the signature was technically in the preview and commercially absent from it. The hero became two columns above 1080 px with the drawing beside the headline, and the whole argument now sits in the first screen.
-
Pass 2 — Depth
Material honesty as the second read
The falling grains now take their shape from the product, not just its colour: fine round sand, chunkier limestone, small dark topsoil, hard even blue metal, and long flat jarrah flecks. Nobody notices it on the first visit; anyone who has shovelled the stuff notices it on the second. The fill rect gained a top edge in the product's own mark colour, which was the fix for crushed limestone reading as an empty body against the pale liner.
Two additions fill the apron without crowding the conversion path: a worn stencil painted on the concrete that changes with the delivery chip — WANGARA YARD, NORTHERN CORRIDOR, PERTH METRO, BEYOND THE METRO — and a pair of faint drive-lane tyre tracks running out of frame. The graduation ticks inside the body light up in the mark colour only below the fill line, so the load reads as a measured depth rather than a coloured rectangle. One ornament was cut here rather than in Pass 3: a weighbridge readout post standing mid-apron that read as a random signpost and meant nothing.
Driving the model against an independently reimplemented oracle confirmed every worked figure — $402 at rest, $48 at the floor, $952, $800, $290 + quoted, $296, $580, $378 — plus the four force drives: the 2 m³ minimum forcing 1 → 2 and restoring on the way back, the beyond-metro cap forcing 10 → 6 and restoring, the partial unwind that holds 2 because the floor still bans 1, and the memory invalidation where an explicit step under the cap clears the restore.
-
Pass 3 — Hardening
375 px first, then the label-collision sweep
The masthead was checked before anything else: the wordmark's right edge against the call button's left edge at 375 px, measured rather than eyeballed. Below 600 px the wordmark drops a size, the icon shrinks and the header button becomes "Call the yard" instead of the full number, which takes the row from an overflow to comfortable.
At phone widths the drawing crops to a taller frame and the bay labels change behaviour: only the lit pile is named, at more than twice the size, with its position clamped inside the frame and a short leader back to the pile. Five 6-pixel labels were unreadable; one 13-pixel label with a colour-swatched chip row directly beneath it is not. The docket docks below the drawing at 640 px and switches every string to its short form — "northern corridor", "2 loads", "6 trips by trailer", "we'll check your volume, free" — while the ledger and the screen-reader mirror keep the full templates at every width.
A native-resolution label-collision sweep at 375, 1000 and 1200 checked the docket against the truck and the bucket, the trip tally against the apron stencil, the reference-cube caption against both, and the stencil against the drive-lane tracks. Reduced motion parks the grains mid-fall and the dust mid-apron and renders a settled frame with the total still printed; the rAF loop pauses when the tab is hidden; every control has a visible focus ring; and the resize handler recomputes the viewBox so the docket keeps clear of the drawing at every width between 641 px and 1440 px.