CREWLINELabour Hire · build guide

Crewline Labour Hire — how this demo was built

Site 344 in the Generative Assets collection, Wave 37 (Industry Demos XXVI). A construction and civil labour-hire landing page for Perth, built as a B2B conversion page around one signature: the Crew Board.

Concept

Crewline Labour Hire is a fictional Welshpool labour-hire company supplying labourers, trades assistants, ticketed operators, qualified trades and leading hands to Perth builders, civil contractors and site managers — "ticketed crews, on site by seven". The audience is the site manager who lost two labourers on Friday, the civil contractor staffing a three-week pour, the shopfitter needing a TA for a fortnight. The page has one job: a crew request with head-count, roles, shift and days already priced, so the phone call is a confirmation rather than a negotiation.

Art direction is the site gate at 6:45am — slab-grey ground, bitumen ink, hi-vis orange, galvanised-steel linework — deliberately blunt and tabular, "a whiteboard at the site office with the crew written up". Every visual is procedural: the site scene, the crew figures, the whiteboard, the metro map are all SVG and CSS. No raster images.

Palette — measured, not assumed

Contrast ratios were measured from the shipped hex tokens with WCAG relative-luminance arithmetic in Pass 1 (a small Node script), not read off the spec. The spec's hypotheses held: bitumen labels on the base hi-vis orange measure 5.43:1, so the pre-minted deeper fill #D95A12 (4.27:1 with bitumen labels — worse, because the label is dark) was not needed and the base orange ships on every button. Orange copy on the ground fails by design (2.62:1) and never appears — every accent-coloured word uses orange-ink #A8420A.

Hi-vis on bitumen (the ledger's total, the sticky-bar call button) measures 5.43:1 — the same pair reversed. Light steel #C9CDD2 on bitumen (ledger sub-lines) measures 10.34:1.

Type

Barlow Condensed 700 for display, headings, the whiteboard and every number — the site-signage condensed that reads through a windscreen. Barlow 400/500/600 for body and controls. Scale is deliberate: hero h1 at clamp(2.55rem → 4rem), the display moment "FIVE ON SITE BY SEVEN, ALL WEEK" at clamp(2.2rem → 4rem) in condensed caps with the head-count word in orange-ink, section h2s at ~2–3.1rem, body 17px, mobile body never below 16px. The bravery is the live display line: the number word and the day phrase are rewritten by the board ("Twenty-one on site by seven, for four weeks"; "No one on site by seven, yet — add a crew").

The Crew Board

Techniques

Inline SVG (viewBox 720×440) for the site scene: slab in false perspective with a timber formwork edge and orange pegs, a site office with a CREWLINE sign, a crane jib with a swinging hook, a site gate with a mesh pattern fill, a forklift and a ute as toggled props. Crew figures are built at runtime with createElementNS — head, hard hat, vest with two reflective stripes, arms, legs, shadow — with role variants: a tool belt for TAs, an orange hat stripe for qualified trades, a white hat and clipboard for the leading hand, a forklift drawn beside operators. Each figure is TWO nested groups: an outer <g transform="translate()"> for slab position and an inner .fig group that CSS scales up from its feet — the W16/W17 rule that a CSS transform must never share an element with an SVG transform attribute. Figures fill 24 slots (4 rows × 6) front row first, so the first heads land at the formwork edge where the eye is.

The whiteboard is HTML laid over the artwork by CSS grid (grid-area:1/1 on both the SVG and the board) so the panel grows to whichever is taller; from 640px up it hangs at the top-left of the scene, below 640px it stacks under the scene and the SVG's viewBox is cropped by JS to the right-hand 444 units so the slab fills the phone width. The price badge is a two-line block on the board: line 1 the job total ("$11,920 · JOB TOTAL"), line 2 "= $2,384 a day". Zero heads with the leading hand off is a designed price-less state: the badge reads "add a crew to price it", line 2 is removed (not blanked), the board reads "— on site by seven", the ledger prints no dollar figure — never $0.

Dual-speed redraw (W21): the first draw staggers figures at 70 ms (1.5 s cap), every later change at 16 ms (500 ms cap). Every control handler bumps a drawToken BEFORE the state swap; the pop-in class and the settled timer both check the token, so a rapid-fire drive (labourers 0→8, shift 10, days 20 in one evaluate) settles at $92,800 with exactly eight figures and no in-flight residue. The crane hook swings by rAF with dt = min(max(now − last, 0)/1000, .05), amplitude 5.5° + 1.2° harmonic, always fully opaque (a never-invisible floor), pauses on document.hidden, and parks at 3° under reduced motion where the figures also appear without transition. Probe: window.__labourhire carries state, total, perDay, heads, priceless, note and settled.

Pricing model (per worker-hour, GST inclusive; super, workers' comp, insurances and PPE included)

LineRateNotes
General labourer$58/hrStepper 0–8. Four-hour minimum per worker per day → cheapest priced output $232 = the grid floor.
Trades assistant$66/hrStepper 0–4. Tool-belt figure.
Ticketed operator$78/hrStepper 0–4. Forklift drawn beside the crew while ≥1.
Qualified tradesperson$92/hrStepper 0–4. Hat-stripe figure.
Leading hand (toggle)$110/hrOne more head on the board; live at every combination. Clipboard figure.
Shift chips4 · 8 · 10 hr4 hr is the minimum call, stated on the chip and on-card. Rest 8 hr.
Days1–20Multiplies the crew hours AND the vehicle line. ≥10 days → NOTE only ("ten days and up: ask about our project rate"), no price change, no disable. Boundary: 9 no note, 10 note — both asserted.
Site vehicle & tools (toggle)+$95/dayUte with the crew's tools and a fuel card, × days. Live at every combination; drawn at the gate. With no crew on the board it stays priceless ("the ute comes with the crew").
Regional / FIFO / night / weekendno number"Quoted per site — award penalties applied as they fall." Designed price-less line on the grid, the ledger inclusion note and the FAQ.

Job total = (Σ heads × rate + 110 if leading hand) × shift hours × days + 95 × days if vehicle. Line 2 = total ÷ days — integer by construction because every line is per-day × days; totals display to the whole dollar.

Worked examples — measured from the running page against an independent oracle

Scenario (full control vector set explicitly)SpecMeasuredLine 2 / notes
Rest: 4 lab + 1 TA, 8 hr, 5 days, LH off, ute off$11,920$11,920= $2,384 a day · five figures · no note
2 operators only, 10 hr, 1 day$1,560$1,560= $1,560 a day · forklift drawn
1 tradesperson, 4 hr, 1 day$368$368= $368 a day
Rest + leading hand$16,320$16,320= $3,264 a day · six figures
Rest + vehicle$12,395$12,395= $2,479 a day · ute at the gate
Rest at 10 days$23,840$23,840= $2,384 a day · project-rate NOTE on board, ledger and a11y mirror
Rest at 9 days$21,456$21,456= $2,384 a day · no note anywhere
All steppers 0, LH off (± vehicle)price-lessprice-less"add a crew to price it" · line 2 absent · no "$0" in badge, board, ledger or mirror · zero figures, no ute
Leading hand alone, 4 hr, 1 day$440$440= $440 a day · one clipboard figure
Rapid-fire: lab 0→8, shift 10, days 20 in one evaluate$92,800$92,800= $4,640 a day · settled, 8/8 figures in
Cheapest priced: 1 labourer, 4 hr, 1 day$232$232= grid floor "from $58/hr — $232 a call"
Full board: 8+4+4+4 + LH, 10 hr, 20 days, uteoracle$305,500= $15,275 a day · 21 figures, all 24-slot rows used, note on

Conversion decisions

Local SEO we'd implement for the real business

The live page emits only WebPage / WebSite / BreadcrumbList schema naming it a demonstration — fictional business data must not enter a real index. For a real labour-hire client the page would carry this (values illustrative):

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "EmploymentAgency",
  "@id": "https://www.example-labourhire.com.au/#org",
  "name": "Crewline Labour Hire",
  "description": "Construction and civil labour hire across Perth — labourers, trades assistants, ticketed operators, qualified trades and leading hands.",
  "url": "https://www.example-labourhire.com.au/",
  "telephone": "+61 8 9000 0344",
  "email": "crews@example-labourhire.com.au",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Unit 0, 0 Example Road",
    "addressLocality": "Welshpool",
    "addressRegion": "WA",
    "postalCode": "6106",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.99, "longitude": 115.94 },
  "areaServed": [
    { "@type": "City", "name": "Perth" },
    { "@type": "AdministrativeArea", "name": "Perth metropolitan area" }
  ],
  "openingHoursSpecification": [
    { "@type": "OpeningHoursSpecification", "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"], "opens": "05:30", "closes": "21:00" }
  ],
  "priceRange": "$$",
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Labour hire charge-out rates",
    "itemListElement": [
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "General labourers" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "58", "priceCurrency": "AUD", "unitText": "worker-hour" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Ticketed operators" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "78", "priceCurrency": "AUD", "unitText": "worker-hour" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Qualified trades" }, "priceSpecification": { "@type": "UnitPriceSpecification", "price": "92", "priceCurrency": "AUD", "unitText": "worker-hour" } }
    ]
  },
  "aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "63" },
  "review": [
    {
      "@type": "Review",
      "author": { "@type": "Person", "name": "Client name (from the review feed)" },
      "reviewRating": { "@type": "Rating", "ratingValue": "5" },
      "reviewBody": "Six through the gate at 6:50 Monday after a Friday-night call."
    }
  ],
  "sameAs": [ "https://www.google.com/maps/place/…", "https://www.linkedin.com/company/…" ]
}
</script>

Alongside: a Google Business Profile category "Labour hire agency" with the depot pin, suburb-level service pages for the corridors the crews actually run (Kewdale/Welshpool industrial, Osborne Park, Wangara, Cockburn), a review feed pulled from Google, and the FAQ marked up as FAQPage only when the answers are the client's real ones.

What changes for the real client

Iteration log

Pass 1 — Craft

  • Measured every palette pair (table above). Base hi-vis with bitumen labels held at 5.43:1 → deeper fill unused; orange-ink used for every accent word; steel demoted to lines with steel-ink for captions.
  • First render put the whiteboard in a side column beside a squat scene — figures rendered tiny and the art sat too far down the hero. Recomposed: the whiteboard now overlays the top-left of the scene (grid-area stacking), the scene was redrawn with the office, slab, crane and gate composed to the right of the board's footprint, and the hero h1 was capped at 4rem so the art rises into the first 750px at 1200 (measured: art top ~270px, badge bottom ~535px).
  • Fixed a W16-class defect before it shipped: figures originally carried both a transform attribute and a CSS transform — split into an outer position group and an inner animated group.
  • Screenshot review of the service-area map caught Fremantle sitting in the ocean and a metro-shading polygon that didn't follow the coast — the coastline was redrawn as a straight-segment path, the metro shading re-cut to it, the Swan re-anchored at the new mouth.
  • Copy read: the eyebrow shortened to "The Crew Board — price it first" so it holds one line at 375; panel heading allowed to wrap its rate caption.

Pass 2 — Depth

  • Role variants on the figures (tool belt, hat stripe, clipboard, forklift companion) so the board reads at a glance; the ute appears at the gate when the vehicle toggle is on and never while the board is price-less.
  • Second-read detail: the whiteboard's day range rewrites itself ("Mon → Fri", "12 days · 2 wk + 2 days"), and the display line rewrites the head-count word and the day phrase (all week / for two weeks / for four weeks).
  • Mobile viewBox crop: below 640px the SVG shows only the right-hand 444 units so the slab and crew fill the phone width instead of leaving the board's desktop footprint empty.
  • Micro-interaction: the hook's swing is a two-harmonic sine (5.5° + 1.2°) so it never reads as a metronome; steppers disable at 0/1 and at their maxima; shift chips carry radio semantics.

Pass 3 — Hardening

  • 375: masthead measured (wordmark right edge clear of the icon-only call button; CTA inside 375); no horizontal overflow on either page; sticky call bar in the bottom 140px; badge tight variant below 640 and the badge never overlaps the scene's box at 375.
  • Harness (141 asserts, headless Chrome): every worked example with the full control vector set explicitly, both note boundaries (9/10 days), the price-less state with explicit line-2 ABSENCE and no "$0" anywhere, the $92,800 rapid-fire plus a second rapid-fire over toggles and chips, stepper clamps, keyboard Enter on a stepper, drive-then-hash liveness, empty submit (5 invalid fields, panel stays hidden) and filled submit (panel shown, echoes $11,920), reserved-phone sweep, JSON-LD allowlist on both pages, guide panel presence, zero console errors.
  • Reduced motion: hook parks at 3°, figures appear without transition, hero rise disabled. rAF paused when the tab is hidden. No backdrop-filter; header and sticky bar are solid.
  • Chanel rule: removed the decorative fence-line group left in the scene's first draft; the crane jib's hatching stays because it reads as structure.

← Back to the site · Generative Assets index