← The pack

Kelbin · template design language

The specification, and the gap between built and good.

Twenty-one homepages and twenty chat pages are live at the pack. This document is what the pack cannot tell you: the mechanisms underneath it, each written so it can be tested rather than admired.

  1. Why this document exists
  2. The locked system
  3. The block registry
  4. The four masters
  5. Dress: a theme is tokens, not a rebuild
  6. The chat contract
  7. Standing rules, with their tests
  8. What separates built from 10/10
  9. Findings you inherit

1 · Why this document exists

The first template built from this design — Morrow Salon — got the visible things right and lost the invisible ones. That pattern is the reason for this document, so it is worth being precise about rather than polite.

What it got right: Fraunces with Figtree, which is the correct warm-editorial pairing. The documented grounds and ink. Oxblood as the accent. A dedicated chat page at /chat rather than a corner drawer. Those are real and they are not small.

What it lost, measured:

DefectMeasuredRule that would have caught it
Eyebrow collapses at 390px 0px wide × 625px tall — one letter per line, stable at 3s Mobile is the design surface
Every image 404s 5 of 5 assets missing; page degrades to broken icons and alt text No layout may depend on an image
Stock photography of people alt text reads “Pexels demo image of a salon stylist” Ban list · stock team photography
Banned face as the base body { font-family: Inter, … }; html unset, falls to Times Only the four pairings
Type scale abandoned 78, 60, 58, 56, 16, 14 — six of eleven sizes off-ramp The documented scale
Context does not travel 7 chat links, all bare /chat, zero seeded intent The chat contract
The diagnosis, stated plainly.

Every surviving element was one you can see in a screenshot. Every lost element was a mechanism you can only know by being told: the scale, the image-independence, the seeding contract, the mobile-first rule.

That is a failure of the design to communicate, not of the build to care. A mock shows what a page looks like when everything goes right. It cannot show what must remain true when the photos never arrive, the business name is sixty characters, and nobody wrote the copy. This document exists to carry those.

2 · The locked system

Three axes flex per business: the accent family, the type register, and image density. Everything else is frozen. That is what lets a plumber and a florist each look right while sharing one standard.

Colour

TokenValueRole
--ground#FBF8F3Page ground
--ground-raised#F4EFE7Quiet fills, image slots
--ground-inverse#1A1714Inverse surfaces
--ink#1A1714Text — 16.85 on cream, 15.59 on bone
--ink-muted#6B6259Secondary text — 5.64 / 5.22
--ink-muted-on-dark#A79E93Secondary on ink — 6.76
--rule / --rule-soft#E5DDD0 / #EFE8DCHairlines
An accent is a pair, never a value.

Fourteen candidates were tested against cream and bone. Not one passed as body text on both. A single --accent token is broken by construction: a colour dark enough to read on cream is too dark to read on ink.

Terracotta #B65335 / #C96445 · Olive #6A7740 / #859550 · Ink Blue #4475A7 / #5185B8 · Oxblood #C24766 / #C95E79 · Teal #3C7C7A / #4B9B98. One family per business, drawn from their own brand — never a Kelbin accent, because the accent is how sites differ from each other.

Test: every colour that renders resolves to a documented token or an alpha derivative of one; every text colour clears 4.5:1 against its composited background, not its declared one.

Type

The typographic system is invariant; the typefaces are not. Faces flex as complete pairings, tested together — a law firm and a Vietnamese restaurant should not share a body face, and unrestricted choice produces mismatched pairs.

RegisterDisplay + BodySuitsUsed by
Warm editorialFraunces + Figtreesalons, restaurants, wellness, hospitalitysalon, cafe, r-menu, v-desk2, v-open, m-editorial
ClinicalSource Serif 4 + Source Sans 3clinics, legal, professional servicesclinic, accountant, r-diag, v-desk, m-ledger
ModernInstrument Serif + Instrument Sansstudios, creative, modern retailwedding, r-poster, v-stage, v-ambient, m-stage
SturdyLora + Libre Franklintrades, mechanics, local practical servicesplumber, r-fp, v-input, m-signpost

Scale, ratio 1.333: 72 · 54 · 40 · 30 · 23 display, then 17 body, 15 label. Body copy is 17px / 1.6 / 62–72ch in every pairing — that measure, not the face, is the invariant. Nothing renders below 15px, which supersedes the document's own 13px caption step for customer-facing pages: the reader is outdoors on a phone.

The scale is not a suggestion, and this is where builds drift first.

Morrow rendered 78, 60, 58, 56, 16 and 14. Each looks defensible alone. Together they are the difference between a typographic system and a pile of font sizes, and it is the single most reliable tell of a template that was eyeballed rather than built.

Test: collect the computed font-size of every element containing text; the set must be a subset of {72, 54, 40, 30, 23, 17, 15}.

Space, radius, motion

Space — 4 · 8 · 12 · 16 · 24 · 32 · 48 · 64 · 96 · 128 · 160. Section padding 96 mobile, 160 desktop. Whitespace is the luxury signal: fewer, bigger moments. Radius — 0 / 2 / 4 / 8, hard maximum 8, images 4; heavy rounding is an era marker. Motion — 160ms, opacity and transform only, honour prefers-reduced-motion, and no scroll reveals: they already read dated.

3 · The block registry

Every block in the pack, its idiom, and what it does when the business cannot supply its content. A block that has no honest empty state is not finished.

BlockIdiomStill works without
svc — servicesNamed service, plain price, description that sells by being specificPrices — every row swaps to “On quote”. Used priceless by plumber, wedding, signpost
price-ledgerPrice and its condition on the same row; the condition is the honestyAny row; prices swap to On quote
facts — hours + locationHours as a table, address spoken like directionsAn email — many trades are phone-only. Never assumes both
slot — imageNeutral --ground-raised block, thin rule, label naming the intended shotItself. Removing every slot must leave a composed page
triageThe customer names their problem before the business names itself—; practical-posture only
availReal bookable times as the hero; honest state or omitLive data — omit the block entirely rather than fake slots
boardOpen/closed now, because that is what a café is askedLive data — degrade to the hours table
menuTypeset print: dotted leaders, rules, no boxes, no table markupPrices; not the items
cond — conditionsProblems in the customer's words, not the trade's service names—
stepsNumbered process; each step says what it buys you—
plansComparison, not a list — you pick which one you arePrices; the columns still compare inclusions
datesDeadlines a small business actually misses—
areaA radius, for a trade with no address worth visitingAn address entirely
calloutWhat it costs to get someone there, before the work is priced—
capacityScarcity stated as plain fact, never as urgency—
diaryFree slots are links; taken slots are not clickableLive data — omit rather than invent
dock / chipsCommodity composer; identity lives in the chips and the voice around itChips — the composer alone still works
doorwayPersistent “Ask Kelbin” that navigates. One per page, never a panel—
thread / carried / composerThe chat page: what travelled in, the exchange, the reply fieldcarried when the doorway was used and no intent was named

Master pages additionally declare blocks explicitly as data-block with a data-kit annotation carrying idiom, registry correspondence and still-works-without. Open any master with ?kit=1 to read them in place.

Test: remove every .slot and every image from any page. Each remaining section must still be over 40px tall and contain text. No section may become an empty band — which means an image never gets a section to itself; it sits beside content.

4 · The four masters

A master is a block sequence plus composition rules. Four exist, and the greyscale wireframe test confirms they stay distinguishable with all dress removed — distinct sequences and distinct hero blocks.

MasterSequenceComposition rule
Set-back editorialhero-setback › services-list › chat-entries › hours-contact Type leads. Imagery is contained below the words, never behind them. Air is the point
Image stagehero-stage › services-band › chat-entries › hours-contact Full-bleed image with a computed scrim; the name stands on the picture. The stage falls back to the ink field when no image exists
Signposthero-signpost › what-list › chat-entries › hours-contact The name set enormous is the hero. Zero photography as a first-class path — not even a slot
Ledgerhero-ledger › price-ledger › chat-entries › hours-contact Dense and priced. Contact in the masthead, before any scroll. Every rate carries its condition

Test: render all four at ?wire=1. No two may share a block sequence or a hero block. If a pair becomes indistinguishable, they are one master — merge them and report it, rather than shipping two names for one thing.

5 · Dress: a theme is tokens, not a rebuild

A dress re-points tokens over an unchanged structure. The structure must not know which dress it wears.

The dark dress is not dark mode by default.

Dark-mode-by-default is on the ban list because it fails our customers' customers, who are on phones in daylight. The dark dress is a chosen register for a business whose room is actually dark. When classification runs at scale, a classifier must never route a business to the dark dress as a fallback.

Test: the two dress files render byte-identical DOMs apart from the data-dress attribute — verify by diff and by comparing outerHTML in the browser, since a build step can diverge what the source shares.

6 · The chat contract

This is the part most likely to be built as a shell and lose its purpose, so it is specified exactly.

An empty chat box is a failure state.

It hands work back to the customer at the exact moment we promised to take work away. If a button's label is a verb with a known object — Book a chair, Change my hours — it either performs the action or opens the chat with that sentence already sent and answered. Only Ask, Tell, Talk to earn a blank box.

ParameterCarriesBehaviour on arrival
?i=<n>A named intent from the page Renders the sent message, Kelbin's reply, and the subject chips that travelled with it
?q=<text>Free text composed in a dock Renders that text verbatim as the sent message, with a reply that moves it forward
&slot=<label>A specific diary slot taken Prepends that slot to the carried chips
?ask=1The doorway — no intent named The only legitimate blank composer; no sent turn is rendered
&y=<px>Where the customer was standing “Back to site” restores that exact scroll position
Return must be exact, and this is subtler than it looks.

Restoring scroll before the webfonts settle clamps to a shorter page and lands you near but not where you were. Re-apply on document.fonts.ready and load. Landing at the top of the homepage is the single most likely way this pattern turns from a signature into a tax.

Test: scroll well down, take an entry, press Back to site, and assert the scroll position is within 30px of where it left.

The chat is always its own page with its own URL — never a modal, drawer or corner panel. A page is returnable, linkable and resumable, which is what matters the moment Kelbin takes a booking and someone asks “what time did I book?” tomorrow, on another device.

7 · Standing rules, with their tests

Each rule is paired with the check that proves it. A rule without a test is a preference.

RuleTest
No layout may depend on an image for legibility. Type-led is the default; photography is enhancement Render every page with images removed. It must still read as composed, with no empty sections
Text over an image requires a computed scrim — never white-on-photo by hope Sample mean luminance inside the text's bounding box; fail below 4.5:1
Every text slot declares a length range and the layout holds all of it Render with a ~60-character business name and a 3-character one. No overflow, no overlap, no clipping
Exactly one primary action, persistent on mobile At 390px the primary action is reachable without scrolling, on every page
No numeric proof slot without a data source Generate with no stats supplied: no empty slots, no invented figures
The site must never state a fact the business did not supply Diff every factual claim on the page against the supplied profile. Anything unsupported is a defect, not a flourish
Mobile is the design surface, not the adaptation Every page reviewed at 390px before desktop; no sideways scroll, and no element flush against the screen edge
Nothing below 15px; tap targets at least 44px Computed font-size and bounding-box height across every text node and control
Alt text is derived, never invented If we do not know what is in the photograph, we do not describe it
Name, phone and location on every page — not only in the footer — plus LocalBusiness schema Assert a tel: link and the business name outside the footer on every page

Never

Indigo or violet default accent · Inter · Space Grotesk · gradient hero · glassmorphism · 3D decoration · three columns of circle icons · stock team photography · rounded-everything · dark mode by default · lorem ipsum · invented statistics, testimonials, review counts, awards or years-in-business.

The empty proof slot is itself the defect.

Published commercial templates ship fabricated proof as standard — invented review counts, testimonials from people who do not exist, and in one case literal “Logoipsum” placeholders left visible in the live design. A generator handed an empty proof slot will fill it. Do not create the slot.

8 · What separates built from 10/10

Not taste. Six concrete things, drawn from what the first build lost.

  1. It survives its own missing assets. The photos will not arrive. A 10/10 template looks deliberate with every image removed; a built one shows holes. Test it by deleting them, not by imagining it.
  2. The type scale is a closed set. Seven sizes, no others. The moment a seventh becomes a ninth, the page stops reading as designed and starts reading as assembled.
  3. Every entry point carries its subject. A chat link that arrives knowing “Thursday 5:30, cut and half-head colour, about two hours” is a different product from one that arrives blank. This is the whole proposition, and it is one URL parameter.
  4. Mobile is composed, not survived. 390px is where the customer's customer actually is. A hero that is beautiful at 1280 and collapses at 390 is not 8/10, it is broken.
  5. The copy is the business's own — and this one is testable too. Keep the owner's plain sentence; do not upgrade a plumber into marketing prose. That rewriting is exactly how a site stops sounding like the business, which is the proposition. Apply the substitution test below.
  6. Honest state or no state. “Next free chair 5:15” makes a sparse page feel alive; the same line invented makes the whole site a lie within a week. Bind it to real data or omit the block.

The substitution test

Every line must be a fact about this business that could not be true of another business in the same trade. Swap in a competitor's name. If the sentence still works, it is decoration — cut it.

Fails — true of any salonPasses — true only of this one
“Make room for your next good hair day” “Unhurried hair, on Lygon Street.”
“A better appointment starts with a little clarity” “A four-chair studio. One stylist, one guest at a time.”
“Detail carries the mood” “Steph keeps the diary small on purpose.”
Design vocabulary is banned from customer surfaces.

Composed, curated, considered, elevated, mood, clarity, detail, craft, thoughtful, intentional, seamless, bespoke. These are words for talking about design, not for a salon talking to someone who needs a haircut.

And no sentence may describe the website itself. The first production build shipped “The website stays composed even when the photo count does not” as customer copy on a salon page — a rule from the design document rendered as marketing. The customer does not know there is a template, and should not be told.

No internal identifiers on a customer surface, ever. A live site was found serving T-036 in its page title and again in its chat heading. Template ids, site ids and build references never reach rendered text, titles, alt attributes or metadata.

Test: for each headline and subhead, substitute a competitor's name and ask whether the sentence survives; it must not. Then grep the rendered text for the banned vocabulary, for any sentence whose subject is the site, and for /[A-Z]-?\d{3}/-shaped identifiers. All three must return nothing.

9 · Findings you inherit

Recorded so they are not rediscovered by someone building on them.

Questions on any of this — ask Sana. The pack is static and self-contained: HTML, one stylesheet, no framework, no build step, no dependency beyond Google Fonts.