Quick Start
- Upload the whole folder (
index.html,assets/) to any static host. - Edit the copy in
index.htmland the colours inassets/style.css(:root). - The three CDN
<script>tags at the bottom ofindex.htmlload GSAP, ScrollTrigger and Lenis. Leave them as they are, or self-host if you prefer.
That is the whole install. Everything below is optional customisation.
The cocktail builder
The builder is one data array near the top of initBuilder in assets/script.js:
Code snippet omitted: it ships with the download.
To change the bar's stock:
- Edit a drink: change its
label,ml,abv,amt(the text shown in the recipe) orgarnish. - Add an ingredient: add a row to
BASESorMIXERS, add a matching<input>in the builder markup (samevalueas theid), add a<div class="layer" data-layer="ID">inside[data-well](mixers go in canonical order, bottom to top), and add its colour as.layer[data-layer="ID"] { background: var(--liq-ID); }plus a--liq-IDtoken in:root. - Rename a signature: the
SIGNATURESmap keys a base id and the sorted active mixer ids to a house name. Anything not in the map composes a name from the boldest ingredient viaDESCRIPTOR. - Fill depth:
FULL_MLis the volume that fills the glass toCAP(90%). LowerFULL_MLto make drinks read fuller.
ABV is computed live from the millilitres and never hand-entered. The garnish line concatenates every active mixer's garnish.
The data-layer, data-recipe-name, data-recipe-measures, data-recipe-garnish, data-recipe-abv and data-well hooks are what the script reads; keep them if you restyle.
Sections
Every section is guarded independently, so you can delete any of them and the rest still runs:
- Hero (
[data-hero]): the wordmark, coupe motif and deco frame. Masked line reveal on the title; thedata-hero-fade="pre|post"attributes order the fade-ins. - Builder (
[data-builder]): the signature. Described above. - Menu (
#menu): a plain list of house drinks. Add or remove.menu-itemblocks freely. - Find us (
#find): address, hours and reservations cards. - Footer.
Scroll-in reveals are opt-in per element with data-reveal.
Palette
Four colours and their tints. Every translucent value in the stylesheet resolves from an --*-rgb triple, so a reskin is the :root block and nothing else.
Code snippet omitted: it ships with the download.
Contrast, measured as used (composited on the surface each colour lands on):
| Pair | On ground | On surface |
|---|---|---|
cream #EFE6D2 (body copy) |
12.19 : 1 | 10.62 : 1 |
dim #BBAF95 (secondary) |
6.98 : 1 | 6.07 : 1 |
brass #C6A15B (display / UI) |
6.23 : 1 | 5.43 : 1 |
jade #3E8E7E |
3.87 : 1 | 3.37 : 1 |
Cream is the reading colour and clears AA with wide headroom at 400 weight, which matters because both faces ship in a single weight. Brass clears AA for normal text too, but is rationed to display type, small-caps eyebrows, rules and UI so the room stays cream-on-teal.
Jade is banned as small body text (3.87:1 fails the 4.5 floor). It is used only for the poured liquid (non-text), hairline marks (3:1 UI floor), and headings at 24px and above (3:1). The --liq-* tokens are decorative fills inside the glass and carry no text.
Images
Two photographs ship with the template as optimised, art-directed WebP in assets/img/:
hero.webp(1920px) — an atmospheric speakeasy interior used as the hero backdrop, behind and around the deco frame, under a heavy ground-toned gradient.room.webp(1200px) — a bartender straining a stirred cocktail, used in the "Find us" section.
Both are graded dark, warm and desaturated so they read as one campaign on the teal-black ground, and each sits over a background-color: var(--ground) fallback. A dark overlay keeps the cream hero headline past 4.5:1 on every pixel of the backdrop. The photos are an enhancement only: with images off (or with JavaScript off) all copy and the full page remain readable.
Regenerating / swapping. The bundled files are baked from assets/img-manifest.json by the repo's build script. Each entry names a source src (a remote URL or a local path), an output name, a width, a JPEG/WebP quality, and a grade (saturation, brightness, gamma, hue, tint) that is baked into the pixels. To swap an image, change its src (and tune the grade), then rebuild:
Code snippet omitted: it ships with the download.
Keep the output names (hero, room) so the markup (assets/img/hero.webp, assets/img/room.webp) still resolves, or update the <img src> in index.html to match. The width/height attributes on the <img> tags should track the baked dimensions to avoid layout shift.
Credits. Photographs from Unsplash, used under the Unsplash License with attribution:
- Hero, speakeasy interior — Shawn DENG: https://unsplash.com/photos/a-dimly-lit-bar-with-stools-and-bottles-on-the-shelves-nsgzQHsxbXE
- Room, bartender straining a cocktail — Olena Bohovyk: https://unsplash.com/photos/a-man-is-making-a-drink-at-a-bar-HN2ukPUF_og
Accessibility
- Native inputs: the base is a radio group and the mixers are checkboxes, so keyboard support, grouping and checked state come from the browser. The recipe panel is an
aria-liveregion, so the assembled drink is announced as it changes. - Reduced motion:
prefers-reduced-motion: reduceremoves the pour, the line reveals and all transitions. The glass fills instantly to the selected drink and the page is complete and static from first paint. - No JavaScript: the glass ships filled to a featured drink (The Vane Martini) through inline layer heights, the recipe panel is real HTML, and the full menu is readable. The script only adds interactivity and the scroll reveals on top.
Dependencies
- GSAP 3.15.0 + ScrollTrigger
- Lenis 1.3.17 smooth scroll (optional; remove the block in
script.jsand the CDN tag to drop it) - Google Fonts: Poiret One (display) and Questrial (body)
Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.