Brauwerk Hoffman is a German-inspired microbrewery and seasonal brewpub in Campbell's Bay, founded in 2018 by Todd Hoffman. It was the first microbrewery in the Pontiac. From Arnprior it is a short drive across the river, and on a summer weekend it is a destination rather than a stop.
The new site went live at brauwerk-hoffman.ca on September 12, 2026. This is how it was built, what made it harder than a normal restaurant site, and the parts that did not go to plan. The short version of the project is on the Brauwerk Hoffman case study. This is the long one.
What was wrong with the old site?
It worked, but it was heavy. The old site ran on a Bootstrap theme that pulled fonts, framework styles and animation libraries from three outside domains on every page load, so every visit depended on three other companies' servers before the page could finish. It also had nothing built for a business that closes for half the year.
None of that is unusual. It is how most small hospitality sites end up: built once, patched for years, and slower every season.
Why bilingual was not optional
Campbell's Bay is in Quebec. Under article 52 of the Charter of the French Language, commercial publications have to be available in French, and the Office québécois de la langue française applies that to websites. It is not theoretical: in 2025 the OQLF announced a second conviction and a fine against a Montreal business whose website was in English only.
So every page ships in English and French with matching content, English at the root and French under /fr/. The French is written, not machine-translated, and it has its own navigation, its own form messages and its own error strings. I have written about what a bilingual site actually involves, and this project is the clearest example I have of why "we'll add French later" does not work on the Quebec side.
One gap is known. At launch, the beer and events archive addresses still used English words in their French URLs, a limit of the free version of the translation plugin. I chose to write those two redirects by hand rather than buy a licence to fix two addresses.
A seasonal business needs a seasonal website
Brauwerk Hoffman is not open year round. For about seven months of the year, the honest answer to "can I come by this weekend?" is no, and the website has to say so everywhere at once: the banner on the home page, the hours, the copy about what is open.
On most sites, that means someone remembering to edit five pages in two languages twice a year. Something always gets missed, and it is usually the hours in the footer. On this one, season status is a single switch. Flip it and the home page badge, the hours and the open-or-closed copy all change together, in both languages, from one screen.
The beers work the same way. A beer is not a paragraph. It has a style, an alcohol percentage, a label image, and a flag for whether it is on tap right now. The beer list, the archive and the 3D can all read from that one record, so there is exactly one place to change when a beer runs out.
An editor that cannot break anything
There is no marketing department at a brewery. Whoever updates the site is doing it between brewing and serving, and they should be able to change the beer list without any chance of breaking the layout.
So the editor's sidebar shows five things: Beers, Events, Menu, Media and Season Status. Everything else is blocked at the address, not just hidden from the menu. That distinction matters. Hiding a link from a menu does not stop someone reaching the page behind it by typing the address, and a lot of "simplified" WordPress dashboards only do the hiding.
Pages lost the free-form editor entirely, apart from the privacy policy. Content that has a shape, like a timeline or a gallery, lives in fields that hold that shape. The design lives in one theme file that defines colour, type and spacing once. The result is a site that stays looking like itself no matter who edits it.
The 3D can, and what happens when it cannot load
The home page has a beer can you can spin. It is built from real geometry rendered in the browser with WebGL, not a photo, and it was the part of the project most likely to go wrong.
Two rules kept it honest. First, the decoder that unpacks the 3D model is hosted on the site itself rather than pulled from someone else's server, because a third-party outage should not turn the home page into an empty box. Second, it always has a fallback. Every can has a pre-rendered image that shows first and only fades out once the 3D version has actually drawn a frame. If a browser cannot do WebGL, visitors see a good still image and never know they missed anything. On the individual beer pages, touch devices keep the still image on purpose.
Getting that handover right took measuring rather than guessing. On a cold load the can took 1.7 seconds to come alive, and only about 200 milliseconds of that was network. The rest was decoding large textures and preparing the lighting, all of it waiting until the visitor had nearly scrolled to it. Starting that work as soon as the page finished loading, instead of at the last moment, means the can is live before anyone reaches it.
What went sideways
Plenty. These are the ones worth telling.
The first launch attempt failed, and it was my fault. The plan was sound, but I skipped the full rehearsal to move faster. Partway through, the connection to the server dropped, which made one step look like it had silently done nothing, and WordPress then refused to start against the new database tables. I aborted. Rolling back, a timestamp in my script was one minute off from the one the cutover had used, so an old folder was never renamed back and the site had no working theme for several minutes. Two lessons came out of that night and both are now rules: rehearse on the real server first, and derive every name from the same value that created it instead of typing it twice.
The second attempt was rehearsed, scripted end to end, and ran with about three seconds of downtime. The old site was kept whole on the server behind a single rollback command. Then my own verification script reported that most of the pages were failing and recommended rolling back. They were fine. The host rate-limits requests a server makes to itself, so the script was being throttled and reading that as a broken site. I checked from outside, confirmed every page was serving properly, and did not roll back. A check that cannot tell a broken site from a busy one is worse than no check, and fixing it is on the list.
A few more, briefly:
- Image uploads failed on the old live site with a server error. The uploads were succeeding; the server's image library took 185 seconds to resize one PNG that another library handles in under two, so the request timed out. Switching libraries took the upload from minutes to about a second.
- The mobile logo was a 1.25MB file for a spot 320 pixels wide. Replacing it with a 26KB version took the mobile Lighthouse score from 66 to 88 and cut the page weight roughly in half.
- Forty-six font-size settings pointed at values WordPress never actually outputs, so they did nothing. A plain heading was rendering at under 12 pixels. Nobody had noticed, which is its own lesson.
- During the build, the contact form sent messages but never told the visitor it had worked. I wrote about why that matters in why your website forms aren't converting.
Accessibility, past the automatic score
Lighthouse gave the finished site 100 for accessibility. A deeper scan the same day with axe-core still found two real problems: every page had its header and footer landmarks nested twice, which confuses screen readers, and the beer carousel could not be scrolled with a keyboard. Both went on the fix list that day.
That is the point I would want any business owner to take from this section. An automated score checks a subset of the rules, and a perfect one is not a pass. I cover what the standard asks for in what WCAG 2.1 AA means for a small business.
What I would tell another seasonal business
Build the site around how the business actually runs. If you open and close with the season, the site should do that in one move. If you are on the Quebec side, French comes first in the plan, not last. If the people updating it have another job, give them the five things they need and lock the rest. And whoever launches it should rehearse the launch on the real server, because the night I skipped that is the night it went wrong.
If you run a seasonal or tourism business on either side of the river and your site fights you every spring, tell me about it. I have been through this one start to finish.