Canon
Component library for internal tools: tables, forms, records.
The markup is the contract.
Canon builds tables, forms and dialogs once, in plain HTML: the browser does the work, a theme decides the look, and the markup outlives both. Made for the admin screens that Django, Rails, Laravel, Go or htmx already render.
Write it once, keep it
A
<button>written today still works in ten years. Canon is unopinionated about your stack and your look: it's markup, so it works with whatever renders it and outlives the framework of the moment.Accessible by nature
This is where Canon is opinionated: names, roles, focus and keys are part of each component's markup. No one has to remember them, and no theme can lose them.
Less JavaScript every year
Browsers keep doing more on their own. Canon uses what they do, and its script steps back as they catch up.
Works the way you expect
A table should read like a table, a form should never lose what was typed, and the keyboard should always work.
Who it's for
Canon is for teams with:
- A back end and no front-end team
- Django, Rails, Laravel and Go templates already write HTML, and that's all Canon needs: two lines in the page, no build step, no client framework.
- Several stacks
- A React app, a server-rendered admin and an old internal tool write the same elements and attributes, and get the same components.
- A brand to follow, or several
- One stylesheet decides the look. A rebrand, a client's colors or a new design system is a new theme; the pages stay as they are.
- Tools that must last years
- Plain HTML has no framework version to keep up with, and a script that fails to load doesn't take the screens with it.
You say what it is. The theme says how it looks.
A button becomes what its attributes say. The theme draws it.
<link rel="stylesheet" href="css/canon/canon.css">
<button type="button" class="canon-button">
<span class="canon-button-label">Save</span>
</button>
The Components
67 of 68 components work without JavaScript.
Each component's specification gives its tier, from T0 to T3: what it does with HTML and CSS alone, and what canon.js adds. When a script doesn't load, the page still works.
- T0 HTML and CSS
- 49 components
- T1 Browser features
- 7 components
- T2 Better with JavaScript
- 11 components
- T3 Needs JavaScript
- 1 component
What Canon leaves to others, on purpose.
Canon is not:
- A JavaScript component library
- There's nothing to import or call, and no build step. Your framework or server renders the HTML.
- Styled in the markup
- Markup says what a thing is (
data-intent), never how it looks: no “outline”, “rounded” or “blue”. - An app framework
- No routing, data loading or state. canon.js keeps the interface honest; saving and fetching are your app's.
- For marketing sites
- Tables, forms, records and dashboards. Hero sections and pricing tables are out of scope.
Origins
The name comes from fiction, where canon is the body of work officially accepted as authoritative within a story's universe. Here, the markup is canon. Themes can retell it however they like, but what the markup says stays true in all of them.
In 2003, Dave Shea published CSS Zen Garden: one HTML document, and a gallery of designs that changed nothing but the stylesheet. Each looked like a different site, and the markup never moved.
Canon is an homage to that page. It asks the same of the things internal tools are made of: write a table, a form or a dialog once, then let a theme redraw all of it without touching a line of HTML.
Start
Two lines, then the markup each component's page shows.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/canon-ui@0.2.1/css/canon/canon.css">
<script src="https://cdn.jsdelivr.net/npm/canon-ui@0.2.1/js/canon.js" defer></script>
Or install it from npm.
npm i canon-ui