Keystone demo

Style + icons + behavior, each loaded from a single tag. This page uses all three; comment any one out in the source to see the others still work.

Accent theme

Swap the accent color with data-ks-accent on <html> (or any container). The primary steel-blue is unchanged; -subtle / -muted tints derive via color-mix so every preset works in light and dark.

-subtle -muted

Buttons

Form (with validation)

View
Plan

Form niceties

https://
/ mo

Rich-text editor

Combobox (searchable & multi-select)

Badges, chips & alerts

Neutral Primary Success Warning Error
Chip Selected tag Active Removable Clickable
Heads up
This is an informational alert.
Saved successfully.

Overlays & feedback

Popover

Click outside or press Esc to close.

Right-click the Keystone site link (or any selected text) to open the custom context menu.

Promise-based dialogs

await Keystone.confirm() / .alert() / .prompt() — built on the modal, resolved on choice.

Result: (none yet)

Tabs & accordion

Overview content.
Details content.
Activity content.
A three-pillar front-end framework.
No — drop in the files and go.

Data & status

NameStatusUptime
Server AOnline99.98%
Server BDegraded97.40%
Server COffline0.00%
Fleet3 nodes65.79%
Progress
Loading…
KS

Table utilities

Sortable columns, a frozen first column, a toolbar, and a responsive card-stack — all composable on the same .ks-table. Click a header (or focus it and press Enter/Space) to cycle ascending → descending → original. Type is auto-detected per column. Narrow the window below 640px to see rows stack into labeled cards. Without JS it stays a plain static table.

Devices 6
Host Status Uptime Last seen Actions
web-01 Online 99.98% Jun 30, 2026
db-02 Degraded 97.40% Jun 28, 2026
cache-03 Offline 0.00% May 11, 2026
edge-04 Online 99.10% Jun 30, 2026
api-05 Online 98.72% Jun 29, 2026
queue-06 Degraded 95.30% Jun 27, 2026

Empty state (as a full-width <td> row):

HostStatusUptime
No devices found Try clearing the filter, or add your first device to start monitoring.

Table pack

Four behaviors over one shared controller, so they compose: the pipeline is always filter → sort → page. Type in the filter and the pager recounts; sort a column and you land back on page 1; select-all ticks the whole filtered set, not just this page; export writes what you are looking at, in the order you are looking at it, minus any column you hid. Rows added later by an API call are picked up automatically. Without JS it is one plain, complete table.

Tickets
Host Tech Priority Open
web-01Adam WilsonP162
db-02Chase MerwinP245
cache-03Taner RussoP344
edge-04Andrew ParisP234
api-05Adam WilsonP328
queue-06Chase MerwinP121
mail-07Taner RussoP319
vpn-08Andrew ParisP217
file-09Adam WilsonP314
print-10Chase MerwinP312
backup-11Taner RussoP19
dns-12Andrew ParisP36
nas-13Adam WilsonP24
wifi-14Chase MerwinP32

Commands, time & clipboard

Press ⌘K (or Ctrl+K) for the palette — it only binds once a page registers a command, so pages that don't use it keep their shortcut. Timestamps rewrite themselves and keep current on one shared tick. Copy buttons read a literal, a selector, or the nearest <pre>. And this whole page has a print stylesheet — try ⌘P.

Command palette

These two register declaratively — the attribute makes the control a command, and the palette runs it by clicking it.

Relative time
EventWhen
Backup finished
Alert raised
Ticket opened
Maintenance window

Styles: default, compact, short. Hover for the absolute timestamp.

Copy to clipboard
Keystone.commands.open()

This line is .ks-no-print — it won't appear on paper.

Printed copy — hidden on screen (.ks-print-only).

Charts — real axes

data-ks-x="time" reads each row label as a date and plots it at its true position. The two charts below hold the same readings: on the left a category axis spaces them evenly, on the right a time axis shows what actually happened — three samples a minute apart, then a six-hour hole, then two more. Also here: data-ks-ref threshold lines, click-a-key-to-isolate, and PNG / CSV export.

Category axis — evenly spaced (the lie)
Timeweb-01db-02
08:004155
08:014457
08:024356
14:029261
14:038860
Time axis — the six-hour gap is visible
Timeweb-01db-02
08:004155
08:014457
08:024356
14:029261
14:038860

Click a legend key to isolate that series — the value axis rescales to what's left, and clicking it again brings everyone back. CSV always exports the full dataset, including a series you've isolated out of view.

Async regions

The four-state dance every data screen does — loading → error+retry → empty → data — as markup instead of app code. Presentation only: Keystone owns which slot shows, the retry button, the poll timer and the stale clock; you own the fetch. Force each state below. Note that a failed reload keeps the last good numbers on screen and just marks the region stale — blanking a working board because one refresh timed out is worse than slightly old data.

Active problems
Couldn't reach the monitoring API
All clear No active problems on any monitored host.
HostProblemSeverity
Server-rendered rows — what a no-JS visitor sees.

App primitives

Building blocks for data-heavy app screens — all style-only, working in light and dark.

Monthly revenue $48,290 Up: 12.4%
Active sessions 1,204 Down: 3.1%
Avg. response 184 ms No change
Skeleton
No messages yet

When teammates reach out, their conversations will show up right here.

Activity
  1. 09:42
    Deployment v1.4.0 succeeded on all nodes.
  2. 09:31
    Node B reported degraded latency during rollout.
  3. 09:20
    Release pipeline started by gkuhl.

Charts

Bar, line/area, donut and sparklines, rendered from real <table> markup (or inline JSON / the JS API). Without the module the tables below simply render; with it, each table stays reachable behind “View data” — it is the chart's screen-reader representation. Series colors are the validated --ks-chart-1…8 slots (colorblind-safe in light and dark, assigned in fixed order — never rank, never a state color). Hover or focus any mark for its values.

Revenue by quarter
QuarterManaged ITCloudSecurity
Q148,29021,4009,800
Q251,12024,90012,300
Q346,70028,15014,050
Q454,38030,60015,900
Tickets by site
SiteHardwareNetworkSoftware
Columbus342146
Dublin181225
Westerville111714
Remote62831
Response time, p50 vs p95
Weekp50p95
W162212
W258198
W366184
W454190
W551171
W649163
Backups completed
Fleet by OS
OSDevices
Windows412
macOS96
Linux44
Other17
Keystone.charts.render(el, config) — click a bar for ks:chart:select

Inline sparklines ride along in text or table cells: CPU , memory , deploys — colored via currentColor, defaulting to slot 1.

Motion — controls, progress & reveal

State changes animate: the check mark springs in, the radio core scales up, the switch knob slides while its track eases off→on. Progress eases width changes and offers a looping --indeterminate sweep. All of this respects prefers-reduced-motion.

Toggle animations
Progress
Indeterminate (looping sweep)
Determinate (eased width) — 20%
Skeleton → content
Scroll reveal

This card rises and fades in as it enters the viewport — scroll-timeline where supported, IntersectionObserver fallback otherwise.

Density modes

Scale control heights, padding and font for data-dense screens without re-theming. Add data-ks-density="compact", "default" or "comfortable" to <html> or any container. Compact clamps interactive controls to a 32px minimum tap target. The middle column below writes "default" explicitly — it is pixel-identical to omitting the attribute, and it is the only way to reset a subtree to 1x inside a comfortable ancestor (see below).

Compact 0.75x
HostStateLoad
node-01Up0.42
node-02Warn0.88
  • Item one A
  • Item two B
Default 1x
HostStateLoad
node-01Up0.42
node-02Warn0.88
  • Item one A
  • Item two B
Comfortable 1.25x
HostStateLoad
node-01Up0.42
node-02Warn0.88
  • Item one A
  • Item two B

Resetting inside a dense page. Density nests, so a container inside a comfortable ancestor inherits 1.25x — and until now there was no way back. Writing data-ks-density="default" returns a subtree to 1x, which is what a data table on an otherwise roomy page wants.

Comfortable page 1.25x

This table is "default" inside the comfortable card:

HostStateLoad
node-01Up0.42
node-02Warn0.88

Sound & haptics

Optional official plugins, shipped off in plugins/config.json. Enable them below to feel/hear them — then click any button on the page. Haptics need a device with a vibration motor.

Stateful fields

Add the stateful attribute and a field remembers its value across reloads — persisted to the browser's private file system (a port of pio). Type below, then refresh.

Theme preference — stateful radio group

Requires the File System Access API (Chromium/WebKit). Other browsers no-op silently. Radios persist by group name.

Icons

Rendered from the embedded webfont via .ks-i-<category>-<name>.

Modal title

This modal is opened by the behavior layer. Press Escape, click the backdrop, or use the close button.

Drawer

Off-canvas panel. Same dismiss model as the modal.