Bouldering gym — pick a problem and watch its holds light the line up an SVG wall, filtered by grade.
Live demo · Build notes · More templates
Use this template — copy the source into a new project:
npx degit bswxyz/crux my-appA design-showcase website template from Parable: a fictional bouldering gym in an old basalt works. Chalky, communal, stoked-but-grounded — grades and beta talk, welcoming to first-timers.
The signature: a trace-a-route climbing wall. The hero is an inline-SVG wall studded with this week's problems, V0–V7. Pick one and its holds latch on in climbing order — mat to top-out — while a line traces the route through them and every other hold falls back into the plywood. Start and finish holds get the setter's dashed tape ring. Fully keyboard accessible; honest under reduced motion (whole line lights at once, no sequence).
| Token | Light (default, "chalk") | Dark ("basalt") |
|---|---|---|
| Background | #f3f1ec chalk |
#14171c basalt |
| Ink | #14171c |
#f3f1ec |
| Accent | hold-orange #ff6a3d (#bb3d12 as text on chalk) |
#ff6a3d |
| Secondary | volcanic #6a3b2a (background wash) |
same |
- Type: Archivo Black (display) · Inter (body) · Martian Mono (grades, times, tape labels)
- Easing: "the mantle"
cubic-bezier(.17,.89,.32,1.01)for reveals; "the dyno"cubic-bezier(.26,1.45,.42,1)— overshoot, then latch — for holds and buttons - Theme toggle persists via
localStorage(crux-theme); tokens flip on:root[data-theme]
- Vite + vanilla TypeScript — no framework, no runtime deps
- All art is inline SVG (the wall, the brand mark) — zero image files
- Hold positions live in the SVG markup;
src/wall.tsreads them from the DOM, so the wall renders complete without JavaScript
npm install
npm run dev # dev server
npm run build # tsc --noEmit && vite build → docs/
npm run preview # serve the production buildBuilt output lands in docs/ (with base: '/crux/') so GitHub Pages can serve it straight
from the branch — Settings → Pages → deploy from main / docs.
index.html all sections + the SVG wall (holds carry data-route/step/x/y)
styles.css design tokens (both themes) at top, then components
src/main.ts theme toggle, reveals, counters, grade filter, demo form
src/wall.ts TraceWall — the signature flourish
src/routes.ts the beta: names, grades, setters
src/reveal.ts IntersectionObserver reveals + counters
public/guide/index.html "How Crux was built" (self-contained, copied to docs/guide/)
public/.nojekyll GitHub Pages serves the output as-is
Everything here is fiction: the gym, the grades, the prices, the bell. The join form is a
demo — it validates and confirms in place but sends nothing anywhere. To take real
sign-ups, point the submit handler in src/main.ts at your own endpoint (Formspree, a
serverless function, your CRM).
MIT — see LICENSE. Designed & built by Parable.