[ Parable build guide ]
How Lather was built
A four-chair barbershop, rendered without a photograph. The signature is a pure-CSS barber-pole whose diagonal stripes turn up the glass, paired with a keyboard-accessible booking board — both hand-built, no libraries, no images.
The idea
A barbershop sells two things a landing page usually fakes: the pole out front and the chair you sit in. So both are real here. The pole actually turns — a rotating-helix illusion done in CSS — and the booking board is a working widget you can drive with a keyboard, not a screenshot of one. Everything else stays out of their way: condensed signage type, tile-and-brass colour, a voice that's sharp and old-school.
The stack
Deliberately plain. One index.html, one styles.css, one
main.js. No framework, no bundler, no CDN, no image files — the pole is a gradient,
the barbers and bottles are inline SVG. Type is Oswald (condensed, the classic
barbershop signage face), Inter for body, and Space Mono
for everything a shop measures in — prices, durations, opening hours, slot times.
Signature technique — the barber-pole
A barber-pole is a cylinder wrapped in a diagonal helix. Spin it and the stripes appear to travel
straight up, forever. We fake the whole thing with one repeating-linear-gradient and
a moving background-position — no canvas, no JS:
.pole-stripes {
background: repeating-linear-gradient(120deg,
var(--brass) 0 18px, var(--cream) 18px 36px,
var(--teal) 36px 54px, var(--cream) 54px 72px); /* period = 72px */
animation: pole-turn 3.4s linear infinite;
}
@keyframes pole-turn {
from { background-position: 0 0; }
to { background-position: 0 -144px; } /* two periods = seamless loop */
}
The trick is the 144px. For a 120° repeating gradient with a 72px stripe period, moving
the background straight up by exactly two periods lands the pattern back on itself — so the loop is
invisible and the stripes seem to climb without end. A static soft-light gradient over
the top fakes the glass tube's curvature and a swept highlight; chrome end-caps top and tail it.
The same gradient, tilted and set moving sideways, becomes a thin divider rule between sections —
the pole as a recurring motif. Under prefers-reduced-motion both animations simply
stop and the stripes hold still.
Signature technique — the booking board
Columns are barbers, rows are 30-minute slots. The grid is built in JS from a barbers array, a
times array and a Set of pre-booked chairs — every cell is a real
<button>, so the whole board is tabbable and each slot carries a full
aria-label ("Book Marcus at 14:30 Thursday"). Selecting one clears the last, flips
aria-pressed, and rewrites a live summary line that reads like a real booking:
Marcus · Thu 14:30 — the Full Service. Booked chairs are disabled and
hatched; pick a service chip to change the tail of the summary.
Details that matter
- The pole is free. It's one animated gradient — no JavaScript touches it, so it
turns even if
main.jsnever loads. - Everything is keyboard-first. Chips, slots, the theme toggle and the reserve
button are all real buttons with
:focus-visiblerings and pressed states announced to screen readers. - No JS? Still bookable. The board's container holds a
<noscript>fallback with the shop's hours and phone number, so nobody hits a dead widget. - Reduced motion is honoured. The pole and the rule freeze, hero lines and scroll reveals resolve instantly, and counters snap to their final value.
- Content is never hidden without JS. The hero rise and reveals are gated behind
an
.jsclass set synchronously in<head>— kill JavaScript and the page is fully legible.
The booking board is a demo — it holds your pick on screen and confirms in-place, but sends nothing. Wire it to a real calendar (a scheduling API, a serverless function, an SMS provider) before taking actual bookings.
Ship it on GitHub Pages
Nothing to build. Push the folder and point Pages at the root.
gh repo create bswxyz/lather --public --source . --push
gh api --method POST /repos/bswxyz/lather/pages -f 'source[branch]=main' -f 'source[path]=/'
Relative paths and a .nojekyll file mean it serves from the project subpath without a
single config change. That's the whole point of the static build.