Crosscomb Barbers· build notes ← Back to the site

Generative Assets · Industry demo 279 · Barbershop

How the Crosscomb Barbers demo was built

A fictional two-chair barbershop on the Albany Highway strip, Victoria Park, built as a conversion demonstration for main-street personal-services businesses. The page has one job: put a visitor in a chair — build the service, see the honest maths, hold the slot.

Concept

Crosscomb Barbers sells twenty good minutes: the same cut every third Saturday, first cuts for kids, a beard lined properly. The audience is the customer first — a bloke on his phone deciding whether to book or walk in — so the signature element is the Chair Menu: cut chips and a beard toggle driving a procedural SVG fade chart (the real guard recipe), a Courier price ticket that does the arithmetic in front of you, and a demo day board that honestly greys the gaps your service won't fit.

Palette — oat & oxblood, argued from the shop

Deliberately not the banned cream-and-terracotta default: the ground is a warm oat (morning light on painted plaster), the red is a deep oxblood leather, the display face is a grotesque, and the marks are barber-pole geometry.

#F0EAE0 · shop oatGround — morning sun on painted plaster.
#2E241D · espressoStructure and primary ink — the walnut bench and the strop.
#7A2E2B · oxbloodLINE/MARK — chair leather, the pole's red turn: fade strokes, price rules, CTAs. Never body text on oat.
#9C7A3C · aged brassClipper housings and the till — marks, chip borders, the mirror frame.
#6B5122 · brass-inkDerived darker ink for small brass-role text (guard labels, eyebrows) — the raw brass fails 4.5:1 on oat, so type gets its own token.

Type

The signature: Chair Menu, fade chart & day board

The fade chart is a procedural SVG scene — brass-framed mirror, side-profile head in espresso line-work, and clipper passes generated in JS as short oxblood strokes between per-band y-extents, with guard numbers pencilled in brass-ink at each band (skin · 0.5 · 1 · 2 · 3 · scissor). Each cut re-renders the chart honestly: classic is scissor-over-comb with a 2 taper, the skin fade lays all five bands with a cross-hatched blend zone, buzz is one guard all over, kids is a soft 3 taper, seniors is the classic with the sides tightened. The beard toggle draws the jaw outline, its own guard work and a "1.5 cheek · 3 body" mark. Strokes draw in via stroke-dashoffset with a dual-speed stagger — 26 ms/element (capped 1.5 s) on first render, 11 ms (capped 520 ms) on every subsequent change — and a window.__barber probe carries the live state with a settled flag. The blend hatch breathes between opacity 0.58 and 1 (never below the visibility floor); reduced motion renders everything complete and static.

The day board is a 16-slot 15-minute lattice (9:00–13:00) with fixed demo bookings. A service needs ceil(minutes/15) consecutive free slots before close: a 60-minute fade-plus-beard fits only 11:30/11:45/12:00, while a 15-minute buzz opens every free gap — so the board visibly responds to the menu, and the greyed slots say why. Picking a slot writes it to the ticket and pre-fills the booking form.

Pricing model — pinned anchors, measured outputs

All prices are pinned anchors from the wave spec; the bundle rule is uniform: beard sculpt with any cut saves $6 — one chair, one gowning, one clean-up; the saving is real overhead, not marketing. Durations sum honestly and drive the board. Measured from the running page (independent oracle in the QA harness, every combination driven):

ServiceSolo+ beard ($22, 15 min)Bundle maths
Buzz cut · 15 min$25$41 · 30 min25+22−6
Kids u12 · 20 min$28— disabled"lollipop, not a beard service"
Seniors · 30 min$30$46 · 45 min30+22−6
Classic cut · 30 min$38$54 · 45 min38+22−6
Skin fade · 45 min$48$64 · 60 min48+22−6

The rest state (skin fade + beard) measured $64 · 60 min — exactly the spec's pure-anchor arithmetic, no corrections. The menu's cheapest output ($25 buzz) equals the services grid's "cuts from $25" floor. The kids rule follows LEARNINGS W18: a control with no honest meaning is disabled with its reason on the page, never left computing nonsense. The apprentice's $20 Tuesday price lives outside the menu as its own quiet line, stated as supervised and disclosed before you sit down.

Conversion decisions

Local SEO we'd implement for the real business

This demo page ships WebPage schema only — fictional structured data in a real index is dishonest markup. For a real barbershop client, we'd ship the full local pack (rendered here as a code sample, not live markup):

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BarberShop",
  "name": "Crosscomb Barbers",
  "image": "https://example.com.au/shopfront.jpg",
  "telephone": "+61 8 XXXX XXXX",
  "priceRange": "$25–$64",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "Albany Highway",
    "addressLocality": "Victoria Park",
    "addressRegion": "WA",
    "postalCode": "6100",
    "addressCountry": "AU"
  },
  "geo": { "@type": "GeoCoordinates", "latitude": -31.9762, "longitude": 115.9048 },
  "openingHoursSpecification": [
    { "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Tuesday","Wednesday","Thursday","Friday"],
      "opens": "09:00", "closes": "18:00" },
    { "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Saturday", "opens": "08:00", "closes": "14:00" }
  ],
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9", "reviewCount": "180"
  },
  "review": [{
    "@type": "Review",
    "author": { "@type": "Person", "name": "Real Customer" },
    "reviewRating": { "@type": "Rating", "ratingValue": "5" },
    "reviewBody": "Pulled from the live Google reviews feed."
  }]
}
</script>

Plus a Google Business Profile tuned for "barber victoria park" / "skin fade near me", service-level pages fed from the booking system, and review markup sourced from the real feed — never written by hand.

What would change for the real client

Techniques

Iteration log

Pass 1 — Craft

Drove every cut chip × beard state through an independently reimplemented pricing oracle: 92 asserts — all bundle sums ($54/$64/$41/$46), all durations (fade+beard = 60 min), board fits per service, ticket text, chart recipe per cut — all measured green, zero console errors. The screenshot review (the part the asserts can't do) caught three real defects: the beard guard labels collided with the fade band labels and their leader lines crossed the bands — moved to a single "beard · 1.5 cheek · 3 body" caption under the chin; the ear read as a floating "O" — redrawn larger with an inner helix line; the clipper passes were cramped scribble — lengthened to fill their band rows, jitter halved, columns aligned. Also fixed a malformed x attribute on the map's Como label.

Pass 2 — Depth

Added the mirrored window-lettering second read (first placed mid-mirror where it collided with the crown scissor marks — raised to the mirror rim), the ticket-total bump on every price change, and trimmed the two-line recipe caption to one. Drove the whole conversion path: fresh-load empty submit raised 4 invalid fields (fresh navigation per W15, since the signature pre-fills the form at rest — verified the pre-fill separately), filled submit rendered the local confirmation in the lexicon family with the demo honesty line; FAQ driven by dispatched click and by keyboard; the menu driven by keyboard (Enter selects, toggles); drive-then-diff liveness passed; subsequent redraws settled well under the dual-speed cap (measured <1.1 s wall-clock including the settle margin, vs 1.5 s+ entrance).

Pass 3 — Hardening

375px masthead first (W18/W21): wordmark vs tel-link edges compared explicitly, no collision, screenshot eyeballed. Full-page 375 sweep: scrollWidth 375, zero partially-overflowing elements, every section stacking cleanly. Reduced-motion emulation: settled immediately, pole animation off, zero undrawn strokes. Focus-visible outline verified on chips. Honest 7-second 1200×750 thumbnail capture: the fade chart sits mid-frame — the signature shares the thumbnail viewport with the hero (W19), asserted via bounding rect. Chanel rule: removed one of the map's minor road lines. Structural sweep: exactly one h1 per page, canonical + OG, WebPage-only JSON-LD, reserved-phone positive sweep (every (08) 9xxx number on the 9000 exchange), date min = today.

Honesty rules

Crosscomb Barbers is fictional and the page says so plainly in the colophon. The phone number uses the reserved (08) 9000 0279 pattern; the address is suburb-level; the ABN is visibly placeholder. Reviews are demo copy, never structured data. Every price on the page is one internally-consistent model, measured from the running code. The one branch not exercisable headlessly is nothing — every interactive branch on this page (chips, toggle, disable rule, slots, form, FAQ) is driven in the harness.