Overview#
Brew is a complete website for a craft brewery, taproom or brewpub, shown as Cinderjack Brewing Co., a fictional independent brewery with a taproom and beer garden on NE Hawthorne Avenue in Bend, Oregon (US). It has a live "on tap now" board with an open-now status in Pacific time, fifteen beers with filters, an ABV/IBU map and SVG flavor charts, opening hours that work out every US federal holiday for any year, a food truck rotation, a dated events calendar, cans and merch to go with a pickup slot picker and a 21+ confirmation, brewery tour booking with live capacity, and a private events inquiry. The look is a hand-printed can label: ochre, juniper green and rust on cream, halftone dots, thick ink outlines with offset "misprint" shadows, mountain-ridge dividers, Alfa Slab One headings and a hand-drawn SVG label for every beer. This is the HTML + Tailwind CSS v4 version (1.0.1): plain HTML files, one stylesheet and two vanilla JavaScript files, with no build step needed to run it.
Nothing on the site goes out of date: the open status, tap board, food trucks, holiday hours, pickup slots, tour
dates and the copyright year are worked out from today's date in Pacific time, and the events move forward while
KEEP_DEMO_CURRENT is on (see Keeping the events current).
Live demo: freeframe.dev/templates/brew
Quick start#
- Unzip the download.
- Open
index.htmlin a browser. Every page works from the file system; links are relative. - To preview it the way a host would serve it, run any static server in the folder, for example:
npx serve .Requirements: none to run it. To rebuild the CSS after editing src/input.css or adding Tailwind classes you need
Node.js 24 (see Building and deploying).
Pages#
| File | Purpose | Notable sections and features |
|---|---|---|
index.html |
Home | Hero with open-now status, today's hours and today's food truck; live tap board (first 8 taps); three year-round beers; can shelf with prices; next three events; tours and private events; story and beer garden drawing |
beers.html |
Beers | Style, strength and "pouring now" filters; 15 beer cards with can art; ABV/IBU map; tasting-notes dialog with a flavor radar chart |
taproom.html |
Taproom & menu | Beer garden drawing; full tap board (12 taps); pour menu; flight builder; hours table; next five holiday hours; house rules; seven-day food truck schedule; snacks |
events.html |
Events | Next-up card with a countdown; type filters with counts; events grouped by month in date order; the regulars; tickets contact |
shop.html |
Shop | 15 products (beer and merch) with options and limits; cart panel with quantities, deposit and total; good-to-know notes |
checkout.html |
Pickup order | Pickup day and 30-minute slot picker; name, email and mobile; 21+ confirmation when the order has beer; order summary; confirmation with a reference |
tours.html |
Tours | Brewhouse cutaway drawing; tour choice, date and time with places left, party size, details; summary panel; saved bookings with cancel |
visit.html |
About & visit | Map drawing; story and timeline; six crew members with portraits; directions; hours and coming-up holiday hours |
contact.html |
Private events & contact | Three spaces; inquiry form with live capacity and minimum spend; contact details; confirmation with a reference |
404.html |
Not found | Links back to the main pages |
Project structure#
brew/
index.html … contact.html, 404.html the pages (header and footer repeated in each)
assets/
css/style.css compiled Tailwind CSS (generated by npm run build, committed)
js/data.js all the business data: hours, holidays, beers, events, products, tours, spaces
js/main.js all behavior: status, tap board, art, filters, cart, checkout, tours, forms
img/favicon.svg the cinder-cone-and-jackrabbit logo mark
img/og-image.svg source of the social sharing image
img/og-image.png 1200 × 630 social image used by og:image
src/input.css Tailwind source: @theme tokens and a small @layer components
package.json npm scripts to rebuild the CSS
README.md short introduction
DOCS.md this guide
CHANGELOG.md version history
LICENSE.txt MIT license (in the download)data.js must load before main.js; every page includes both at the end of <body>:
<script src="assets/js/data.js"></script>
<script src="assets/js/main.js"></script>Customizing#
Brand and colors#
The design tokens live in the @theme block of src/input.css. Each becomes Tailwind utilities (bg-ochre,
text-rust, border-ink and so on).
| Token | Value | Used for |
|---|---|---|
--color-cream |
#f5ead3 |
Page background |
--color-paper |
#fbf5e8 |
Cards and fields |
--color-sand |
#ead9b5 |
Alternate sections (with the halftone utility) |
--color-ink |
#241a13 |
Text, outlines and offset shadows |
--color-muted |
#5f4c3a |
Secondary text on cream, paper and sand |
--color-ochre / --color-ochre-soft |
#e0a133 / #f3d692 |
Fills, buttons, date blocks; ochre is text only on dark |
--color-rust |
#983a19 |
Accent text on light backgrounds |
--color-rust-bright |
#f08a5d |
Accent text on dark sections |
--color-pine / --color-pine-deep |
#24493f / #16302a |
Juniper green; dark sections, tap board and footer |
--color-sage |
#c9d4c4 |
Secondary text on dark |
Change a color in one place and rebuild:
@theme {
--color-ochre: #d98e2b;
}Keep the contrast pairs above: ochre and rust-bright are for dark backgrounds or fills with ink text, rust and muted
for light ones. The printed look comes from four small utilities in src/input.css: halftone (dot screen),
sunburst (rays behind the cans), print-shadow and print-shadow-sm (the offset ink shadow).
Typography#
Two Google Fonts, loaded with one <link> in the <head> of every page:
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Alfa+Slab+One&family=Golos+Text:wght@400..800&display=swap">- Alfa Slab One (
font-display): headings, prices, tap numbers and the text on the can labels. - Golos Text (
font-sans): body text, buttons and labels.
To swap a font, change the <link> on every page and the family in --font-display or --font-sans in
src/input.css, then rebuild. The SVG can labels in main.js and og-image.svg name the families directly
(font-family="Alfa Slab One, …"), so update them too.
Content#
Page text is in the HTML files. Everything the pages compute from lives in assets/js/data.js, returned as
window.BREW_DATA:
| Constant | What it holds |
|---|---|
BREW |
Name, time zone (America/Los_Angeles), locale, currency, phone numbers, email, address |
HOURS |
Regular hours per weekday, Sunday first, as ['HH:MM', 'HH:MM'] or null for closed |
LAST_CALL_MINUTES |
When "Last call" shows before closing (30) |
HOLIDAY_HOURS |
What the taproom does on each federal holiday: null closed, hours, or 'regular', plus a note |
EXTRA_HOURS |
Special days every year by "MM-DD" (Christmas Eve, New Year's Eve) |
STYLE_GROUPS, STRENGTHS |
Beer filters: four style groups and three ABV bands |
FLAVOR_AXES |
The six axes of the flavor chart |
BEERS |
The 15 beers (see below) |
SEASONAL_TAP |
The tap that Rimfire and Tumbleweed take turns on, each from its release event (see Tap board) |
LIQUID |
The color of each beer in the glass (keg bars and the flight paddle) |
POURS, STRONG_ABV |
Pour sizes and prices; beers over STRONG_ABV are poured 10 oz only |
TRUCKS, TRUCK_ROTA |
Food trucks and which trucks take turns on each weekday |
SNACKS |
The taproom counter menu |
EVENT_TYPES, EVENTS |
Event categories and the 32 dated events (11 in the regular program, 21 seasonal) |
PRODUCTS, DEPOSIT, PICKUP |
Shop items, Oregon's 10¢ container deposit and pickup slot settings |
TOURS, TOUR_BOOK_AHEAD_DAYS, TOUR_MAX_GROUP |
The two tours, how far ahead they can be booked, the largest group |
SPACES, HIRE_EVENT_TYPES, HIRE_MIN_DAYS_AHEAD |
Event spaces, the event type list and the notice needed |
KEEP_DEMO_CURRENT, DEMO_ANCHOR |
Keep the dated events ahead of today (see below) |
Add a beer by appending an object to BEERS:
{
id: 'hop-harvest', name: 'Hop Harvest', style: 'Fresh Hop Pale', group: 'hoppy', abv: 5.8, ibu: 45,
tap: null, cans: false, tapped: null, motif: 'pine', colors: ['#cfe0c3', '#983a19', '#241a13'],
tagline: 'Hops picked in the morning, in the kettle by lunch.',
notes: 'Wet Centennial straight from the farm…',
hops: 'Centennial (fresh)', pairing: 'Fish tacos',
flavor: { bitter: 3, malt: 2, roast: 0, body: 2, tart: 0, fruit: 3 }
}idis used in URLs (beers.html?beer=hop-harvest) and must be unique; add its glass color toLIQUID.groupmust be a key ofSTYLE_GROUPS; the strength band is worked out fromabv.tapis the tap number on the board (nullwhen it isn't pouring);tappedis how many days ago the keg went on (3 or fewer shows "Just tapped"). Beers listed inSEASONAL_TAPget both from their release events instead.motifpicks the label drawing fromMOTIFSinmain.js(cone,whirl,hare,rim,pine,mesa,flake,tumble,tower,ridges,stones,cave,sprig,cactus,berry);colorsis[label, band, ink].- To sell it to go, add a product with
beer: '<id>'toPRODUCTS.
The hero copy, the three "year-round" cards and the can shelf on index.html are written in the HTML (the cans and
shelf prices are filled from the data through data-can="<beer id>" and data-product-price="<product id>"), as
are the style filter buttons on beers.html (their counts are recalculated).
Images and artwork#
All artwork is SVG; there are no photos to license.
- Can labels are drawn by
canSvg(beer)inmain.jsfrom each beer'smotif,colors, name and ABV (viewBox 120 × 200). Long names wrap onto two lines. - Merch drawings (
MERCH_ART: tee, cap, glass, growler, pin, stickers, crowler; viewBox 160 × 200). - Flavor radar (
radarSvg), ABV/IBU map (drawn in the beers section ofmain.js), flight paddle and event type icons (EVENT_ICONS) are generated from the data. - Scenes written inline in the pages: the beer garden (
taproom.html,index.html), the brewhouse cutaway (tours.html), the neighborhood map (visit.html) and six crew portraits (visit.html).
To use product photos instead, replace the productArt(p, …) call in the shop grid with an <img> in a 4:5 box
(the art area is h-52). Keep the 1200 × 630 size for the social image: edit assets/img/og-image.svg, then render
the PNG (from the Freeframe repository, node scripts/og-png.mjs assets/img/og-image.svg assets/img/og-image.png index.html, or export it from any vector editor). The favicon is assets/img/favicon.svg.
Navigation, header and footer#
The header and footer are repeated in every HTML file. To add a page, copy an existing one, change its <title>,
description and Open Graph tags, and add a link to the <nav aria-label="Main"> list, the mobile menu
(#mobile-menu) and the footer of every page. Mark the current page's links with aria-current="page".
The top bar shows the open status (data-status), today's truck (data-truck-today) and the phone number. The
"Order" button links to checkout.html and shows the cart count (data-cart-count).
Features#
Open status and hours#
Every element with data-status gets a data-state of open, lastcall or closed and its data-status-text
child is filled: "Open now · until 10pm", "Last call · closing at 10pm" or "Opens tomorrow at 11am" (never a bare
"Closed"; on a holiday it reads "Thanksgiving · Opens Friday at 12pm"). Add data-status-short for the compact
version. data-today-hours shows today's hours, tbody[data-hours-table] is filled from HOURS with today's row
highlighted, and ul[data-holidays="5"] lists the next five days with changed hours.
The code is taproomStatus(), dayInfo() and paintStatus() in main.js; they use the taproom's wall clock in
BREW.timeZone via Intl.DateTimeFormat, whatever the visitor's time zone, and refresh every minute.
Federal holidays#
federalHolidays(year) works out the eleven US federal holidays for any year by rule: fixed dates (New Year's
Day, Juneteenth, Independence Day, Veterans Day, Christmas Day) and nth-weekday rules (Martin Luther King Jr. Day,
Presidents' Day, Memorial Day, Labor Day, Indigenous Peoples' Day, Thanksgiving). The taproom keeps holiday hours on
the actual date, not the federal "observed" weekday. HOLIDAY_HOURS says what happens on each; EXTRA_HOURS adds
days that aren't federal holidays:
const EXTRA_HOURS = {
'12-24': { name: 'Christmas Eve', hours: ['11:00', '16:00'], note: 'Closing at 4pm.' }
};Closed days are skipped by the pickup slots, tour dates and events, and shown in the food truck schedule.
Tap board#
[data-tapboard] elements show every beer with a tap number, in tap order (data-tapboard="8" shows the first
eight and a "+ 4 more taps" link). Each row has the name (linking to its tasting notes), style, ABV, IBU, the pour
price (10 oz for beers over STRONG_ABV), a "Just tapped" badge and a keg level bar. The heading reads "On tap now"
while open, otherwise "Pouring today from 12pm". Keg levels are a stable demo value per beer and day that drains
through opening hours (kegLevel()); replace that function if you have a real source.
Tap 4 is the seasonal tap. SEASONAL_TAP names the beers that take turns on it and the release event of each:
const SEASONAL_TAP = { tap: 4, beers: { rimfire: 'rimfire-release', tumbleweed: 'tumbleweed-release' } };Each beer goes on at the start time of its release event and pours until the next one's release starts
(assignSeasonalTap() in main.js), so the board, the tasting notes ("Back on tap Saturday, October 24 at 12pm")
and the events calendar always agree: Tumbleweed pours from the spring opening of the beer garden, Rimfire from
its October release day. Leave tap: null on those beers in BEERS. To pour a beer year-round instead, remove
it from SEASONAL_TAP and give it a tap number.
Food trucks#
truckOn(date) picks the truck for a date from TRUCK_ROTA[weekday]: when a weekday lists several trucks they
take turns by ISO week number, so Friday to Sunday alternate every other week. Closed days have no truck.
[data-truck-today] shows today's truck (or the next opening day's after closing) and ul[data-truck-week] the
next seven days with serving times (opening until an hour before close).
Pour menu, snacks and flight builder#
ul[data-pour-menu] lists the taps with pint and 10 oz prices, ul[data-snacks] the SNACKS, and
[data-pour-price="pint"] spans show prices from POURS. The flight builder ([data-flight]) has four selects
filled with the taps; the paddle SVG and the summary (average ABV and IBU, a light-to-bold drinking order) update on
change.
Beer filters, map and tasting notes#
beers.html renders BEERS into [data-beer-grid]. The style chips (data-filter-style), strength chips
(data-filter-strength) and the "Pouring now" toggle (data-filter-tap) use aria-pressed, update the count in
a polite live region and keep the URL in sync with ?style=, ?strength= and ?tap=1 (validated with own-key
checks). The ABV/IBU map dims beers that don't match. "Tasting notes & flavor chart" opens the native <dialog id="beer-dialog"> with the can, stats, notes, flavor radar, hops, pairing and where to buy it; ?beer=<id> opens
it on load. The dialog traps focus, closes with Esc, the close button or a click on the backdrop, and returns
focus to the button that opened it.
Events and the countdown#
events.html lists upcoming EVENTS in date order (events that have ended, fall on a closed holiday or would
start after that day's closing time, such as an evening show on Christmas Eve, are hidden), grouped by month, with a relative label ("Tomorrow", "In 2 weeks"), the time range, price and, for ticketed
events, seats left and a phone link. Type chips (data-filter-type, ?type=) filter the list. The next-up card
([data-event-next]) shows the soonest event with "Starts in 6 hours" or "Starts in 2 days". ul[data-events-next="3"]
on the home page shows the next three. Seats left are a stable demo number (never below 30% of seats).
Keeping the events current#
The events are written for the weeks after DEMO_ANCHOR ('2026-10-21'), in two kinds. While
KEEP_DEMO_CURRENT is true, main.js keeps both current:
-
The regular program (bands, trivia, beer dinners: events without
yearly) moves forward by the whole weeks elapsed since the anchor, so it is always ahead of today and every event keeps its weekday:jsconst SHIFT_DAYS = D.KEEP_DEMO_CURRENT ? Math.max(0, Math.floor(daysBetween(D.DEMO_ANCHOR, TODAY) / 7) * 7) : 0; -
Seasonal events (
yearly: true: the Rimfire and Tumbleweed releases, costume night, the winter makers market, the summer block party and so on) come back once a year at the same time of year. After one has passed it moves to the next year, on the same weekday in the week ending on the written date's anniversary (yearlyOn()), so costume night is always the Friday before Halloween and never drifts into spring.
The calendar shows events up to the last date of the regular program (about twelve weeks ahead), so seasonal events appear as their season comes round and the list is full on any date. The written dates are what the site shows on the day it was built.
On a real site, enter your own dates in EVENTS and set KEEP_DEMO_CURRENT = false in data.js; every event
then keeps its date, past events drop off the list on their own and there is no twelve-week limit. The seasonal tap
follows the release events' dates either way. Nothing else in the template is dated: hours, holidays, trucks, slots
and tour dates are always worked out from today.
Cart and shop#
shop.html renders PRODUCTS into [data-product-grid], with Beer/Merch tabs (data-shop-tab). Products with an
option show a select: tee sizes, or for the crowler fill the beers currently on tap. "Add to order"
(data-add="<product id>") adds one, up to the product's limit, and shows a toast. The cart panel
([data-cart-panel]) has quantity buttons (data-qty, data-step) and remove buttons (data-remove), the
subtotal, Oregon's container deposit (DEPOSIT × units per product), "None in Oregon" for sales tax, and the
total paid at pickup. shop.html?add=<product id> (used by the tasting-notes dialog) adds one item on load.
Checkout and pickup slots#
checkout.html offers the next PICKUP.days open days (pickupDays()), each with slots every
PICKUP.slotMinutes from 30 minutes after opening until 30 minutes before close (pickupSlots()). Same-day slots
need PICKUP.leadMinutes notice, and PICKUP.slotsFull percent of slots show as full (a stable demo value; replace
pickupSlots() with your real capacity). The form checks a slot, name, email and a 10-digit mobile; when the order
contains beer it requires the 21+ confirmation. On success the cart is cleared and the confirmation shows a
reference like CJ-4821, the pickup time, the lines and the total, and asks for photo ID for beer orders.
Tour booking#
tours.html books TOURS: the brewhouse tour (Fri 4pm, Sat and Sun 12pm and 2pm, 14 places, under-21s free) and the
barrel room tour (Sat 4pm, Sun 3:30pm, 8 places, 21+). Dates run from tomorrow to TOUR_BOOK_AHEAD_DAYS ahead on
open days; each start time shows "9 of 14 left" or "Full" (tourSeatsLeft(): a stable demo number minus the
visitor's own saved bookings). Steppers keep the party within the places left and TOUR_MAX_GROUP. A booking gets a
reference like CJT-1234, is saved in localStorage and appears under "Your bookings" with a cancel button, which
frees the places again. tours.html?tour=barrel preselects a tour.
Private events inquiry#
contact.html reads SPACES. Choosing a space, date and guest count updates the dark estimate panel: the minimum
spend for that weekday (minSpend[0] Sunday to Thursday, minSpend[1] Friday and Saturday), a warning when the
guests exceed the space, a per-head figure (only for 8 guests up to the space's capacity, worked out from the minimum
for the chosen day), and a note when the taproom is closed that day. Sending checks 8–400 guests and that the count
fits the chosen space (SPACES[id].guests), with an inline error otherwise. Dates must be at least
HIRE_MIN_DAYS_AHEAD (14) days out; the date field's min is set to match. contact.html?space=garden preselects a
space.
Header, mobile menu, reveals and back to top#
Below 1280px the main menu collapses into #mobile-menu, toggled by #menu-toggle (aria-expanded, Esc closes it
and returns focus). Elements with .reveal fade in as they scroll into view; everything above the viewport edge is
revealed too, and nothing animates with reduced motion. A back-to-top button appears after a screen of scrolling
and steps aside for sticky panels marked data-back-to-top-hide.
Forms#
Every form validates in the browser with inline messages ([data-error="<field>"], aria-invalid, and focus on the
first problem) and finishes with the business's own confirmation and a reference. The forms don't send anything
until you connect them.
| Form | Page | Checks | Reference |
|---|---|---|---|
| Pickup order | checkout.html |
Slot chosen, name, email, 10-digit mobile, 21+ box for beer | CJ-1234 |
| Tour booking | tours.html |
Date and time, places left, name, email, optional phone | CJT-1234 |
| Private events | contact.html |
Space, event type, date 14+ days out, 8–400 guests within the space's capacity, name, email, optional phone | CJE-1234 |
To connect one, find its submit handler in main.js (each has a comment at the point to change) and send the
data before showing the confirmation. With Formspree, for example, add action to the form and post it:
const res = await fetch('https://formspree.io/f/your-id', {
method: 'POST',
body: new FormData(form),
headers: { Accept: 'application/json' }
});
if (!res.ok) { /* show an error message and stop */ }Make the handler async, and only show the success panel when the response is OK. The pickup order isn't in a
form field, so append it before sending: data.append('pickup', pick.day + ' ' + clock(pick.time)) and the cart
lines from Cart.get(). For real tour capacity or pickup slots, replace tourSeatsLeft() and pickupSlots() with
calls to your booking system.
Saved data (localStorage)#
| Key | Shape | Validated on read |
|---|---|---|
cinderjack-cart |
{ lines: [{ id, opt, qty }] } |
Unknown product ids and invalid options are dropped, quantities are clamped to 1…limit, duplicates merged, at most 30 lines |
cinderjack-tours |
[{ ref, tour, date, time, adults, minors, name }] |
References must match CJT-0000, tours must be keys of TOURS, dates real calendar dates, counts whole numbers within limits; at most 6 kept |
All access goes through one safeStorage object: localStorage when it works, otherwise an in-memory stand-in, so the
cart and bookings still work for the page view when storage is blocked (sandboxed iframes, strict privacy modes). To
reset, clear the two keys in the browser's developer tools, or run localStorage.removeItem('cinderjack-cart').
URL parameters#
| Parameter | Page | Effect |
|---|---|---|
?style=hoppy|crisp|dark|wild |
beers.html |
Style filter |
?strength=session|standard|strong |
beers.html |
Strength filter |
?tap=1 |
beers.html |
Only beers pouring now |
?beer=<beer id> |
beers.html |
Opens that beer's tasting notes |
?type=music|trivia|release|food|community |
events.html |
Event type filter |
?add=<product id> |
shop.html |
Adds one of that product (products without options) |
?tour=brewhouse|barrel |
tours.html |
Preselects a tour |
?space=loft|barrel|garden |
contact.html |
Preselects a space |
?now=YYYY-MM-DDTHH:MM |
every page | Previews the site at a Pacific date and time (status, board, trucks, slots, events) |
Every value is checked with an own-key lookup (Object.prototype.hasOwnProperty.call), so ?beer=constructor
matches nothing.
SEO and sharing#
Each page has its own <title>, meta description, Open Graph tags (og:title, og:description, og:image,
og:locale en_US), a Twitter card and theme-color, all in the page's <head>. Social sites need an absolute
image URL, so after deploying change every og:image to your domain:
<meta property="og:image" content="https://cinderjack.example/assets/img/og-image.png">Add a sitemap.xml and robots.txt at the root for search engines. A brewery can also add Brewery structured
data (schema.org) with the address, phone and openingHoursSpecification matching HOURS.
Accessibility#
- Skip link, landmarks (
header,nav,main,footer), one<h1>per page and a logical heading order. - Current page marked with
aria-current="page"; today's row in hours tables witharia-current="date". - Filters, tabs, day and slot pickers are buttons with
aria-pressed; disabled slots are realdisabledbuttons with "(full)" for screen readers. - Counts, cart contents, tour summaries and estimates update in polite live regions; the toast is a
role="status". - The tasting-notes dialog is a native modal
<dialog>labelled by the beer's name. - Charts and drawings have text alternatives (
role="img"with a label or<title>); decorative cans are hidden. - Visible focus outlines, WCAG AA contrast in every section, and no motion with
prefers-reduced-motion.
When editing, keep one h1, label every new input and give error messages an id referenced by
aria-describedby.
Building and deploying#
Rebuilding the CSS#
assets/css/style.css is compiled from src/input.css and the classes used in the HTML and JavaScript. After
changing either, rebuild with Node.js 24:
npm install
npm run build # or: npm run watch while you workClasses added only inside JavaScript strings must be written out in full ('bg-ochre', not
'bg-' + color) so Tailwind can find them.
Deploying#
The folder is a static site. Upload it as-is to Netlify (drag the folder onto Netlify Drop), Cloudflare Pages,
GitHub Pages, Vercel or any web host. There is no server code; DOCS.md, README.md, src/ and package.json can
be left out of the upload.
Troubleshooting#
- The status says "Opens …" when you're open. Check
HOURSandBREW.timeZone, and whether today is inHOLIDAY_HOURSorEXTRA_HOURS. Preview other times with?now=2026-10-23T13:00. - The events list is empty or shows the wrong dates. With
KEEP_DEMO_CURRENToff, events before today are hidden: enter future dates inEVENTS. Events on a closed holiday are hidden too. - A new beer has no can art or breaks the board. Its
motifmust be one of theMOTIFSkeys,colorsneeds three values, andLIQUIDneeds an entry for its id. - New Tailwind classes have no effect. Rebuild the CSS with
npm run build; classes in JavaScript must be whole strings. - The cart forgets items after a reload. The browser is blocking localStorage (private mode, sandboxed iframe): the cart then works in memory for the page view only. Open the site normally.
- Pickup or tour slots show as full. That's the demo availability (
PICKUP.slotsFull,tourSeatsLeft()); setslotsFullto 0 or connect your booking system. - The social image doesn't appear when sharing.
og:imagemust be an absolute URL on your domain.
License#
MIT. The code and all included artwork are free for personal and commercial use; the full text is in LICENSE.txt,
included in the download. Alfa Slab One and Golos Text are licensed under the SIL Open Font License. Cinderjack
Brewing Co., its beers, staff, food trucks, bands and events are fictional.