September 1, 2026
Why did you rebuild Hugobricks?
Last year I explained why I built Hugobricks. I ended that post with a sketch of a better solution. Split a page into parts with a horizontal ruler and let the theme figure out what each part should look like. Form follows function.
That sketch kept nagging at me. I wanted proof that the concept actually works, because if it does, I will start building client websites this way. And there was a second itch: the Hugobricks logo has carried the subtext “The Ultimate Hugo Theme” from day one, a promise I felt the theme never quite lived up to. Those are the two reasons. Meet Hugobricks v2.
From shortcode soup to plain markdown
In the original Hugobricks, a page was a stack of shortcodes. Every brick was wrapped in an opening and a closing tag:
---
title: Page title
---
{{< brick_intro >}}
# Title 1
Your first paragraph with some lorem ipsum dolor sit amet.
{{< /brick_intro >}}
{{< brick_cta >}}
{{< /brick_cta >}}
It worked, and it was already a big step up from a database-driven page builder. But those shortcodes bothered me. They are not markdown. Forget a closing tag and your build breaks. Paste the content into another editor, another CMS or another theme and you are looking at tag soup. The layout decisions were still in the content, just wearing a developer costume.
In Hugobricks v2, the same page is plain markdown. Bricks are simply parts of the content, separated by three minus signs, which is a horizontal ruler in markdown:
---
title: Page title
---
# Title 1
Your first paragraph with some lorem ipsum dolor sit amet.

---
## Call to action
[Get started](/get-started/){:.button}
No opening tags, no closing tags, nothing to break. Remove the theme and you still have a perfectly readable document. That was the whole idea from the original post, written down in 2025 as a thought experiment and now running in production.
Form follows function, literally
The obvious question: if the content does not name any bricks, how does the theme know what to render? It looks at the content, exactly like I argued a designer should. Every content pattern has a function, and the function dictates the form.
A section that opens with an h1, a paragraph, a button and an image is clearly an introduction, so it becomes the intro brick. If that same image carries a ‘background’ class, the section is clearly meant as a hero, so it becomes a header. If the h1 section is the only section on the page, it renders as a single wide brick, because that is what a plain article wants to be. These recognition rules live in the site configuration, so the developer, the person responsible for the design, decides how content patterns map to bricks. The editor just writes.
This is the point where Hugobricks v2 stops being a page builder. The editor no longer builds pages, they write content, and the design responds to it. The plumber writes about plumbing; the living room stays decorated by the decorator.
A pinch of Kramdown
Careful readers will have noticed two oddities by now: an image that “carries a ‘background’ class” and a link followed by ‘{:.button}’. Standard markdown has no syntax for either. Jekyll veterans will recognize it immediately: it is Kramdown’s inline attribute list, and Hugobricks v2 brings it to Hugo.
[Get started](/get-started/){:.button}
{:.background}
Append ‘{:.classname}’ to a link or an image and the rendered element gets that class. A link with the ‘button’ class renders as a button. An image with the ‘background’ class becomes the backdrop of its section, which is also how the brick recognition tells a header from an intro. Hugo’s markdown engine, Goldmark, supports attribute lists on headings and block elements, but not on inline links and images, so Hugobricks adds it with its own render hooks and a small post-processing pass.
Is this a design decision sneaking back into the content? I don’t think so. “This link is the primary action” and “this image is a backdrop” are statements about function, not form. The theme still decides what a button looks like and how a backdrop is cropped, scrimmed and scaled. That is exactly the division of labor I was after: the editor annotates meaning, the theme translates it into design. And because it is just a class in curly braces, any other markdown tool will simply ignore it. Your content stays portable.
When you do want control
Sometimes automatic recognition is not what you want, and I promised in the original post that explicit control should remain available as a last resort. So it is. You can name a brick right on the divider:
---.cta
That forces the section into the cta brick, whatever its content looks like. There are around twenty bricks to choose from: intro, header, features, prices, quote, reviews, team, contact, and so on. Bricks that you reuse across pages, like a call to action, can live in a shared bricks folder and be pulled in with a named divider with no content below it.
And for the parts of a page that are data rather than prose, think pricing tables, review carousels and forms, there are still shortcodes that read from Hugo’s data folder. Data is data; it never belonged in a markdown paragraph anyway.
What this buys you
Portability. Your content is now boring, standard markdown. It survives a theme switch, a CMS switch, even a generator switch. Try that with an Elementor export.
Unbreakability. There is no tag an editor can forget to close. The worst thing that can happen is a section rendering as a different brick than intended, and even then the content is still on the page.
And most importantly: the separation between layout and design is no longer a discipline you have to maintain, it is a property of the system. The content holds the order of the sections and nothing else. Everything visual lives in the theme, where it belongs.
Try it
Hugobricks v2 is up for preview and the source is on GitHub. Like the original, it is free, built with vanilla CSS and it scores 99+ on Google PageSpeed. It is my test bench: once the concept has proven itself here, it is how I will build client websites too. Kick the tires and tell me what you think.
Happy coding!
() Joost van der Schee