Reusability & Composition
Nana components are designed to be consumed through one stable contract —
attributes, slots, and events — and themed through design tokens, CSS variables,
and ::part(). Stay on that surface and the same component drops into any
screen, in any framework, with no forking and no shadow-DOM hacks.
How it flows
Section titled “How it flows”Tokens flow in, the component exposes a public API, your app composes it — and you theme once at the edges.
design tokens component (public API) your app ┌──────────────┐ ┌───────────────────────────┐ ┌──────────────┐ │ --nana-color │ │ attributes variant/size │ │ <nana-card> │ │ --nana-space │ ───▶ │ slots header/body │ ───▶ │ <nana-…/> │ │ --nana-radius│ │ events nana-change │ │ </nana-card>│ └──────────────┘ └───────────────────────────┘ └──────────────┘ ▲ ▲ style via ::part / CSS vars │ └────────────────────┴─── theme once, reuse everywhere ──┘1 · One component, many uses (attributes)
Section titled “1 · One component, many uses (attributes)”Behaviour and appearance are props. No variant means no new component, no copy-paste, no custom CSS.
<nana-button variant="primary">Primary</nana-button><nana-button variant="secondary">Secondary</nana-button><nana-button variant="ghost">Ghost</nana-button><nana-button variant="danger" size="sm">Delete</nana-button><nana-button variant="primary" loading>Saving…</nana-button>2 · Compose with slots
Section titled “2 · Compose with slots”Structure comes from slotted children, so you assemble layouts from primitives instead of reaching inside them.
<nana-card> <nana-card-header> <nana-card-title>Monthly statement</nana-card-title> <nana-card-description>June 2026</nana-card-description> </nana-card-header> <nana-card-content> Your balance increased by 4.2%. </nana-card-content></nana-card>3 · Theme once, reuse everywhere
Section titled “3 · Theme once, reuse everywhere”Restyle through the three supported layers below — never by targeting shadow-DOM internals directly. A token change re-skins every instance at once.
:root { --nana-color-primary-600: #0f1f1d; --nana-radius-md: 8px;}nana-table { --nana-table-pad-x: 1rem; --nana-table-pad-y: .5rem; }nana-dialog::part(panel) { box-shadow: 0 20px 60px rgb(0 0 0 / .2); }4 · Reusable icon registry
Section titled “4 · Reusable icon registry”The icon set is a shared registry: register a glyph once and every <nana-icon>
can use it by name. The bundled glyphs are code-split — they load on demand, so
apps that register only their own icons never ship the full set.
import { registerIcons } from "@nana-tec/ui-components";registerIcons({ github: { viewBox: "0 0 24 24", paths: ["M12 .5C5.37.5 …"] },});5 · Stay clean
Section titled “5 · Stay clean”If you find yourself writing !important or piercing the shadow DOM, there’s
almost always a prop, slot, or part for it.
<nana-icon name="settings" size="sm"></nana-icon><nana-button variant="ghost" size="sm" icon-only> <nana-icon name="more_vert"></nana-icon></nana-button><nana-icon name="settings" class="!w-[16px] !h-[16px]"></nana-icon>| Need | Reach for | Not |
|---|---|---|
| Different look / state | variant, size, loading attributes | Cloning the component |
| Custom structure | Slots (header / body / footer) | Wrapper divs around internals |
| Brand colours / spacing | Design tokens + documented CSS vars | !important overrides |
| Style an internal part | ::part() | Piercing the shadow DOM |
| A new glyph | registerIcon() once, reuse by name | Inline one-off SVGs everywhere |