Documentation
Savanna — the design system
A proudly-African, dark-first design system — palette grounded in deep teal-green, accented with Kente gold, emerald, clay and indigo. Ships as a suite of installable packages usable in React, Next, Vue, Svelte, Angular or plain HTML. This page documents every layer: tokens, CSS, fonts, motifs, 91 components and Web Components.
Overview
What Savanna is, and how it's built
Savanna is layered like MUI: a framework-agnostic core (tokens, CSS, icons, motifs, fonts) plus framework bindings. The core is MIT-licensed and free; the advanced React components are commercial. You only install the packages you need.
Two variable namespaces exist by design, and it's important to know which you're using:
--tz-*— the React design system. Loaded by@kwacha-kulture/savanna/styles/theme.cssand consumed by the@kwacha-kulture/reactcomponents (e.g.--tz-bg,--tz-border,--tz-shadow-md).--sv-*— the framework-free CSS layer. Loaded by@kwacha-kulture/savanna-cssand consumed by thesv-*classes and Web Components (e.g.--sv-gold,--sv-bg).
The @kwacha-kulture/savanna-tokens package is the single source of truth for the raw values behind both, and also emits a tokens.css (--sv-color-*, --sv-radius-*…) and a W3C tokens.json for design tools.
The suite
Seven packages
| Package | Licence | What it gives you | Use in |
|---|---|---|---|
@kwacha-kulture/savanna-tokens | MIT | Design tokens as JS/TS, CSS custom properties and W3C JSON. Zero dependencies. | Any tool / framework |
@kwacha-kulture/savanna-css | MIT | The entire system as framework-free CSS + vanilla JS + inline-SVG motifs. | Any framework / plain HTML |
@kwacha-kulture/savanna-elements | MIT | Native Web Components (custom elements) built on the CSS layer. | Vue / Svelte / Angular / HTML |
@kwacha-kulture/savanna-icons | MIT | 318 Adinkra, Ndebele & UI icons as React components and raw SVG, with a registry. | React / SVG |
@kwacha-kulture/savanna | MIT | The React design system: tokens, African motifs, brand marks and theme.css (--tz-* variables). | React |
@kwacha-kulture/react | MIT | 54 base React components: buttons, cards, forms, overlays, tables, free charts and utilities. | React |
@kwacha-kulture/react-pro | Commercial | Advanced React components: data grid (+ Pro), spreadsheet, kanban, gantt, tree view, rich-text editor, world map, date/time pickers, pro charts. Requires a paid licence. | React |
Not on npm: the Savanna Display typeface is licensed separately — download the font kit from Type under a licence that covers it.
Setup
Installation
Install only what you need. Peer dependencies: the React packages need react and react-dom ≥ 19 and motion ≥ 12; everything else is dependency-free.
@kwacha-kulture/savanna-tokensMITDesign tokens as JS/TS, CSS custom properties and W3C JSON. Zero dependencies.
npm i @kwacha-kulture/savanna-tokens@kwacha-kulture/savanna-cssMITThe entire system as framework-free CSS + vanilla JS + inline-SVG motifs.
npm i @kwacha-kulture/savanna-css@kwacha-kulture/savanna-elementsMITNative Web Components (custom elements) built on the CSS layer.
npm i @kwacha-kulture/savanna-elements@kwacha-kulture/savanna-iconsMIT318 Adinkra, Ndebele & UI icons as React components and raw SVG, with a registry.
npm i @kwacha-kulture/savanna-icons@kwacha-kulture/savannaMITThe React design system: tokens, African motifs, brand marks and theme.css (--tz-* variables).
npm i @kwacha-kulture/savanna@kwacha-kulture/reactMIT54 base React components: buttons, cards, forms, overlays, tables, free charts and utilities.
npm i @kwacha-kulture/react@kwacha-kulture/react-proCommercialAdvanced React components: data grid (+ Pro), spreadsheet, kanban, gantt, tree view, rich-text editor, world map, date/time pickers, pro charts. Requires a paid licence.
npm i @kwacha-kulture/react-proGet going
Quick start, per framework
React / Next.js
Install @kwacha-kulture/react, import the stylesheet once (in your root layout / entry), then use components. They are client components — under the Next App Router they carry "use client", so import them into client boundaries.
// app/layout.tsx (or your entry)
import '@kwacha-kulture/react/styles.css';
// any component
import { Button, Card, Field, Input } from '@kwacha-kulture/react';
export function Example() {
return (
<Card>
<Field label="Email"><Input placeholder="you@africa.com" /></Field>
<Button variant="primary">Continue</Button>
</Card>
);
}For the design language (motifs, brand marks, the --tz-* theme) add @kwacha-kulture/savanna:
import '@kwacha-kulture/savanna/styles/theme.css';
import { KwachaMark } from '@kwacha-kulture/savanna/brand';
import { Sankofa } from '@kwacha-kulture/savanna/motifs';Pro components need a licence key set once at startup:
import { setLicenseKey, DataGridPro } from '@kwacha-kulture/react-pro';
import '@kwacha-kulture/react-pro/styles.css';
setLicenseKey(process.env.NEXT_PUBLIC_SAVANNA_LICENSE!);Vue · Svelte · Angular · plain HTML
Use the Web Components + the CSS layer. They work identically everywhere.
import '@kwacha-kulture/savanna-css/savanna.css';
import { defineSavannaElements } from '@kwacha-kulture/savanna-elements';
defineSavannaElements();
// then in any template:
// <sv-button variant="primary">Get started</sv-button>
// <sv-card>…</sv-card> <sv-badge tone="emerald">Live</sv-badge>No build step at all? Load from a CDN:
<link rel="stylesheet" href="https://unpkg.com/@kwacha-kulture/savanna-css/dist/savanna.css">
<script type="module">
import { defineSavannaElements } from 'https://esm.sh/@kwacha-kulture/savanna-elements';
defineSavannaElements();
</script>
<button class="sv-btn sv-btn-primary">Get started</button>Design tokens only (Tailwind, Figma, native…)
import { theme, palette, space } from '@kwacha-kulture/savanna-tokens';
// or CSS variables: import '@kwacha-kulture/savanna-tokens/tokens.css'; // --sv-color-* on :root
// or W3C JSON: @kwacha-kulture/savanna-tokens/tokens.json -> Style Dictionary / FigmaFoundation
Design tokens
Every value is a token. Consume them as JS/TS objects, as CSS variables, or as W3C JSON. The palette carries stops 50 → 900; components read the semantic theme, never raw stops.
Palette
| Family | 50 | 100 | 300 | 500 | 700 | 900 |
|---|---|---|---|---|---|---|
| terracotta | #FBEFEA | #F4D5C8 | #E08A66 | #C4582E | #8A3A1D | #4A1F10 |
| sienna | #FAEDE6 | #F1CCB7 | #D38057 | #A24A20 | #6E2F11 | #3A1808 |
| mahogany | #F4E6E1 | #E2BCB0 | #A86A57 | #7A3A2B | #4F2218 | #2A100A |
| ochre | #FBF1DA | #F4DCA1 | #E0AE4E | #B8841E | #7A560B | #402C03 |
| kenteGold | #FFF6D9 | #FFE89B | #F5CE5A | #E0A91D | #9C7308 | #523B02 |
| emerald | #E6F5EC | #B7E0C5 | #5DBE82 | #1F8A48 | #0F5128 | #062513 |
| indigo | #E8E7F5 | #BCB9DF | #6F69C0 | #3D3690 | #221E5C | #0F0D33 |
| clayRed | #FBE7E3 | #F2BAAF | #D9614F | #B83C2E | #7B2418 | #411008 |
| ink | — | #1B1813 | #100E0A | #070605 | — | — |
| charcoal | #EDEDEC | #C7C6C2 | #7A7873 | #3E3C38 | #26241F | #0F0E0B |
| bone | #FBF8F2 | #F4EEE0 | #D8CDB1 | #A89B7B | #6F6448 | #3A3220 |
Semantic theme theme.*
| Token | Value |
|---|---|
theme.bg | #0B0907 |
theme.bgRaised | #16140F |
theme.bgInset | #070605 |
theme.bgPattern | #100E0A |
theme.bgOverlay | rgba(7, 6, 5, 0.72) |
theme.text | #FBF8F2 |
theme.textStrong | #FFFFFF |
theme.textMuted | #D8CDB1 |
theme.textSubtle | #7A7873 |
theme.textInverse | #0B0907 |
theme.border | rgba(216, 205, 177, 0.14) |
theme.borderStrong | rgba(245, 206, 90, 0.42) |
theme.borderMotif | #9C7308 |
theme.primary | #E0A91D |
theme.primaryHover | #F5CE5A |
theme.primaryInk | #0B0907 |
theme.secondary | #C4582E |
theme.secondaryHover | #E08A66 |
theme.emerald | #1F8A48 |
theme.emeraldHover | #5DBE82 |
theme.indigo | #3D3690 |
theme.indigoHover | #6F69C0 |
theme.success | #1F8A48 |
theme.warning | #B8841E |
theme.danger | #B83C2E |
theme.info | #6F69C0 |
theme.focusRing | #F5CE5A |
theme.focusRingSoft | rgba(245, 206, 90, 0.35) |
Spacing, radii & shadows
| space | rem |
|---|---|
0 | 0 |
1 | 0.25rem |
2 | 0.5rem |
3 | 0.75rem |
4 | 1rem |
5 | 1.25rem |
6 | 1.5rem |
8 | 2rem |
10 | 2.5rem |
12 | 3rem |
16 | 4rem |
20 | 5rem |
24 | 6rem |
| radii | value |
|---|---|
none | 0 |
sm | 4px |
md | 8px |
lg | 14px |
xl | 20px |
pill | 999px |
| shadows | kind |
|---|---|
sm | warm, gold-tinted |
md | warm, gold-tinted |
lg | warm, gold-tinted |
glow | warm, gold-tinted |
Typography scale
| fontSizes | value |
|---|---|
display | 4rem |
h1 | 3rem |
h2 | 2.25rem |
h3 | 1.75rem |
h4 | 1.375rem |
h5 | 1.125rem |
h6 | 1rem |
body | 1rem |
small | 0.875rem |
caption | 0.75rem |
overline | 0.6875rem |
| fontWeights | value |
|---|---|
regular | 400 |
medium | 500 |
semibold | 600 |
bold | 700 |
black | 800 |
| lineHeights | value |
|---|---|
tight | 1.1 |
snug | 1.25 |
normal | 1.5 |
relaxed | 1.7 |
| letterSpacings | value |
|---|---|
tight | -0.02em |
normal | 0em |
wide | 0.04em |
overline | 0.18em |
Make it yours
Theming & dark mode
Savanna is dark-first by design. Re-branding means overriding a couple of accent variables — no rebuild needed.
Re-colour the CSS layer
/* your stylesheet, loaded AFTER savanna.css */
:root {
--sv-gold: #C4582E; /* new accent */
--sv-gold-soft: #E08A66;
--sv-emerald: #1F8A48;
}Prefer no code? Ship the templates' bundled customize.html — pick a preset or your own colours, preview live, and copy the CSS.
Re-colour the React system
Override the --tz-* variables from theme.css, or generate a fresh set from the tokens at build time with css(prefix) from @kwacha-kulture/savanna-tokens.
The CSS layer also supports a light theme on demand: set data-theme="light" on the <html> element (the bundled data-theme-toggle does this and persists it).
Type
Fonts
Three typefaces, bound to CSS variables so you can swap them:
| Role | Typeface | Variable |
|---|---|---|
| Display | Sora (headings, prices) | --sv-display |
| Body | DM Sans | --sv-body |
| Mono | JetBrains Mono | --sv-mono |
Loading options: (1) next/font in a React app, binding --font-display / --font-body / --font-mono; (2) the Google-Fonts <link> that @kwacha-kulture/savanna-css exports as FONT_LINK; or (3) self-host and point the variables at your files. A system fallback stack means everything still renders offline.
import { FONT_LINK } from '@kwacha-kulture/savanna-css';
// FONT_LINK is a <link> string for Sora + DM Sans + JetBrains Mono.A bespoke display face, Savanna Display, is sold separately (woff2/woff/ttf/otf) — it's a drop-in for the --sv-display / --font-display slot.
@kwacha-kulture/savanna-css
The framework-free CSS layer
Import savanna.css and compose with sv-* classes — in any framework or plain HTML. Theming variables are listed under Theming.
Buttons
| Class | Description |
|---|---|
sv-btn | Base button — carries the Kente stripe on its top edge. |
sv-btn-primary | Gold-gradient primary action (with gold glow). |
sv-btn-emerald · sv-btn-secondary · sv-btn-ghost | Emerald · clay · outline variants. |
sv-btn-lg · sv-btn-sm | Large / small sizes. |
sv-icon-btn | Square icon button; add a .sv-count child for a badge. |
Cards & surfaces
| Class | Description |
|---|---|
sv-card | Elevated card with the Kente top-stripe. |
sv-card-plain | Card without the stripe. |
sv-card-watermark | Faint motif watermark, absolutely positioned inside a card. |
sv-kente-strip | A woven 5-colour Kente divider bar. |
sv-kente-bg · sv-adinkra | Subtle Kente / Adinkra background texture on any container. |
sv-stat · sv-feature · sv-plan · sv-tile | Stat card · feature card · pricing plan · media tile. |
Forms
| Class | Description |
|---|---|
sv-input | Text input / textarea — gold left-edge + inset. |
sv-tag | Chip/tag with a gold left border. |
sv-pills · sv-pill | Pill group · a single pill (add .active). |
Badges & display
| Class | Description |
|---|---|
sv-badge | Base badge. |
sv-badge-gold · sv-badge-emerald · sv-badge-terracotta | Tone variants. |
sv-avatar | Avatar with the signature conic-gradient ring. |
sv-price | Display-font price text in gold. |
Layout
| Class | Description |
|---|---|
sv-container | Max-width (1160px) centred container with padding. |
sv-section | Vertical section rhythm (80px block padding). |
sv-grid + sv-cols-2/3/4 | Responsive grid; columns collapse on small screens. |
sv-row · sv-between · sv-center | Flex row · space-between row · text-centre. |
Typography
| Class | Description |
|---|---|
sv-eyebrow | Mono uppercase kicker with a Kente tick. |
sv-gold | Animated shimmering gold gradient text. |
sv-muted · sv-dim | Muted / dim text colour. |
sv-mono | Monospace (JetBrains Mono). |
Chrome & overlays
| Class | Description |
|---|---|
sv-header · sv-nav · sv-brand · sv-nav-links | Sticky header, nav bar, brand lockup, nav links. |
sv-app · sv-side · sv-topbar · sv-content | App shell: sidebar layout, top bar, content region. |
sv-menu · sv-menu-pop | Dropdown menu. |
sv-drawer · sv-scrim | Slide-in drawer (e.g. cart) + backdrop. |
sv-acc · sv-acc-q · sv-acc-a | Accordion (question / answer). |
sv-table | Data table (sortable headers via [data-sort]). |
sv-hero · sv-hero-img · sv-hero-canvas | Cinematic hero, ken-burns image bg, generative canvas. |
Theming variables
| Variable | Purpose |
|---|---|
--sv-bg | Deep teal-green ground (#0A1A1F). |
--sv-surface · --sv-surface2 · --sv-inset | Raised / raised-2 / inset surfaces. |
--sv-text · --sv-muted · --sv-dim | Text · muted · dim. |
--sv-gold · --sv-gold-soft | Kente-gold accent — override these to re-brand. |
--sv-emerald · --sv-clay · --sv-indigo | Secondary accents. |
--sv-line · --sv-line-strong | Borders. |
--sv-r-sm · --sv-r · --sv-r-lg · --sv-pill | Corner radii. |
--sv-shadow · --sv-glow-gold | Elevation shadow · gold glow. |
--sv-display · --sv-body · --sv-mono | Font families. |
--sv-kente | The repeating Kente gradient used on stripes and button edges. |
No framework needed
JavaScript behaviours
The CSS layer ships a dependency-free behaviour script. In a bundler call initBehaviors() once; in plain HTML include savanna.js. Then wire interactions declaratively with data-* attributes:
import { initBehaviors } from '@kwacha-kulture/savanna-css';
initBehaviors(); // idempotent; no-op on the server| Attribute / hook | Behaviour |
|---|---|
data-nav-toggle="#sel" | Toggle .open on the target element (mobile nav / sidebar). |
data-theme-toggle | Flip light/dark; the choice persists to localStorage. |
.sv-menu + .sv-menu-btn | Dropdown: click the button to open, click-away to close. |
data-cart-open / data-cart-close | Open / close the cart drawer (#sv-cart). |
data-add-to-cart data-name data-price data-img | Add an item to the persistent (localStorage) cart. |
data-cart-count / data-cart-subtotal / data-cart-list | Live targets: item count · subtotal · rendered item list. |
data-search-open (or press “/”) | Open the cross-page search overlay (#sv-ovl); reads an inline JSON index. |
data-search="#grid" / data-search-all | Live-filter [data-item] elements within a grid / across the page. |
data-filter-group="#grid" + data-filter | Filter pills that show/hide [data-tags] items. |
data-tabs (buttons) + data-panels | Tabbed panels: the nth button shows the nth panel. |
data-pills | Single-select pill group (e.g. product variants). |
data-bill-toggle + data-price-m / data-price-y | Monthly/annual pricing toggle that swaps the price text. |
data-form [data-endpoint] [data-redirect] [data-ok] | Validated form: inline errors, success state, optional POST + redirect. |
data-consent="1|dismiss" | Accept / dismiss the cookie-consent bar (#sv-consent). |
sv-reveal (+ sv-d1…sv-d4) | Fade-and-rise when scrolled into view (staggered delays). |
data-count [data-suffix] [data-prefix] | Animated count-up when the element enters the viewport. |
data-kente-canvas | Generative Kente/Adinkra particle field on a <canvas>. |
table.sv-table th[data-sort] | Click a header to sort the column. |
Heritage
African motifs
Real Adinkra glyphs, woven Kente, Bogolan mudcloth, Ndebele borders, and the Kwacha brand mark — available three ways.
As SVG-string builders (any framework)
import { adinkra, mark, kenteStrip, bogolan, ndebeleBorder, ADINKRA_NAMES } from '@kwacha-kulture/savanna-css';
el.innerHTML = adinkra('sankofa', 48); // returns an <svg> string
el.innerHTML = mark(40); // the Kwacha roundel
// ADINKRA_NAMES -> ["sankofa","gyenyame","akoma","dwennimmen","nyamedua","eban"]As React components
import { Sankofa, GyeNyame, KenteStrip, BogolanStamp } from '@kwacha-kulture/savanna/motifs';
import { KwachaMark, KwachaWordmark, KwachaLogo } from '@kwacha-kulture/savanna/brand';
<Sankofa size={48} /> <KwachaMark size={40} />As Web Components
<sv-adinkra name="sankofa" size="48"></sv-adinkra>
<sv-mark size="40"></sv-mark>@kwacha-kulture/savanna-icons
Icons
318 Adinkra, Ndebele and UI glyphs — as tree-shakeable React components and raw SVG, with a typed registry.
import { Icon, IconSankofa, svgString } from '@kwacha-kulture/savanna-icons';
<Icon name="sankofa" size={24} /> // by name
<IconSankofa size={24} /> // named component
svgString('sankofa', 24); // raw SVG string — usable anywhere
// browse / filter:
import { iconRegistry, ICON_CATEGORIES, ICON_COUNT } from '@kwacha-kulture/savanna-icons/registry';@kwacha-kulture/react + react-pro
React components (91)
free 49 free (MIT, in @kwacha-kulture/react) · pro 30 pro · premium 12 premium (both in the commercial @kwacha-kulture/react-pro). Every component below is a complete reference; open the live gallery for interactive demos and copyable code.
Forms · 18
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Button | free | The primary action control — variants, sizes, loading state. | variant, size, loading, leftIcon, rightIcon | @kwacha-kulture/react |
| Input | free | A labelled text input with error + hint states. | label, error, hint, leftIcon | @kwacha-kulture/react |
| Textarea | free | A multi-line text field. | rows, error, hint | @kwacha-kulture/react |
| Select | free | A styled native select. | options, value, onChange | @kwacha-kulture/react |
| Checkbox | free | A checkbox with label. | checked, label, onChange | @kwacha-kulture/react |
| Radio | free | A radio option. | name, value, label | @kwacha-kulture/react |
| Toggle | free | An on/off switch. | checked, onChange | @kwacha-kulture/react |
| Slider | free | A range slider with a gold fill and label. | value, min, max, step, onChange | @kwacha-kulture/react |
| Combobox | pro | A searchable, filterable select. | options, value, onChange | @kwacha-kulture/react-pro |
| Autocomplete | pro | A multi-select autocomplete with chips and type-to-filter. | options, value, onChange, multiple | @kwacha-kulture/react-pro |
| Search Field | free | A search input with a leading icon and a clear/cancel button. | value, onValueChange, onClear | @kwacha-kulture/react |
| Transfer List | pro | A dual-list picker with checkboxes and move-all/selected. | left, right, onChange, titles | @kwacha-kulture/react-pro |
| Date Picker | pro | A date input with a popover month calendar. | value, onChange, label | @kwacha-kulture/react-pro |
| Time Picker | pro | A compact HH:MM time picker with hour + minute steppers. | value, onChange, minuteStep | @kwacha-kulture/react-pro |
| Rich Text Editor | premium | A formatting toolbar over a contentEditable surface — bold/italic, headings, lists, quote, links. | defaultValue, onChange, placeholder | @kwacha-kulture/react-pro |
| Dropzone | free | A drag-and-drop (or click) file upload area. | onFiles, accept, multiple, hint | @kwacha-kulture/react |
| Mentions | premium | A textarea with @-mention autocomplete — type @ to pick people. | value, onChange, options, rows | @kwacha-kulture/react-pro |
| Color Picker | premium | An HSV colour picker — saturation/value field, hue slider and hex input. | value, onChange | @kwacha-kulture/react-pro |
Layout · 5
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Box | free | A polymorphic styled div with shorthand padding/margin/bg/radius props. | as, p, px, py, m, bg, radius, border, shadow | @kwacha-kulture/react |
| Container | free | A centred, max-width content frame (sm/md/lg/xl). | maxWidth, gutter | @kwacha-kulture/react |
| Grid | free | A CSS-grid wrapper with column count and gap. | cols, gap, rows | @kwacha-kulture/react |
| Stack | free | A flex wrapper with direction, gap, align and justify. | direction, gap, align, justify, wrap | @kwacha-kulture/react |
| Image List | pro | A responsive image grid with hover captions. | images, cols, gap, radius, ratio | @kwacha-kulture/react-pro |
Data Display · 15
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Card | free | A surface for grouping content, with a kente accent option. | title, accentTop, padding | @kwacha-kulture/react |
| Badge | free | A small status/label pill. | variant, children | @kwacha-kulture/react |
| Tag | free | A removable tag/chip. | onRemove, children | @kwacha-kulture/react |
| Avatar | free | A user/brand avatar with fallback initials. | src, name, size | @kwacha-kulture/react |
| Counter | pro | An animated count-up for metrics — parses prefix/suffix. | value, duration | @kwacha-kulture/react-pro |
| Table | free | A sortable, striped data table with sticky headers and row hover. | columns, rows, sortable, striped | @kwacha-kulture/react |
| Stat | pro | A KPI tile with value and up/down delta — for dashboards. | label, value, delta, icon | @kwacha-kulture/react-pro |
| Timeline | pro | A vertical event timeline with motif nodes. | items | @kwacha-kulture/react-pro |
| Avatar Group | pro | Overlapping avatars with an overflow count. | people, max, size | @kwacha-kulture/react-pro |
| Chip | free | A compact chip with avatar/icon, delete and click — filled or outlined. | label, icon, avatar, color, variant, onDelete | @kwacha-kulture/react |
| Data Grid | premium | A full data grid — sortable, per-column filtering, column resize & pin, pagination, and row selection. | columns, rows, pageSize, selectable, getRowId | @kwacha-kulture/react-pro |
| Kanban | premium | A drag-and-drop board — move cards between columns. | columns, onChange | @kwacha-kulture/react-pro |
| Data Grid Pro | premium | A virtualized data grid — only visible rows mount (handles thousands of rows) — with inline editing, keyboard navigation (arrows/Enter/Esc) and per-column validation. | columns(+editable/validate), rows, rowHeight, height, onCellEdit | @kwacha-kulture/react-pro |
| Calendar | premium | A month calendar / scheduler with per-day events and selection. | events, value, onSelect | @kwacha-kulture/react-pro |
| Spreadsheet | premium | A lightweight spreadsheet — editable cells, formula bar and live column sums. | rows, cols, initial, onChange | @kwacha-kulture/react-pro |
Feedback · 11
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Spinner | free | A loading spinner. | size | @kwacha-kulture/react |
| Skeleton | free | A loading placeholder shimmer. | width, height | @kwacha-kulture/react |
| Empty State | free | A friendly empty/zero-data panel. | title, description, action | @kwacha-kulture/react |
| Tooltip | free | A hover/focus tooltip. | content, placement | @kwacha-kulture/react |
| Live Badge | pro | A pulsing "live/now" indicator. | label, color | @kwacha-kulture/react-pro |
| Alert | free | An inline message — info, success, warning, or danger. | tone, title, onDismiss | @kwacha-kulture/react |
| Banner | free | A full-width announcement bar with an action. | tone, action, onDismiss | @kwacha-kulture/react |
| Progress | free | A linear progress bar with optional label and value. | value, max, label, showValue | @kwacha-kulture/react |
| Progress Ring | pro | A circular progress indicator with a centred value. | value, size, stroke, tone | @kwacha-kulture/react-pro |
| Rating | pro | A fractional star rating, read-only or interactive. | value, max, onChange, readOnly | @kwacha-kulture/react-pro |
| Snackbar | free | A positioned, auto-hiding notification with an action. | open, message, action, tone, position, autoHideMs | @kwacha-kulture/react |
Overlay · 5
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Modal | free | A centred dialog. | open, onClose, title | @kwacha-kulture/react |
| Drawer | pro | A slide-in side panel. | open, side, onClose | @kwacha-kulture/react-pro |
| Popover Menu | pro | An anchored dropdown menu. | items, trigger | @kwacha-kulture/react-pro |
| Info Modal | pro | A richer informational dialog with motif framing. | open, title, motif | @kwacha-kulture/react-pro |
| Backdrop | free | A full-screen dimmer for loading or focus states. | open, blur, onClick | @kwacha-kulture/react |
Navigation · 11
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Tabs | free | Tabbed navigation with an animated indicator. | items, value, onChange | @kwacha-kulture/react |
| Accordion | free | Collapsible content sections. | items | @kwacha-kulture/react |
| Breadcrumbs | free | A path trail with a custom separator. | items, separator | @kwacha-kulture/react |
| Pagination | free | Page controls with ellipsis and prev/next. | page, pageCount, onChange | @kwacha-kulture/react |
| Segmented Control | free | A pill-style segmented toggle for switching views. | items, value, onChange, size | @kwacha-kulture/react |
| Stepper | pro | A horizontal multi-step progress indicator. | steps, current | @kwacha-kulture/react-pro |
| App Bar | free | A top application bar with leading, title and actions. | title, leading, actions, color, elevated | @kwacha-kulture/react |
| Bottom Navigation | pro | A mobile-style bottom tab bar with icons and labels. | items, value, onChange | @kwacha-kulture/react-pro |
| Menu | pro | A full dropdown menu — sections, icons, shortcuts, danger items. | trigger, sections, align | @kwacha-kulture/react-pro |
| Command Palette | premium | A ⌘K command palette — search, grouped results, full keyboard navigation. | open, onClose, commands | @kwacha-kulture/react-pro |
| Tree View | premium | An expandable hierarchical tree. | nodes, defaultExpanded | @kwacha-kulture/react-pro |
Media · 2
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Image Viewer | pro | A full-screen lightbox with zoom and keyboard navigation. | images, open, index, onClose, onIndexChange | @kwacha-kulture/react-pro |
| Audio Player | pro | A compact audio player with a deterministic waveform scrubber. | src, title, artist, seed | @kwacha-kulture/react-pro |
Charts · 7
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Sparkline | free | A tiny inline trend line for KPI tiles. | data, color, width, height, fill | @kwacha-kulture/react |
| Line Chart | pro | An area line chart with gridlines and points. | data, color, height | @kwacha-kulture/react-pro |
| Bar Chart | free | A labelled vertical bar chart. | data, color, height | @kwacha-kulture/react |
| Donut Chart | pro | A ring chart with a legend and percentages. | segments, size, thickness | @kwacha-kulture/react-pro |
| Area Chart | free | A responsive gradient area chart with a hover readout — the premium time-series. | data, color, height, unit | @kwacha-kulture/react |
| World Map | premium | A choropleth world map — colour countries by a value and hover for a per-country readout. Compact public-domain geometry baked in (178 countries), joined by ISO alpha-2; every colour is a prop. | data[{code,value}], colorLow, colorHigh, valueSuffix, renderTooltip | @kwacha-kulture/react-pro |
| Gantt | premium | A Gantt chart — tasks as bars over a unit timeline, with progress fill. | tasks, units, unitLabel, labelWidth | @kwacha-kulture/react-pro |
Motion · 3
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Reveal | free | Scroll-reveal wrapper (fade/slide in on view). | delay, children | @kwacha-kulture/react |
| Tilt Card | pro | A 3D pointer-tilt card. | max, children | @kwacha-kulture/react-pro |
| Magnetic | pro | A pointer-magnetic element. | strength, children | @kwacha-kulture/react-pro |
Typography · 5
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Eyebrow | free | A small uppercase kicker label. | color, children | @kwacha-kulture/react |
| Kente Rule | free | A woven kente divider, optionally labelled. | label | @kwacha-kulture/react |
| King Name | pro | A carved-gold name display in the regal lockup. | name | @kwacha-kulture/react-pro |
| Kbd | free | A keyboard-key glyph for shortcuts. | children | @kwacha-kulture/react |
| Divider | free | A functional separator, optionally labelled or vertical. | orientation, label | @kwacha-kulture/react |
Motifs · 6
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Kente Strip | free | A woven colour-band strip that fills its width. | height | @kwacha-kulture/react |
| Ndebele Border | free | A geometric Ndebele border band. | height, color | @kwacha-kulture/react |
| Sankofa | free | The Sankofa Adinkra symbol. | size, color | @kwacha-kulture/react |
| Gye Nyame | free | The Gye Nyame Adinkra symbol. | size, color | @kwacha-kulture/react |
| Dwennimmen | pro | The Dwennimmen (ram’s horns) Adinkra symbol. | size, color | @kwacha-kulture/react-pro |
| Bogolan Stamp | pro | A mudcloth (bogolan) stamp motif. | size, color | @kwacha-kulture/react-pro |
Brand · 3
| Component | Tier | Description | Key props | Import from |
|---|---|---|---|---|
| Kwacha Mark | free | The Sankofa-in-roundel brand mark. | size | @kwacha-kulture/react |
| Kwacha Wordmark | pro | A two-tone wordmark lockup. | lead, accent, fontSize | @kwacha-kulture/react-pro |
| Kwacha Logo | pro | The full mark + wordmark lockup. | lead, accent, markSize | @kwacha-kulture/react-pro |
Example usage
import { Button } from '@kwacha-kulture/react';
<Button variant="primary">Continue</Button>@kwacha-kulture/savanna-elements
Web Components
Native custom elements built on the CSS layer, encapsulated with a shadow root and the Savanna stylesheet adopted — drop-in for Vue, Svelte, Angular, Solid or plain HTML.
| Element | Attributes |
|---|---|
<sv-button> | variant, size |
<sv-card> | plain |
<sv-badge> | tone (gold · emerald · terracotta) |
<sv-input> | placeholder, type, value |
<sv-mark> | size |
<sv-adinkra> | name, size |
import { defineSavannaElements } from '@kwacha-kulture/savanna-elements';
defineSavannaElements(); // registers every element (idempotent, SSR-safe)For everyone
Accessibility
- Keyboard-navigable components with visible focus rings (
--sv-line-strong/--tz-focus-ring). - Motion respects
prefers-reduced-motion— reveals, count-ups, ken-burns and the generative canvas fall back to static. - Dark-mode contrast is tuned to meet WCAG AA on text; the accent is intentional against the deep ground.
- Semantic landmarks (
<header>,<nav>,<main>,<footer>) and ARIA labels on icon-only controls. - Audio never autoplays and always exposes a mute; images are lazy-loaded.
Patterns
Recipes
A hero with the generative canvas (CSS layer)
<section class="sv-hero sv-kente-bg">
<canvas class="sv-hero-canvas" data-kente-canvas></canvas>
<div class="sv-container">
<p class="sv-eyebrow">Proudly African</p>
<h1>Build the continent's <span class="sv-gold">next great thing.</span></h1>
<a class="sv-btn sv-btn-lg sv-btn-primary" href="#">Get started</a>
</div>
</section>A form (React)
import { Field, Input, Textarea, Button } from '@kwacha-kulture/react';
<form>
<Field label="Name"><Input placeholder="Amara Okafor" /></Field>
<Field label="Message" hint="We reply within a day"><Textarea rows={4} /></Field>
<Button variant="primary">Send</Button>
</form>The fine print
Versioning, licence & support
Packages follow semver and version independently. The core (tokens, css, elements, icons, savanna, react) is MIT. @kwacha-kulture/react-pro is commercial — it installs from public npm but requires a paid licence (call setLicenseKey()); see its LICENSE.
Questions, licences and the Savanna Display font: savanna.venanciogomani.net. Source: GitHub.