Keystone gives you styles, icons, and behavior as three independent layers — each a single include that works from a CDN or a local file, on its own or together. Charting comes as an optional fourth include, so pages that don't chart pay nothing. The behavior layer carries what data apps keep rewriting: paged and selectable tables, async region states, a command palette, and a print sheet — and the charts module now draws gauges, heatmaps, status boards and more, moving with your data.
Small, honest, and dependency-free. Use exactly the parts you want.
Want our icons but not our styles? Our JS but your own CSS? Every pillar stands alone and degrades gracefully when the others are absent.
No frameworks, no runtime, no bundler. The behavior layer is vanilla JavaScript written from scratch. Drop the files in and go.
The same single tag works whether you link to a CDN or download the files. Icons embed their font, so there's no assets folder to chase.
Everything is built on --ks-* CSS custom properties. Retheme by overriding variables; dark mode ships in the box.
Focus management, ARIA wiring, keyboard support, and reduced-motion respect are built into the components — not bolted on.
Add your own components with a buildless plugin system: drop a module, list it in config.json, and it loads at runtime.
Each is one include. Use one, two, or all three.
Design tokens and component classes — buttons, forms, cards, modals, tabs, tables and more.
<link rel="stylesheet" href="keystone.css">
601 geometric icons in one self-contained file — the webfont is base64-embedded, so there's nothing else to download.
<link rel="stylesheet" href="keystone-icons.css">
From-scratch components: modals, drawers, dropdowns, tabs, accordions, tooltips, toasts, theme & form helpers.
<script type="module" src="keystone.js"></script>
Bar, line, area, donut and sparklines as inline SVG — no charting library.
Charts are built from a real <table>, so
without JS the data still renders, and with it the table stays reachable
behind “View data” as the chart's screen-reader representation. Series
colors come from the colorblind-safe --ks-chart-1…8
slots and follow the theme automatically.
<link rel="stylesheet" href="keystone-charts.css"> <script type="module" src="keystone-charts.js"></script>
| Quarter | Managed IT | Cloud | Security |
|---|---|---|---|
| Q1 | 48,290 | 21,400 | 9,800 |
| Q2 | 51,120 | 24,900 | 12,300 |
| Q3 | 46,700 | 28,150 | 14,050 |
| Q4 | 54,380 | 30,600 | 15,900 |
Dashboards spend most of their code on the same few problems. These are in the pillars you already loaded — no extra include, no extra file.
Sorting, client-side paging, row selection, column show/hide and CSV / clipboard export — four behaviors over one controller, so they compose instead of fighting. Rows arriving later from an API are picked up automatically. Without JS it's one plain, complete table.
<table class="ks-table" id="tickets"
data-ks-table-sortable
data-ks-table-select
data-ks-table-page="5">
| Host | Team | Open |
|---|---|---|
| fw-edge-01 | Service desk | 62 |
| web-prod-02 | NOC | 45 |
| sql-03 | Projects | 44 |
| app-04 | Field | 34 |
| backup-05 | Service desk | 28 |
| ws-0142 | NOC | 21 |
| ops-07 | Projects | 17 |
The four-state dance every data screen does — loading, error + retry,
empty, data — as markup instead of app code, with the poll timer and the
“updated 2m ago” stamp handled. Keystone owns the states;
you own the fetch. A failed refresh
keeps your last good numbers on screen instead of blanking the board.
Keystone.resource('#problems', {
load: () => myApi.activeProblems(),
poll: 60000,
});
| Host | Problem | Since |
|---|
Register commands, get ⌘K and real keyboard shortcuts. It only binds once your page registers something, so pages that don't use it keep the shortcut.
<time data-ks-relative> reads
“” and keeps itself
current on one shared tick that stops when the tab is hidden. The date you
author stays the no-JS fallback.
A dashboard prints as a report: light palette, hairlines, no chrome, charts
with their data tables. And data-ks-copy on any
button copies a snippet — every Copy button on this page is one.
Gauges, heatmaps, status boards, waffles, bullets, funnels and scatter plots —
built from the <meter> and
<table> markup you'd write anyway. When the data
changes, the marks move to it instead of redrawing. Press the button.
| Status | Devices |
|---|---|
| Fully patched | 431 |
| Queue | Open |
|---|---|
| Service desk | 17 |
| Network | 9 |
| Projects | 7 |
| Security | 5 |
| Day | Microsoft 365 | Site-to-site VPN | Cloud backup |
|---|
Marks are keyed by meaning, so an update slides a bar, re-points a line and re-sweeps a dial. Table-fed charts watch their table — re-render the rows and the chart follows.
gauge · heatmap ·
calendar · waffle ·
status · bullet ·
funnel · scatter — plus
annotations and event bands on every line, area and bar chart.
Categorical slots for identity, a validated steel ramp for magnitude, an ordinal ramp for stages, and state tones only for state — always with a word and a glyph.
data-ks-stagger, Keystone.flip()
(sorted rows glide), Keystone.countTo() and
.ks-draw — each one shows a change, none of them loops.
Keystone.motion.set('reduce') silences every
animation in-app, persisted. Reduced motion always renders the final state —
never a frozen middle.
.ks-meter, .ks-ring,
.ks-steps and .ks-proportion
need no script at all — just the stylesheet you already load.
Link to it, or download it. Then write plain HTML.
Drop these in your <head>. Keep only the pillars you need.
<link rel="stylesheet" href="https://ui.aheliotech.app/keystone.css"> <link rel="stylesheet" href="https://ui.aheliotech.app/keystone-icons.css"> <script type="module" src="https://ui.aheliotech.app/keystone.js"></script> <!-- Optional: only on pages that chart --> <link rel="stylesheet" href="https://ui.aheliotech.app/keystone-charts.css"> <script type="module" src="https://ui.aheliotech.app/keystone-charts.js"></script>
Served from ui.aheliotech.app. Pin a version when releases are tagged.
<link rel="stylesheet" href="keystone.css"> <link rel="stylesheet" href="keystone-icons.css"> <script type="module" src="keystone.js"></script> <!-- Optional: only on pages that chart --> <link rel="stylesheet" href="keystone-charts.css"> <script type="module" src="keystone-charts.js"></script>
Download the files below and reference them from your project.
Components are plain classes and data-ks-* hooks. The behavior layer wires them up automatically.
<body class="ks-body ks-scope">
<button class="ks-btn ks-btn--primary" data-ks-modal-open="hello">
<span class="ks-i ks-i-controls-search"></span> Open
</button>
<div class="ks-modal" data-ks-modal id="hello">
<div class="ks-modal-dialog">
<div class="ks-modal-header">
<h3 class="ks-modal-title">Hello</h3>
<button class="ks-modal-close" data-ks-modal-close></button>
</div>
<div class="ks-modal-body">It works.</div>
</div>
</div>
</body>
These controls are live — rendered by the same Keystone files this page loads.
This dialog is rendered by keystone.js — focus is trapped, Esc closes it, and the backdrop dismisses on click.
It works.
Grab individual files, or take the whole keystone/ folder.
Optional charts module — add both files only on pages that chart.
No build, no bundler. Drop a module, list it, and it loads at runtime.
plugins/config.json
[
{ "path": "sample/main.js", "enabled": true },
{ "path": "my-plugin/main.js" }
]
plugins/my-plugin/main.js
export default function register(ks) {
ks.register('greeter', {
selector: '[data-greet]',
mount(el) {
ks.utils.on(el, 'click', () =>
ks.toast({ message: 'Hi!' }));
},
});
}
The core fetches config.json relative to keystone.js and imports each module in the order you list. A missing config is harmless.
No pillar requires another. Load whatever fits.
| Loaded | Result |
|---|---|
| Icons only | .ks-i-* glyphs render. |
| Style only | Components fully styled; interactive ones sit in their default state. |
| Behavior only | Behaviors work; injected marks are inline SVG; appearance is unstyled. |
| Any combination | Progressive enhancement, with no errors when a pillar is absent. |
A two-tone bracketed [KS] — technical and corporate. Download the assets below.