Skip to content
RU
Scale
Density
Theme
Accent

Charts

Share

What a period was made of: twenty-two hours up, an hour and a half down, half an hour nobody looked. One bar, divided by the share of each outcome in it.

The component
Markup
<div class="inst-share" role="img"
     aria-label="The last 24 hours: 92% up, 6% down, 2% not checked">
  <span class="inst-share-part" data-tone="ok" style="--share:0.92"></span>
  <span class="inst-share-part" data-tone="error" style="--share:0.06"></span>
  <span class="inst-share-part" data-tone="neutral" style="--share:0.02"></span>
</div>

Contract

What Required Why
A role="img" and an aria-label with the shares in words yes Three empty <span>s are three empty <span>s to a screen reader. The reading has to be text: "92% up, 6% down"
A --share on every part yes With no share a part gets a grow factor of 0 and is not drawn. The number is the data and comes from the server
A tone from the vocabulary yes neutral · running · ok · warn · error. There is no sixth outcome
Shares that sum to 1 no What is not accounted for stays track, and a bar a third full is the honest drawing of eight hours of data out of twenty-four
A minimum width for a small share no, and it is refused A floor would draw 0.02% as wide as 2%, and the bar would stop reporting proportion in the units of a bar that claims to report proportion

The style="--share:0.92" is the value rather than styling, by the same rule as the width of a meter fill: the quantity comes from the server and physically cannot live in static CSS. Everything else — the height, the corner, the track, the colour — comes from the library.

The extent is drawn by the inner hairline, as on a meter: the track stays light enough for a tone on it to hold 3:1.

The parts are square inside a clipped strip. Rounding each part would put an arc at every internal boundary — four corners where the data has one edge — and at --size-meter those arcs meet and leave a notch of track showing between two neighbouring outcomes.

Accessibility

The name A role="img" plus an aria-label with the shares in words. The parts are empty and are not read; what is read is the name of the bar
Colour is not the only carrier The bar stands beside the numbers, in a legend or in a line of text. On its own it says "mostly one thing"; which thing is a matter of the label
Contrast A part takes --tone-mark — a 3:1 threshold against the surface, the same as a state mark. The track is not load-bearing and is not asked for 3:1
Motion There is none. A share is a state rather than a process, and the bar does not animate as the data updates
Forced-colours mode The strip gets a CanvasText border and the parts survive the reset, because they carry a value. The tones collapse into one fill there, so what stays readable is the division: each part but the last draws a line on its trailing edge
Print It is printed as it is: the parts are told apart by tone and stay grey steps on paper

Sizes

Example
Markup
<div class="inst-share" role="img" aria-label="Today: 88% up, 9% degraded, 3% down">
  <span class="inst-share-part" data-tone="ok" style="--share:0.88"></span>
  <span class="inst-share-part" data-tone="warn" style="--share:0.09"></span>
  <span class="inst-share-part" data-tone="error" style="--share:0.03"></span>
</div>
<div class="inst-share inst-share--lg" role="img" aria-label="Today: 88% up, 9% degraded, 3% down">
  <span class="inst-share-part" data-tone="ok" style="--share:0.88"></span>
  <span class="inst-share-part" data-tone="warn" style="--share:0.09"></span>
  <span class="inst-share-part" data-tone="error" style="--share:0.03"></span>
</div>

Two, for the two places the bar stands. The base is the meter's height, because in a row of data this is the meter's job with more than one answer. The large one is a glyph's height: on the screen of a single object the bar is the subject rather than an annotation to a number.

Composition

With a legend

Example
  • up 92%
  • down 6%
  • not checked 2%
Markup
<div class="inst-stack">
  <div class="inst-share" role="img"
       aria-label="The last 24 hours: 92% up, 6% down, 2% not checked">
    <span class="inst-share-part" data-tone="ok" style="--share:0.92"></span>
    <span class="inst-share-part" data-tone="error" style="--share:0.06"></span>
    <span class="inst-share-part" data-tone="neutral" style="--share:0.02"></span>
  </div>
  <ul class="inst-legend">
    <li class="inst-legend-item" data-tone="ok"><span class="inst-legend-swatch"></span>up <span class="inst-legend-value">92%</span></li>
    <li class="inst-legend-item" data-tone="error"><span class="inst-legend-swatch"></span>down <span class="inst-legend-value">6%</span></li>
    <li class="inst-legend-item"><span class="inst-legend-swatch" data-empty></span>not checked <span class="inst-legend-value">2%</span></li>
  </ul>
</div>

The swatch reads --tone-mark when no --series is set, so one legend keys both a chart of series and a bar of states. An hour nobody checked is neither: it takes data-empty, the same word inst-history-tick uses for it — drawn as the track, because a swatch that paints nothing is a missing swatch rather than an entry about absence.

Above a history strip

In context
Markup
<div class="inst-stack">
  <div class="inst-share" role="img" aria-label="The last 12 hours: 92% up, 8% down">
    <span class="inst-share-part" data-tone="ok" style="--share:0.92"></span>
    <span class="inst-share-part" data-tone="error" style="--share:0.08"></span>
  </div>
  <div class="inst-history" role="img" aria-label="12 hours, 4 checks each: 44 succeeded, 4 with an error">
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="error"></span><span class="inst-history-tick" data-tone="error"></span><span class="inst-history-tick" data-tone="error"></span><span class="inst-history-tick" data-tone="error"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
    <span class="inst-history-group" style="--n:4"><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span><span class="inst-history-tick" data-tone="ok"></span></span>
  </div>
</div>

A strip answers when, a share answers how much, and an uptime screen wants both. The comparison stands in the section index.

API

Classes
NameWhat it does
class
inst-shareThe bar. The carrier of the role and the name
inst-share-partOne outcome. Its width is its share of the whole
inst-share--lgThe height of a glyph, for a bar that is the subject of the screen rather than an annotation to a number
Attributes
NameValueWhat it does
attribute
data-toneneutral · running · ok · warn · errorThe outcome of a part. Closed. With no attribute, the accent
roleimgThe bar is a picture of data, and it has to have a name
Tokens
NameValueWhere from
token
--shareThe share of a part, 0 to 1. It is the value rather than styling and comes from the data—one across every cell
--size-meter6pxbase +4: scale
--size-icon18pxbase +4: scale
--radius-markvar(--radius-2xs)one across every cell
--tracklight-dark(oklch(0 0 0 / 0.10), oklch(1 0 0 / 0.16))one across every cell
--hairline1pxbase +1: screen
--borderlight-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11))one across every cell
--tone-markvar(--text-secondary)base +4:
--accent-marklight-dark(var(--a-4), var(--a-3))one across every cell

Why the kit works this way — the constitution · Open source under MIT, github.com/keshon/instrument