import "overscan/radar";
<ov-radar source="contacts" range="800"
rings="3" period="4"></ov-radar>
import "overscan/scope";
<ov-scope source="square" sweeps="16"
window="160"></ov-scope>
import "overscan/waterfall";
<ov-waterfall source="square" map="hue"></ov-waterfall>
import "overscan/map";
<ov-map lat="35" lon="139" span="16"
range="400" source="places"></ov-map>
import "overscan/gauge";
<ov-gauge source="flux" min="0" max="100"
unit="flux" ticks="5"></ov-gauge>
A UI kit for control panels, dashboards and telemetry screens. Web components and a stylesheet, ten dark themes, no build step and nothing to install alongside it. Every readout refuses to invent a number it was never given.
01src/tokens.css · ten fields
Set one attribute on any container and everything inside it changes: borders, depth, corners, type, motion, and the shader painting the background. The same panel is a genuinely different object in each one, which is why a swap is never a recolour. Every panel below is live.
white phosphor, flat
green phosphor, chunky
yellow and cyan, glitched
primary on true black, no finish at all
amber and cyan, smoke
phosphor green on true black, katakana rain
machine vision. red, interlaced, degraded
near-white, projected, banded and additive
saturated blue on navy, hairlines, very dense
magenta strokes on true black. no raster, so no scanlines
import 'overscan'
import 'overscan/overscan.css'
<div data-ov-theme="vector">
<ov-field></ov-field>
…
</div>import { OvField } from 'overscan/react'
<div data-ov-theme="vector">
<OvField />
…
</div>import { OvField } from 'overscan/vue'
<div data-ov-theme="vector">
<OvField />
…
</div>import OvField from 'overscan/svelte/OvField.svelte'
<div data-ov-theme="vector">
<OvField />
…
</div>ALLsrc/*.js · src/*.css
Everything the kit ships, live and running, before the six sections below pick out the ideas behind them. None of it is a screenshot. The wall is generated from the source and the build fails if it has dropped one, for the same reason the table below tells you it drew 100 of 50,000.
Generated from src/*.js. tools/gen_home.py refuses to write this page if any element defined in the kit is missing from it, so the count above is a measurement rather than a promise. The CSS layer's utility and state classes carry no picture of their own and are not on the wall: .ov-arrive, .ov-finish, .ov-scan, .ov-cluster, .ov-var, .ov-vertical, .ov-has-fault, .ov-has-ornament and .ov-snapguide.
02src/ov-segment.js · REFUSAL.md
Hand a display a value it cannot show and it tells you so, instead of showing something close. There are exactly four reasons a readout is allowed to give, they all draw the same dashes, and the display is not entitled to a fifth opinion.
| GIVEN | DRAWN, AND ITS OWN REASON | WHY |
|---|---|---|
| nominal | seven segments have no letters, and closing the gap would spell a number | |
| 99999 | five digits into four cells; a display that drops one is inventing the rest | |
| 412.0 | the instrument was declared 0 to 400, so this is not this instrument's reading | |
| (nothing) | no value has ever arrived, which is not the same as a value of zero |
import 'overscan'
import 'overscan/overscan.css'
<ov-segment
value="99999"
digits="4"
></ov-segment>import { OvSegment } from 'overscan/react'
<OvSegment
value="99999"
digits="4"
/>import { OvSegment } from 'overscan/vue'
<OvSegment
value="99999"
digits="4"
/>import OvSegment from 'overscan/svelte/OvSegment.svelte'
<OvSegment
value="99999"
digits="4"
/>03src/gauge.css · src/log.css
Analog instruments that report what they missed. The needle never smooths what it was handed, and the feed says when it dropped lines rather than scrolling them quietly away, because a log that hides its own gaps cannot be audited.
import 'overscan'
import 'overscan/overscan.css'
<ov-gauge
source="flux"
unit="flux"
ticks="5"
></ov-gauge>import { OvGauge } from 'overscan/react'
<OvGauge
source="flux"
unit="flux"
ticks="5"
/>import { OvGauge } from 'overscan/vue'
<OvGauge
source="flux"
unit="flux"
ticks="5"
/>import OvGauge from 'overscan/svelte/OvGauge.svelte'
<OvGauge
source="flux"
unit="flux"
ticks="5"
/>04src/scope.css · src/shaders/scope.glsl
A trace drawn the way a beam actually behaves: brightest where it moves slowest, so it burns on the flat tops and thins out on the fast edges. Stroking a polyline inverts that, and puts the brightest pixels exactly where the beam spent the least time.
import 'overscan'
import 'overscan/overscan.css'
<ov-scope
source="square"
sweeps="16"
></ov-scope>import { OvScope } from 'overscan/react'
<OvScope
source="square"
sweeps="16"
/>import { OvScope } from 'overscan/vue'
<OvScope
source="square"
sweeps="16"
/>import OvScope from 'overscan/svelte/OvScope.svelte'
<OvScope
source="square"
sweeps="16"
/>05src/table.css
Fifty thousand rows, and it says how many of them it actually drew rather than presenting a window as a total. It also states its own ARIA violation on the element, because a kit that hides one is asking you to find it in production.
import 'overscan'
import 'overscan/overscan.css'
<ov-table row-height="21"></ov-table>import { OvTable } from 'overscan/react'
<OvTable row-height="21" />import { OvTable } from 'overscan/vue'
<OvTable row-height="21" />import OvTable from 'overscan/svelte/OvTable.svelte'
<OvTable row-height="21" />06src/layout.css · src/window.css
Rails, split panes and draggable windows on a shared resize bus, so a pane that changes size tells its instruments instead of each one polling. Nothing here scrolls a document; it allocates a display.
import 'overscan'
import 'overscan/overscan.css'
<ov-window resizable>…</ov-window>import { OvWindow } from 'overscan/react'
<OvWindow resizable />import { OvWindow } from 'overscan/vue'
<OvWindow resizable />import OvWindow from 'overscan/svelte/OvWindow.svelte'
<OvWindow resizable />GETpackage.json · src/overscan.js
Install it from npm and import the elements you use, or point one script tag at the whole kit and write the tags. There is no compile step in either route, and nothing here is a placeholder: the package name, the subpaths and the URL are the real ones.
npm install overscan
import "overscan/radar";
import "overscan/overscan.css";
Every element is its own subpath, so you take
the ones you use: ov-radar with everything it
imports is 13 kB gzip. overscan/radar.js and
overscan/ov-radar.js resolve to the same file, for
anyone typing what they see in a file listing.
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/overscan/src/overscan.css">
<script type="module"
src="https://cdn.jsdelivr.net/npm/overscan/src/overscan.js"></script>
No install, no bundler, no import map. Not one
bare specifier ships in the source, so a browser resolves all
85 elements from that one URL. That is the whole kit,
424 kB of script and 46 kB of CSS gzip, one
request per module. Point it at src/ov-radar.js
instead to take a single element.
Either way, the markup is the same:
<ov-radar source="contacts" range="800"></ov-radar>