Extending
The grammar your own components follow to stay on-system.
Extending keel — the grammar
New components follow the same contract the shipped ones do:
- Names follow
k-block__element--modifier(BEM) — drop thek-prefix for your own so it's clear what shipped and what didn't. - Components consume tokens, never raw values. No hex colors, no pixel paddings — every visual decision points at the tokens layer, so your component rethemes with the rest of the page.
- Expose your own knobs with token defaults —
var(--my-knob, var(--k-token))— so others can retheme your component the same way you retheme keel's.
A complete worked recipe — a testimonial component, defined in
this page's own style block and rendered live below:
.testimonial {
background: var(--k-surface);
border: 1px solid var(--k-border);
border-radius: var(--k-radius-l);
padding: var(--k-space-5);
}
.testimonial__quote {
font-size: var(--k-text-l);
color: var(--k-text-soft);
margin: 0 0 var(--k-space-3);
}
.testimonial__name {
font-family: var(--k-font-mono);
font-size: var(--k-text-xs);
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--k-text-faint);
}
Twelve lines of CSS, and it already looks like it shipped with the framework — because it's written in the same words.
This is the same grammar keel itself is written in; read
keel.css and you already know how to extend it.
What's missing
keel ships zero JavaScript, so anything that needs a script is
deliberately absent. Dropdowns, tooltips, and toasts used to be on
this list — native details, a CSS attribute, and
popover now cover them above, trade-offs stated.
What's still missing:
- No tab panels.
.k-tabsships the bar, but there is no accessible zero-JS panel-switching pattern keel is willing to ship. Point the tabs at separate pages, use a.k-accordion, or write the three lines of JavaScript yourself. (Carousels used to be on this list too —.k-gallerynow covers them with native scroll-snap.) - No form submission handling. keel styles fields and validation states; what happens on submit — client-side or otherwise — is yours.
- No modal-trigger JS. The base layer styles
dialogand.k-drawer; the one line ofshowModal()wiring is yours to write.