Component Library
The KAINYNE design system — native Custom Elements themed via the shared --kn-* token sheet. Every component is template-customizable through HTML attributes, CSS custom properties, and slotted content. Foundation pieces below; forms, menus, and page templates ship in follow-up RFEs (SUP-024).
<kainyne-button>
Foundation button. Wraps a native <button> inside a shadow root so its press-feedback styling is independent of game-specific CSS. Attributes: variant (solid / outline / ghost), size (sm / md / lg), tone (neutral / accent / danger / muted), disabled, type.
Live stage
Tone × variant matrix
Size scale
Disabled state
<kainyne-frame>
Foundational container / card. Slots: header, default (body), footer. Each slot wrapper auto-hides when its slot is empty so a body-only frame doesn't render two empty bars. Attributes: tone, padding (none / sm / md / lg), radius (none / sm / md / lg), border (boolean).
Live stage
Frame body — slot content composes freely. Try the controls below to flip every attribute.
Footer status lineBody-only frame (header / footer slots hide automatically)
Body-only frame — neither header nor footer is provided, and the shadow-DOM wrappers stay hidden.
<kainyne-carousel>
Light-DOM Custom Element that wraps the existing src/carousel-core.js factory. Inherits the PTR-047 single-interval contract + PTR-093 self-heal on DOM detach. Attributes: auto-advance-ms (default 6000), resume-grace-ms (default 8000), aria-label (forwarded to the inner region). Children of the element are treated as slides.
Three-slide demo
Forms
Four form primitives: <kainyne-input> (text / email / password / number / tel / url / search), <kainyne-select> (clones light-DOM <option> children into a shadow-DOM <select>), <kainyne-checkbox> (with indeterminate support), and <kainyne-form> (Light-DOM wrapper for theme-consistent spacing). Every field carries label-text, required, disabled, invalid attributes plus help-text / error-text slots. Note: native form-data aggregation across shadow-rooted fields is deferred to a follow-up RFE — for now hook the submit event and pull values from the shadow-DOM fields directly.
kainyne-input — all variants
Live stage — flip invalid on the input
Theming
Every component reads its surface from CSS custom properties prefixed --kn-*, declared at :root in /src/ui/tokens.css. The tokens default to the existing --bg / --accent / --font-body brand vars via var(fallback), so per-game :root overrides flow through to components without the games needing to know about --kn-* names.
Per-instance override (inline style)
<kainyne-theme> — palette wrapper (REQ-570)
Wrap any subtree with <kainyne-theme palette="..."> to apply a token-override palette to its descendants. CSS variables inherit through Shadow DOM, so every nested <kainyne-*> component picks up the override.
<kainyne-window> (REQ-565)
Modal overlay built on the native <dialog> element. Focus trap + Esc-to-close come free from dialog.showModal(); scrim-click-to-dismiss is layered on top. Attributes: open, label, dismissable, size (sm / md / lg / full).
This is a modal built on the native <dialog> element. Try pressing Esc, clicking the scrim, or using the buttons below.
Esc and scrim-click are disabled. Use the button below to close.
<kainyne-menu> (REQ-566)
Accessible dropdown / popover. Arrow keys cycle focus through items; Esc closes; click-outside dismisses. Attributes: open, placement (top / bottom / left / right). Slots: trigger, default (items).
Page templates (REQ-567..554)
Layout-level primitives so a new page is a few <kainyne-page-*> tags rather than copy-pasted scaffolding.
<kainyne-page-hero>
<kainyne-page-section> + <kainyne-page-grid>
variant × size × tone
header / body / footer
swipe + autoplay
required / invalid / disabled
clones option children
checked + indeterminate
<kainyne-frame> cards in to extend.
Round-3 components
Form-gap fillers (<kainyne-radio> / <kainyne-radio-group> / <kainyne-textarea> / <kainyne-switch>), disclosure pack (<kainyne-tabs> / <kainyne-accordion> / <kainyne-tooltip>), transient notifications (<kainyne-toast>), and inline labels (<kainyne-badge> / <kainyne-chip> / <kainyne-tag> / <kainyne-avatar>).
Form gap — radio / textarea / switch
Disclosure — tabs + accordion + tooltip
Overview content — tabs auto-show the matching panel.
Mechanics content — ArrowLeft / ArrowRight cycles tabs.
Lore content — Home / End jump to the bounds.
What is Tabletop OS?
A modular rule architecture (see /design/).
How are components customized?
Via --kn-* tokens, attributes, and slots.
Do components need a build step?
No — every file is loaded directly by the browser.
Transient notification — toast
Inline labels — badge / chip / tag / avatar
<kainyne-font> (REQ-571)
Declarative font registration helper. Set family, href, optional preconnect + token; on connect the element injects the matching <link> tags into <head> (idempotent) and declares a --kn-font-<token> CSS custom property on itself so descendant components can opt in via font-family: var(--kn-font-<token>);. CSP note: every font origin MUST be allow-listed in the page's style-src / font-src directives.
Default body font.
Default mono token (--kn-font-mono).
Game primitives (REQ-596..600)
First round of card-game-surface components: playing card with rank/suit/face-down/interactive, fan/row/stack hand layout, labeled score with delta-flash, multi-player turn indicator, countdown timer with one-shot expire event.