How to Build This Site
Learn how crafta.beer works, how to run it locally, and how to add your own tutorials using the block-based content system.
What is Craft a Beer?
What is Craft a Beer?
Craft a Beer (crafta.beer) is an open-source Nuxt 3 site that provides step-by-step DIY tutorials for homebrewing electronics projects. It helps brewers build things like fermentation temperature controllers and gravity-logging bridges using affordable microcontrollers.
The Block-Based Tutorial System
The site's core architectural idea is that tutorials are assembled from reusable content blocks. Instead of writing each tutorial as a single monolithic page, you write small, self-contained steps — called blocks — and then compose them into tutorials by listing the blocks in order.
This means that when two tutorials share a step (e.g., flashing firmware), you write that step once and reference it from both tutorials. If the flashing process changes, you update one file and every tutorial that uses it gets the fix automatically.
Tech Stack
The site is built on:
- Nuxt 3 — the Vue.js framework that handles routing, rendering, and the dev experience
- @nuxt/content — reads markdown and YAML files from the
content/directory and makes them queryable - @nuxtjs/i18n — provides internationalization support with locale-prefixed routes
- Tailwind CSS — utility-first CSS framework with the Typography plugin for rendered markdown
How Tutorials Work
A tutorial is a YAML file that declares metadata (title, difficulty, estimated time) and an ordered list of block paths. At render time, the useTutorial composable fetches the YAML definition plus all the referenced blocks in a single query, then assembles them into a numbered step-by-step page.
Prerequisites
Parts Needed
| Part | Qty |
|---|---|
| Node.js 22.x (LTS) | ×1 |
| Git | ×1 |
| A code editor (VS Code, WebStorm, etc.) | ×1 |
Prerequisites
Before you can run the site locally, you'll need a few tools installed on your machine.
Node.js
The site requires Node.js 22.x (the current LTS release). You can check your version with:
node --version # should print v22.x.x
npm --version # should print 10.x.x or later
If you need to install or update Node.js, grab it from nodejs.org or use a version manager like nvm or fnm.
Git
You'll need Git to clone the repository. Most systems have it pre-installed. Verify with:
git --version
Code Editor
Any editor works, but VS Code with the Vue and Tailwind CSS extensions provides the best experience. WebStorm also has excellent built-in Vue and Tailwind support.
Clone and Run the Dev Server
Clone and Run the Dev Server
Let's get the site running on your machine.
Clone the Repository
git clone https://github.com/thorrak/craftabeer.git
cd craftabeer
Install Dependencies
npm install
This installs Nuxt, Vue, Tailwind CSS, the Content and i18n modules, and everything else the project needs. It also runs nuxt prepare automatically via the postinstall script, which generates the .nuxt/ directory with TypeScript types.
Start the Dev Server
npm run dev
The dev server starts at http://localhost:3000 with hot module replacement. Changes to Vue components, pages, composables, and content files will reflect immediately in the browser without a full reload.
Verify It Works
Open http://localhost:3000 in your browser. You should see the Craft a Beer landing page. Try navigating to /browse to see the wizard and tutorial listings.
Understand the Project Structure
Understand the Project Structure
Here's how the codebase is organized:
craftabeer/
├── content/en/ # All content files
│ ├── blocks/ # Reusable tutorial steps (markdown)
│ │ ├── brewpi/ # BrewPi-specific blocks
│ │ └── tiltbridge/ # TiltBridge-specific blocks
│ ├── tutorials/ # Tutorial definitions (YAML)
│ │ ├── brewpi/ # BrewPi tutorial definitions
│ │ └── tiltbridge/ # TiltBridge tutorial definitions
│ └── guides/ # Standalone guide articles (markdown)
├── pages/ # Nuxt file-based routing
├── components/ # Vue components
├── composables/ # Shared reactive logic
├── config/ # Wizard configuration (YAML)
├── server/ # API routes (serves wizard config)
├── types/ # Shared TypeScript interfaces
├── assets/css/ # Global styles (Tailwind entry point)
├── locales/ # i18n translation files (JSON)
└── public/ # Static assets (favicon, images)
Key Directories
content/en/blocks/ — Each markdown file is a single tutorial step. Blocks are organized by project (e.g., brewpi/, tiltbridge/). A block can be referenced by any tutorial, even one in a different project folder.
content/en/tutorials/ — YAML files that define a tutorial's metadata and its ordered list of blocks. The slug field determines the URL path.
composables/ — Houses the core logic: useTutorial assembles blocks into tutorials, useWizard manages the project selector, and useBookmarks handles localStorage-based bookmarking.
config/ — The wizard configuration (wizard.en.yml) defines the dropdown options and maps selection combinations to tutorial slugs.
Write a Content Block
Write a Content Block
Blocks are the building blocks (pun intended) of every tutorial. Each block is a markdown file with YAML frontmatter.
Create a New Block
Create a markdown file in the appropriate project folder under content/en/blocks/. For example, to add a block for a new project called "keezer":
content/en/blocks/keezer/what-is-a-keezer.md
Block Frontmatter
Every block needs a title and estimatedTime in its frontmatter:
---
title: "What is a Keezer?"
estimatedTime: "3 mins"
---
# What is a Keezer?
A keezer is a chest freezer converted into a draft beer dispenser...
Optional: Parts List
If your block involves gathering components, add a parts array to the frontmatter. The tutorial renderer will display these as a shopping list:
---
title: "What You'll Need"
estimatedTime: "5 mins"
parts:
- name: "ESP32 DevKit"
link: "https://example.com/esp32"
quantity: 1
- name: "Temperature sensor"
quantity: 2
---
Writing Tips
- Keep blocks focused on a single step or concept
- Write as if the reader has completed the previous steps in the tutorial
- Include code blocks, commands, or wiring diagrams where helpful
- Blocks should make sense on their own — another tutorial might reuse yours in a different order
Create a Tutorial Definition
Create a Tutorial Definition
A tutorial definition is a YAML file that ties blocks together into a complete, ordered tutorial.
Create the YAML File
Create a new file in the appropriate project folder under content/en/tutorials/. For example:
content/en/tutorials/keezer/keezer-basic.yml
Tutorial Definition Format
slug: "keezer/keezer-basic"
title: "Build a Basic Keezer Controller"
description: "Turn a chest freezer into a temperature-controlled keezer."
image: "/images/tutorials/keezer-basic.jpg"
category: "Electronics"
difficulty: "beginner"
estimatedTime: "60 mins"
tags:
- keezer
- esp32
- temperature-control
blocks:
- keezer/what-is-a-keezer
- keezer/what-you-need
- keezer/wiring
- keezer/firmware-flash
- keezer/testing
The slug must match the file's path relative to content/en/tutorials/ (without the .yml extension). The blocks array lists block paths relative to content/en/blocks/.
Block Overrides
Sometimes a shared block needs a small addition for a specific tutorial. Instead of duplicating the block, use blockOverrides to append a note:
blockOverrides:
keezer/what-you-need:
appendNote: "For this build you'll also need a chest freezer and an inkbird-style temperature probe."
The override note is displayed below the block's content, giving tutorial-specific context without modifying the shared block.
Wire It Into the Wizard
To make your tutorial discoverable through the wizard on the /browse page, add a route entry in config/wizard.en.yml that maps a selection combination to your tutorial's slug. See the next step for details.
Configure the Wizard
Configure the Wizard
The wizard is the interactive project selector on the /browse page. It guides users through a series of dropdowns to find the right tutorial. Configuration lives in config/wizard.en.yml.
Wizard Config Structure
The config has three sections:
selectors: # The dropdown menus
routes: # Maps selection combos → tutorial slugs
fallback: # Message shown when no tutorial matches
Adding a New Project
To add a new project to the first dropdown, add an entry under selectors[0].options:
selectors:
- id: project
label: "I want to build a"
options:
- id: keezer-controller
label: "Keezer Controller"
Adding Skill Options
The second dropdown shows different options depending on the selected project. Add your options under selectors[1].optionsByProject:
- id: skill
connective: "And I"
optionsByProject:
keezer-controller:
- id: no-solder
label: "don't want to solder anything"
- id: solder-ok
label: "am comfortable soldering"
Adding Routes
Map each selection combination to a tutorial slug:
routes:
"keezer-controller+no-solder": "keezer/keezer-no-solder"
"keezer-controller+solder-ok": "keezer/keezer-soldered"
Use * as a wildcard to match any option in a position:
"keezer-controller+*": "keezer/keezer-basic"
Testing
After editing the wizard config, reload the /browse page. Select your new project from the first dropdown and verify that the second dropdown shows the correct options and that the matched tutorial appears.
Build and Deploy
Build and Deploy
Once you're happy with your changes locally, here's how to get them into production.
Production Build
Run a production build to verify everything compiles cleanly:
npm run build
This generates the .output/ directory using the node-server Nitro preset. You can preview the production build locally with:
npm run preview
Deploy
The site auto-deploys when you push to the main branch. A GitHub webhook notifies the server, which pulls the latest code, installs dependencies, builds, and restarts the application via PM2.
git add .
git commit -m "Add keezer tutorials"
git push origin main
That's it — your changes will be live on crafta.beer within a minute or two.
Static Generation (Optional)
If you prefer static hosting instead of a Node server, the site can also be statically generated:
npm run generate
This produces a fully static site in .output/public/ that can be deployed to any static host (Netlify, Vercel, GitHub Pages, etc.).