The vocabulary is closed, not a cage. Your product will need a datepicker, a kanban board, a file uploader. This is the supported path, and the linter checks it.
The cheapest extension is the one you do not write. Five agents given the same page all asked for a footer component. Four of the five had already written the identical thing without it:
<footer data-layout="grid" data-cols="3" data-gap="xl" data-padding="xl">They wanted a name, not a capability. That is why a component earns its place when generations diverge without it, never when people ask.
An extension takes data-x-component, not data-component. The vocabulary stays closed and yours stays visibly yours, written in the same grammar instead of falling back to classes.
<div data-x-component="datepicker" data-padding="md" data-gap="sm">
<div data-x-slot="grid">…</div>
</div>This is what decides whether your component reads like Canon or like a guest. data-gap, data-padding and data-size already work on your element because they are token-driven.
When Canon does not have the value you need, because a calendar day is unavailable and no closed set was going to predict that, use data-x-variant and data-x-state, which take anything kebab-case. Regions are data-x-slot.
The CSS goes in @layer canon.app, a layer Canon declares and leaves empty for you.
@layer canon.app {
[data-x-component='datepicker'] {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: var(--space-xs);
background: var(--color-surface-raised);
border-radius: var(--radius-md);
}
}canon-lint fails on a hardcoded colour or spacing there, because those are what the theme controls. A border width or a grid template is yours and nobody is checking it.
✓ 12 files clean
9 rules in @layer canon.app. That is what the vocabulary does not cover here.
2 extension components: datepicker, kanban-boardThat number is meant to be read. A small app layer means Canon fits your product. A large one means it does not, which is a bug report rather than something to live with.
npx -p canoncss canon-init --extension date-fieldA compact date picker as a zero-JS <details> popover. Canon has no datepicker and by the admission test should not: four independent generations converged on the shape without one. What they did not converge on was the look, and all four produced a month-sized block sitting inline in the page, reflowing the form every time it opened.
The other direction. Structure is closed, the aesthetic is retargeted through token overrides, and that is measured rather than claimed: two opposite brands, each overriding 49 tokens with only two landing on the same value, produced markup that was 96% structurally identical.
npx -p canoncss canon-init --theme institutionalTwo ship with the package. institutional is navy, square-cornered and tight; soft is coral, very rounded and airy. Deliberately as far apart as two brands get, so the pair shows the range rather than two tastes of the same thing. Omit the flag for a blank one to fill in.
A theme that misspells a token is caught too. --color-brnd is valid CSS that overrides nothing and produces a page that looks almost right with no visible cause, so the linter names it and suggests the token you meant.