# 78 UI Kit > A theme-forward, no-build UI kit — plain HTML / CSS / JS you drop in. One stylesheet, one script, and > every color resolves at runtime from CSS custom properties, so re-theming is an override block rather > than a rebuild. Built for dashboards: KPI cards, tables, no-library data-viz primitives, and theme > adapters that recolor Tabulator, FullCalendar and Chart.js along with everything else — including on a > live theme switch. MIT. Two things shape every decision in the kit: - **Theme-forward.** A theme is *only* color. Color tokens live in `:root[data-theme="dark"]` / `["light"]`; non-color tokens (radius, type, spacing, layout sizes) live in a plain `:root`. Nothing hardcodes a color, so overriding the tokens re-themes the whole kit. - **Built for dashboards.** Stat/KPI cards, a table style, sparkline / progress / bar / donut / gauge / trend primitives, an app shell, and adapters so third-party tables and charts follow the theme too. No framework. No build step. No dependencies. Version 0.x — the public API may still change before 1.0. ## Install ```html ``` `@v0.4.0` pins a release; `@main` tracks the latest. `kit.min.css` / `_78.min.js` are also served. Or download the repo and serve the files yourself. Library adapters are opt-in and live under `/css/adapters/` and `/js/adapters/` — they are never part of `kit.css`. ## Naming - **CSS classes** are prefixed `_78-` — `._78-btn`, `._78-stat-card`, `._78-nav-item`. - **Modifiers** are suffixes on the block class — `._78-btn-primary`, `._78-table-compact` — and shared state classes are their own prefixed class alongside it: `._78-active`, `._78-selected`, `._78-up`, `._78-down`, `._78-invert`, `._78-high` / `._78-mid` / `._78-low`, `._78-danger`. - **JS** is one global, `_78`, with no dependencies: `_78.theme`, `_78.tone`, `_78.util`, `_78.shell`, `_78.modal`, `_78.notify`, `_78.tabs`, `_78.seg`, `_78.viz`, `_78.toc`, `_78.adapters.chartjs`, `_78.gen`. - **Events** are namespaced `_78:` and fire on `document` (except `_78:tabchange`, which bubbles from the tab list, `_78:segchange`, which bubbles from the segmented control, and `_78:tocchange`, from the contents rail): `_78:themechange`, `_78:railchange`, `_78:drawerchange`, `_78:tabchange`, `_78:segchange`, `_78:tocchange`, `_78:genchange`. ## Tokens Token *names* are the contract; the shipped values are a neutral default palette meant to be overridden. - **Core color:** `--bg` `--bg2` `--bg3` `--border` `--text` `--dim` `--accent` - **Accent derived:** `--accent-hover` `--accent-text` `--accent-lo` `--accent-lo-hover` `--accent-glow` - **Semantic:** `--success` `--warn` `--danger` `--info`, each with a `-lo` tint - **Categories:** `--cat-1` … `--cat-6`, each with a `-lo` tint — label colors that are not states - **Charts:** `--series-2` — the second chart series, a gray that recedes behind `--accent` - **Surface details:** `--row-hover` `--row-border` `--row-selected` `--ring` `--overlay` `--shadow` `--shadow-lg` - **Non-color (shared `:root`):** `--radius-sm` `--radius` `--radius-lg` `--radius-pill` · `--font` `--font-mono` · `--fs-xs`…`--fs-xl` `--lh` · `--space-1`…`--space-6` · `--control-h` `--control-h-sm` `--icon-btn-size` `--ring-w` · `--sidebar-w` `--rail-w` `--header-h` `--content-max` · `--transition` `--transition-slow` The `--space-*` tokens are also the escape hatch above the utility scale: there is no margin utility over 16px and there are no padding utilities, by design — use the token directly instead. ## Tones One vocabulary for every component that takes a tone, and it is the token names: `accent` `success` `warn` `danger` `info` `dim`. Aliases, permanent and identical: `green` = success, `red` = danger, `amber` / `yellow` = warn. Write the canonical name. - Badge `._78-badge-{tone}` · tag `._78-tag-{tone}` · text `._78-text-{tone}` or `._78-{tone}` (also on `._78-stat-card__num`) · alert `._78-alert-{tone}` · data-viz `._78-tone-{tone}` / `data-tone` · modal `tone:` · toast `type:` (`danger` = the toast's `error`). All six on each. - `_78.tone('red')` → `'danger'`; `_78.tone.names` lists the six. - **Categories** (labels that are not states, like a source or a team): `cat-1` … `cat-6` on badge, tag, text and viz tone — `._78-badge-cat-3`. Hues sit clear of success / warn / danger; use them in order. ## Components - **App shell** — `._78-topbar` + `._78-sidebar` that collapses to an icon rail and becomes a mobile drawer, plus `._78-app-main`; driven by `_78.shell`. Flat nav items only, no nested menus. The open drawer keeps keyboard focus inside it and makes the page behind `inert`. - **Buttons** — `._78-btn` (`-primary` / `-ghost` / `-danger` / `-sm` / `-full`), `._78-icon-btn`, `._78-btn-group`. - **Cards** — `._78-card` and the KPI `._78-stat-card` (label / number / delta / sparkline), laid out by `._78-kpi-grid` / `._78-card-grid`. Stacked cards space themselves; in a grid they line up at the top. `._78-card-stack` spaces a list of cards with a gap, even when each one is wrapped. `._78-stat-card__actions` puts a small control (a seg, an icon button) beside the label; `._78-stat-card__lines` / `__line` / `__key` / `__value` are key : value lines with the keys in one column. - **Contents rail** — `._78-toc` in a `._78-toc-layout`, filled by `_78.toc`: an "On this page" rail built from the page's headings, sticky, with an IntersectionObserver scroll-spy (`aria-current`), a `._78-details` dropdown under 880px, nothing under two headings. `data-toc-*` options, `_78:tocchange`. - **Grid** — `._78-grid` (auto-fit, `--min` track size, no breakpoints) with `._78-col-span-2` (two columns when there's room, else the whole row) and `._78-col-full`. `._78-kpi-grid` / `._78-card-grid` are presets of it. - **Meta line** — `._78-meta`, the small dim line of facts under a headline; the kit draws the `·` between child elements. `._78-meta-label` lead-in, `-xs`, and a line clamp (`-clamp` / `-clamp-2`). - **Forms** — `._78-field` with `._78-input` / `._78-select` / `._78-textarea` / `._78-check` and one consistent focus ring. `._78-form-inline` puts a field and its button on one row (a form in a card foot). - **Toggle switch** — `._78-switch` (+ `-sm`, `._78-switch-row`), a real `` restyled as a track and thumb, so keyboard, `:disabled` and label association are native. Stacked `._78-switch-row`s space themselves. - **Segmented control** — `._78-seg` / `._78-seg-btn` (+ `-sm` / `-full` / `-accent`), one-of-N, mounted by `_78.seg` as a `radiogroup` with roving tabindex; fires `_78:segchange`. - **Disclosure** — `._78-details` (+ `-plain` / `-fill` / `-sm`), a styled `
` / `` with a rotating chevron marker. CSS only. - **Figure row** — `._78-figure-row` / `._78-figure`, N related numbers side by side, each with a label and a `._78-tone-*` colored left edge; `._78-figure-neutral` / `._78-figure-row-neutral` for figures that carry no meaning. - **Badges & pills** — `._78-badge` and `._78-tag` (any tone), `._78-score-pill`, `._78-eyebrow`. - **Status line** — `._78-status`: a badge + `._78-status-text` caption that wraps cleanly on a phone (`._78-status-inline` inside a sentence). - **Table** — `._78-table` (`-compact` / `-striped` / `-sticky`) in a `._78-table-wrap` scroller. For a real data grid, use Tabulator plus its adapter. `._78-table-card` + `._78-table-filters` (`._78-table-dates`, `._78-table-count`) frame either with a filter-and-date toolbar. - **Tabs** — `._78-tabs` (+ `._78-tabs-segmented`), auto-mounted by `_78.tabs` with full ARIA and arrow keys. - **Dropdown menu** — `._78-menu-wrap` > trigger + `._78-menu`, auto-wired with roving focus and the `menu` / `menuitem` roles. - **Modal** — native `` via `_78.modal.open()` / `.confirm()` / `.alert()`, promise-returning. `confirm()` and `alert()` resolve `true` for their button and `false` for any dismissal. - **Toast** — `_78.notify()` and `.success` / `.error` (or `.danger`) / `.warn` / `.info`; auto-dismissing, never blocking. - **Inline alert** — `._78-alert` (+ any tone) and `._78-empty`, for anything tied to a region of the page. - **Quiet notes** — `._78-notes`: dim notes inside a card, quieter than an alert — an optional `._78-notes-title`, list lines with a dot marker that takes any `._78-tone-*`, and `-box` (a compact alert) / `-ruled` (a footnote) / `-xs`. - **Aurora text** — `._78-text-aurora`, an animated gradient for a hero word or a wordmark; recolor with `--_78-aurora`, retime with `--_78-aurora-speed`; static under reduced motion. - **Data-viz, no library** — `._78-sparkline`, `._78-progress`, `._78-bar-row`, `._78-split-bar` (a whole divided into named, proportional segments), `._78-donut` / `._78-gauge`, `._78-trend`, driven by `_78.viz` and toned with one class. - **Theme toggle** — any `._78-theme-toggle` element is wired on load and cycles Dark → Light → System. - **Utilities** — a small set: `._78-text-dim`, `._78-flex`, `._78-gap-12`, `._78-mt-16`, `._78-truncate`, `._78-nums`, `._78-sr-only`, `._78-list-plain`, and friends. Plain lists inside an alert body, card, modal body or disclosure keep their indent and markers, and consecutive paragraphs there space themselves. Text color takes any tone, in two interchangeable spellings: `._78-text-danger` / `._78-danger`. ## Helpers — `_78.util` - `_78.util.sortAlpha(a, b)` — a human-sort comparator (case-insensitive, locale-aware, "Item 2" before "Item 10", blanks last) for `array.sort()` or a Tabulator `sorter`. - `_78.util.duration(ms, { long, parts })` — "45 sec", "2 hrs 5 min", "3 days"; `long: true` spells units out, `parts` caps how many (default 2). Never "0 min". ## Library adapters (opt-in) - **Tabulator 6.x** (verified against 6.5.2) — `css/adapters/tabulator.css`, loaded after Tabulator's own stylesheet. Covers responsive-collapse mode too (the fold-out panel wraps long values; the +/- toggle is accent-colored). - **FullCalendar 7.x core** (verified against 7.0.2) — `css/adapters/fullcalendar.css`, loaded after FullCalendar's CSS. - **Chart.js 4.x** (verified against 4.5.1) — `js/adapters/chartjs.js`, loaded after Chart.js and after `_78.js`. Canvas cannot read CSS variables, so it pushes tokens into `Chart.defaults` and calls `update()` on every live chart when the theme switches (datasets, legend, tooltip and scales). Don't set those colors per chart. Series colors: `--accent`, `--series-2`, then `--cat-1` … `--cat-6`. The version beside each library is the one its adapter was checked against; re-check after a major upgrade. Nothing is vendored — bring your own copy of each library at your own version. ## Theme generator `js/generator.js` (`_78.gen`) derives the whole token set for light *and* dark from one accent color, in OKLCH, with live WCAG AA checks, an apply-to-the-page mode and a shareable `?theme=` URL. Opt-in, not part of `_78.js`. ## More - [llms-full.txt](https://cdn.jsdelivr.net/gh/1978io/78UIKit@main/llms-full.txt): the complete reference — every component's classes, variants, `data-*` attributes and JS options, each with a copy-paste example. - [78uikit.com](https://78uikit.com): docs, the live component gallery, and the theme generator. - [github.com/1978io/78UIKit](https://github.com/1978io/78UIKit): source, releases, MIT license.