For almost four years I worked as a full-stack developer on a large WordPress site. It combined e-commerce and e-learning, and over time it grew to more than 60 plugins. By the end, even simple changes were stressful. A theme update could break a page, a plugin update could break the theme, and nobody could tell in advance what a change would touch.
The site wasn't badly built. The problem was structural: content, design and features had grown into each other. That's the question I started Umono with. What if those layers had hard boundaries, enforced by the system instead of by everyone's discipline?
Umono is an open-source CMS (Go, SQLite, MIT) that I've been building on my own since 2024. This post is about the idea behind it, not the feature list.
Three layers, three jobs
- Content says what something means.
- A theme decides how it looks.
- A plugin adds what it does.
In most CMSs each of these can reach into the others. A theme ships templates and logic, so switching themes can change what a page shows. A plugin writes its own markup into your content, and when the plugin goes, that markup stays. In Umono each layer gets only what its job needs, and nothing more.
Content only says what it means
Pages are written in Compono, a Markdown-based language with components. Its Markdown surface is closed: there is no raw HTML and no way to style anything from the content. A component is reusable content:
{{ SAY_HELLO name="World" }}
~ SAY_HELLO name="Guest"
# Hello, {{ name }}!
That renders <h1>Hello, World!</h1>. Component names never appear in the output, and Umono adds no classes of its own. The result is plain, semantic HTML. Since the content carries no design, it doesn't care which theme displays it.
A theme only styles
A theme is a package of CSS, fonts and images. No templates, no scripts. This is a deliberate limit, and Umono checks it when a theme is built, installed and activated:
- A theme has no JavaScript and can't change how anything works.
- It can't add words, not even with CSS
content. Text, link targets and accessibility attributes belong to the site owner. - It loads nothing from another origin: no fonts from a CDN, no remote images. The page's Content-Security-Policy enforces it.
- It can't touch anything outside the page's root element,
<head>included.
What a theme styles is plain HTML like this:
<umono-root>
<header>
<a href="/">My website</a>
<nav aria-label="Main menu">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main>
<h1>Hello</h1>
<p>This is my first page, written in <strong>Compono</strong>.</p>
</main>
<footer>
<p>Made with Umono</p>
</footer>
</umono-root>
And a theme is CSS against it:
:scope {
display: flex;
flex-direction: column;
background-color: #fdfcf8;
color: #1f1d1a;
}
nav ul {
display: flex;
gap: 1.25rem;
list-style: none;
}
a[aria-current="page"] {
font-weight: bold;
}
The payoff for these limits is that switching themes is safe. Activating another theme replaces the old one in one step, without touching a single page.
The boundary is a public contract
How does a theme know which HTML to expect? From a contract. A contract is a TOML file published on umono.dev that describes a kind of site:
-
Layouts, the structure of a page. A blog has
post,post-listandpage. - Parameters, everything a layout shows: the site name, the menu, the title, the content. They all come from the site owner. A contract writes no text of its own, so it works in every language.
- Components that the layouts share.
A theme picks one contract, and three rules keep that contract trustworthy:
- A published contract never changes and is never removed. A new structure comes as a new contract version.
- Contracts are MIT-0. Anyone can write a theme for any contract. Nobody controls who is allowed to style your site.
- The HTML is frozen. The same content produces the same elements in every Umono release until v1. Fixes to the output are collected and land once, in v1. A theme written today keeps working.
Umono also doesn't take a theme's word for it. It detects which features a theme actually uses and refuses one that declares less than it uses.
This is what I mean by no vendor lock-in. Your content isn't written for a theme, and a theme isn't written for your site. Both are written against a public contract, so either side can be swapped.
Plugins: the same idea, not built yet
Plugins are planned the same way. A plugin will implement a published schema, run as a separate service and get only the access the site owner grants it. Plugins for the same schema will be interchangeable, like themes for the same contract. This is still in design, and I'd rather say so than sell it.
Your site stays yours
Umono is one Go binary and one SQLite file on your own server. Public pages set no cookies and make no third-party requests. Fonts are served by the site itself.
What it costs
Hard boundaries have a price, and I don't want to hide it:
- A theme can't add a feature. If a contract has no place for something, a theme can't invent one. That takes a new contract version.
- Compono is a language you have to learn. Today it suits technical users more than editors.
- Only the official contracts exist so far, with one official theme each: Octavo for simple sites and Folio for blogs.
- There is no plugin system yet.
I think the trade is worth it, but it is a trade, and I'd like to hear where it hurts.
Where it is, and how you can help
Umono has had regular releases since November 2024, and umono.io runs on it. Version 0.9 brought the theme system. It isn't finished, and development continues.
-
Try it.
curl -fsSL https://umono.io/install.sh | sh, then the docs walk you from an empty directory to a site with a theme: https://docs.umono.dev/getting-started/your-first-site/ - Write a theme. The contracts are open, and the first-theme tutorial takes about fifteen minutes: https://docs.umono.dev/themes/your-first-theme/
- Tell me where the idea breaks. Is a contract between content and theme the right boundary, or too rigid for real sites? GitHub Discussions: https://github.com/umono-cms/umono/discussions
- Share it. If the idea resonates, a star or a share helps a one-person project get seen: https://github.com/umono-cms/umono
I wrote this post with help from an AI assistant. The ideas, the design and the decisions behind Umono are mine.
Top comments (0)