Adapters
Tables and charts are most of a dashboard, and almost no component library themes them. These adapters map three widely-used libraries onto the kit's tokens, so they follow your theme — including when it switches.
The idea
Your data grid ships its own gray. Your calendar ships its own blue. Drop either into a themed app and it looks bolted on — and if your app has a dark mode, it looks broken. The usual fix is a pile of overrides you maintain forever.
Instead, three rules:
- Nothing is vendored. The kit ships no copy of any library. You bring your own, at your own version, from wherever you already get it.
-
Adapters are opt-in. They are deliberately not part of
kit.css. Include only the ones you actually use. - Always after the library. An adapter's job is to win, so it has to come second.
Two of the three are pure CSS and cost you nothing at runtime — the library's own colors
resolve to kit tokens, so a theme switch is free. Chart.js is the exception, and the reason is
worth knowing: a chart is a <canvas>, and canvas pixels cannot resolve
var(--accent). Its adapter is JavaScript, and it does the part everyone misses
— see below.
Including them
Library first, then its adapter. Include only the rows you need.
<!-- the kit -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/1978io/78UIKit@v0.4.0/css/kit.css">
<script defer src="https://cdn.jsdelivr.net/gh/1978io/78UIKit@v0.4.0/js/_78.js"></script>
<!-- Tabulator, then its adapter (verified against Tabulator 6.5.2) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tabulator-tables@6.5.2/dist/css/tabulator.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/1978io/78UIKit@v0.4.0/css/adapters/tabulator.css">
<script defer src="https://cdn.jsdelivr.net/npm/tabulator-tables@6.5.2/dist/js/tabulator.min.js"></script>
<!-- FullCalendar v7 (core + classic theme), then its adapter (verified against 7.0.2) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@7.0.2/skeleton.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@7.0.2/themes/classic/theme.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@7.0.2/themes/classic/palette.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/1978io/78UIKit@v0.4.0/css/adapters/fullcalendar.css">
<script defer src="https://cdn.jsdelivr.net/npm/fullcalendar@7.0.2/all/global.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/fullcalendar@7.0.2/themes/classic/global.js"></script>
<!-- Chart.js, then its adapter (verified against 4.5.1); the adapter goes after _78.js too -->
<script defer src="https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/1978io/78UIKit@v0.4.0/js/adapters/chartjs.js"></script>
That is exactly what this page loads — view source and compare. The CSS adapters are version-sensitive, because class names move between majors; the version each was built against is noted at the top of the adapter file and repeated below.
Tabulator CSS adapter
Headers, rows, hover, selection, sort arrows, header filters, pagination and popups all map to kit tokens. Click a row to select it, sort a column, page through, or filter by region — every one of those states is themed.
Built against 6.x, verified on 6.5.2. Header background maps to
--bg2, row hover to --row-hover, selection to --row-selected
and pagination to --accent. Re-themes for free.
A date range and filters above the grid? Wrap it in the kit's table card and toolbar.
Responsive collapse
With responsiveLayout: "collapse", columns that no longer fit fold into a panel under
each row, opened with the + toggle. The adapter themes that panel too. Narrow the
window, or open this page on a phone, and open a row.
-
Long values wrap inside the table. Tabulator's cells are
nowrap, and the panel would otherwise inherit that and run off the edge. The adapter setswhite-space: normalon the panel's cells. -
The toggle follows the accent. Tabulator draws the +/− with a hardcoded white
stroke, which disappears on a pale tint in light theme. The adapter strokes it with
currentColor, so it is--accenton--accent-loin both themes. -
The panel's type follows the tokens. Tabulator sets the panel to a fixed 14px; the
adapter sets
--fs-base, the same size as the cells, so a changed type scale reaches it.
new Tabulator('#accounts', {
layout: 'fitColumns',
responsiveLayout: 'collapse',
responsiveLayoutCollapseStartOpen: false,
columns: [
// The toggle column first, so it never collapses itself.
{ formatter: 'responsiveCollapse', width: 40, minWidth: 40, hozAlign: 'center',
resizable: false, headerSort: false },
{ title: 'Customer', field: 'customer', minWidth: 120, responsive: 0 }, // never collapses
{ title: 'Plan', field: 'plan', minWidth: 100, responsive: 1 },
{ title: 'MRR', field: 'mrr', minWidth: 90, responsive: 2 },
{ title: 'Notes', field: 'notes', minWidth: 260, responsive: 3 } // collapses first
]
});
FullCalendar CSS adapter
Toolbar, grid lines, today's cell, events, the list view and the focus ring all follow. Switch views to see it hold up.
Primary target v7 core, verified on 7.0.2; a legacy block in
the same file keeps v6.x working. Core only — no paid plugins. v7 ships
hashed class names, so its palette custom properties are the entire theming surface, and remapping
--fc-classic-* onto kit tokens is all the adapter does.
Chart.js JS adapter
Canvas cannot read CSS variables, so this adapter reads the tokens into Chart.defaults
instead. And because those defaults are only read when a chart is constructed, it also calls
update() on every live chart on _78:themechange — which is the part
that makes a theme switch actually work rather than only the first paint.
Everything the theme colors follows a live switch: the datasets the adapter owns, the legend, the tooltip, and the grid, axis line and tick labels. (Chart.js binds each scale's colors once, so the adapter rewrites them on the live scales — but only colors that still match the previous theme's tokens, never ones a chart set for itself.) Scales fixed in 0.3.0
plugins.legend.labels.color,
scales.x.grid.color — out-ranks the adapter and is read once, so it stays on the
theme the page loaded in. Leave those unset and the adapter keeps them in step. If you need a dimmer
legend on one chart, that is the case for setting it yourself, and for re-setting it on
_78:themechange.
The series colors
An adopted dataset takes its color from its position. The first is --accent; the
second is --series-2, a true gray that recedes behind the accent and, having no hue, can
never sit near it whatever your accent is — that is the Target line above. From the
third on, the six categorical tones
take over. The semantic colors are deliberately not in the ramp: a series is a kind of thing, not a
state, and a green line reads as “good”.
borderColor / backgroundColor yourself — the adapter
never touches a dataset that declares its own.
Who owns a dataset's color
There is one rule, and it is deliberately simple. A dataset that declares no colors
is adopted by the adapter and recolored on every theme switch. A dataset that declares its own
backgroundColor or borderColor is never touched —
your color wins, permanently. The chart below sets its own purple, so it stays purple in both themes
while the axes, grid, legend and tooltip still follow along.
// No colors given → adopted by the adapter, recolored on every theme switch.
new Chart(el, {
type: 'line',
data: { labels: months, datasets: [{ label: 'Revenue', data: revenue, fill: true }] }
});
// Explicit color → the adapter never touches this dataset.
new Chart(el, {
type: 'bar',
data: { labels: months, datasets: [{ data: net, backgroundColor: '#8b5cf6' }] }
});
// Series colors from the current theme: --accent, --series-2, then --cat-1 … --cat-6.
_78.adapters.chartjs.palette(4);
There is an explicit opt-out for the awkward middle case: a dataset that declares no colors, but that
you still want left alone — because a plugin colors it, or you set the colors later. Set
$_78 on the dataset and the adapter honors it either way.
// Hands off, even though this dataset declares no colors of its own.
{ label: 'Forecast', data: forecast, $_78: false }
// Adopt this one, even though it does declare colors.
{ label: 'Actual', data: actual, backgroundColor: '#888', $_78: true }
Left unset, the adapter decides once on first pass — adopted when both
backgroundColor and borderColor are undefined — and
records its decision on the dataset as $_78.
Built against 4.x, verified on 4.5.1. The adapter also exposes
.tokens(), .alpha(color, a) and .apply() for anything it does
not cover.