Skip to content

Data display

A data table in the HUD style: the header is a small uppercase eyebrow, rows are separated by hairlines and numbers align on tabular figures. Use real table markup: <caption>, <th scope> and <tfoot> give assistive technology the structure. Wrap the table in sb-table-wrap so it scrolls sideways on narrow screens instead of widening the page.

Active jobs on the board. The selected row carries aria-selected.
Job Fixer District Reward Status
Ghost in the wireDakotaBadlands 12,400 Open
Static bloomReginaWatson 8,750 Taken
Cold storageWakakoWestbrook 21,000 Pending
Neon requiemPadreHeywood 4,200 Failed
Total46,350
<div class="sb-table-wrap">
<table class="sb-table sb-table-striped sb-table-hover">
<caption>Active jobs on the board. The selected row carries aria-selected.</caption>
<thead>
<tr>
<th scope="col" aria-sort="ascending">Job</th>
<th scope="col">Fixer</th>
<th scope="col" aria-sort="none">District</th>
<th scope="col" class="sb-table-num">Reward</th>
<th scope="col" class="sb-table-center">Status</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Ghost in the wire</th><td>Dakota</td><td>Badlands</td>
<td class="sb-table-num">12,400</td>
<td class="sb-table-center"><span class="sb-badge sb-badge-success">Open</span></td>
</tr>
<tr aria-selected="true">
<th scope="row">Static bloom</th><td>Regina</td><td>Watson</td>
<td class="sb-table-num">8,750</td>
<td class="sb-table-center"><span class="sb-badge sb-badge-info">Taken</span></td>
</tr>
<tr>
<th scope="row">Cold storage</th><td>Wakako</td><td>Westbrook</td>
<td class="sb-table-num">21,000</td>
<td class="sb-table-center"><span class="sb-badge sb-badge-warning">Pending</span></td>
</tr>
<tr>
<th scope="row">Neon requiem</th><td>Padre</td><td>Heywood</td>
<td class="sb-table-num">4,200</td>
<td class="sb-table-center"><span class="sb-badge sb-badge-danger">Failed</span></td>
</tr>
</tbody>
<tfoot>
<tr><th scope="row" colspan="3">Total</th><td class="sb-table-num">46,350</td><td></td></tr>
</tfoot>
</table>
</div>
Class / selector Effect
sb-table The table. Captions render below the table.
sb-table-wrap Scroll container with a tube border and radius; also scrolls vertically if you give it a height.
sb-table-striped Zebra rows.
sb-table-hover Row highlight on hover.
sb-table-compact Tighter cells.
sb-table-bordered Vertical hairlines between cells.
sb-table-sticky Sticky header cells; the wrap must have a fixed height and overflow: auto.
sb-table-caption-top Moves the caption above the table.
sb-table-num End-aligned numeric cell with tabular figures. sb-num does the same inside a table.
sb-table-center Centered cell.
th[aria-sort="ascending"], th[aria-sort="descending"] Sorted column: highlighted header with an arrow.
th[aria-sort] Marks the header as sortable.
tbody tr[aria-selected="true"], tbody tr[data-selected] Selected row: a tint and an accent mark on the first cell.
Compact, bordered, with a sticky header
AccentRoleOrder
plasmabrand primary1
laserdanger2
emberparameters3
sodiumwarning4
halogentypes5
acidsuccess6
coolantoperators7
frostproperties8
ClassLevel
Netrunner50
Solo (data-selected)42
Techie31
<div class="sb-table-wrap" style="height: 11rem; overflow: auto">
<table class="sb-table sb-table-compact sb-table-bordered sb-table-sticky sb-table-caption-top">
<caption>Compact, bordered, with a sticky header</caption>
<thead>
<tr><th scope="col">Accent</th><th scope="col">Role</th><th scope="col" class="sb-table-num">Order</th></tr>
</thead>
<tbody>
<tr><td>plasma</td><td>brand primary</td><td class="sb-num">1</td></tr>
<tr><td>laser</td><td>danger</td><td class="sb-num">2</td></tr>
<tr><td>ember</td><td>parameters</td><td class="sb-num">3</td></tr>
<tr><td>sodium</td><td>warning</td><td class="sb-num">4</td></tr>
<tr><td>halogen</td><td>types</td><td class="sb-num">5</td></tr>
<tr><td>acid</td><td>success</td><td class="sb-num">6</td></tr>
<tr><td>coolant</td><td>operators</td><td class="sb-num">7</td></tr>
<tr><td>frost</td><td>properties</td><td class="sb-num">8</td></tr>
</tbody>
</table>
</div>
<div class="sb-table-wrap">
<table class="sb-table">
<thead>
<tr><th scope="col">Class</th><th scope="col" aria-sort="descending">Level</th></tr>
</thead>
<tbody>
<tr><td>Netrunner</td><td>50</td></tr>
<tr data-selected><td>Solo (data-selected)</td><td>42</td></tr>
<tr><td>Techie</td><td>31</td></tr>
</tbody>
</table>
</div>

The CSS only styles aria-sort. If the column header is a sort button, put the button inside the <th>, set aria-sort on the <th> and update it from your script.

sb-kbd is a keycap: mono type, a tube border with a 2px bottom edge. Use the real <kbd> element.

F Esc sm Space E Shift Ctrl+K
<kbd class="sb-kbd">F</kbd>
<kbd class="sb-kbd">Esc</kbd>
<kbd class="sb-kbd sb-kbd-sm">sm</kbd>
<kbd class="sb-kbd sb-kbd-lg">Space</kbd>
<kbd class="sb-kbd sb-kbd-primary">E</kbd>
<kbd class="sb-kbd" data-pressed>Shift</kbd>
<span class="sb-kbd-group"><kbd class="sb-kbd">Ctrl</kbd>+<kbd class="sb-kbd">K</kbd></span>
Class / attribute Effect
sb-kbd Keycap, 1.5rem tall.
sb-kbd-sm, sb-kbd-lg 1.25rem and 2rem keycaps.
sb-kbd-primary Primary ink, border and bloom, for a highlighted or bound key.
[data-pressed], [aria-pressed="true"] Pressed look: primary ink and a key that sinks 1px. Set it from your key handler.
sb-kbd-group Wrapper for a combination such as Ctrl + K.

A key hint is a keycap plus a label: the “[F] Interact” prompt of a HUD. sb-key-hints lays several out in a wrapping row. On a HUD they should sit on a glass strip (the demo uses the sb-glass utility) rather than straight on the game.

FInteract EPick up TabScanner RReload QQuickhack CCrouch SpaceJump
1Primary 2Secondary
<div class="sb-key-hints sb-glass" style="padding: var(--sb-space-2) var(--sb-space-4); border-radius: var(--sb-radius-md)">
<span class="sb-key-hint"><kbd class="sb-kbd">F</kbd><span class="sb-key-hint-label">Interact</span></span>
<span class="sb-key-hint" data-active><kbd class="sb-kbd">E</kbd><span class="sb-key-hint-label">Pick up</span></span>
<span class="sb-key-hint sb-key-hint-active"><kbd class="sb-kbd">Tab</kbd><span class="sb-key-hint-label">Scanner</span></span>
<span class="sb-key-hint" aria-disabled="true"><kbd class="sb-kbd">R</kbd><span class="sb-key-hint-label">Reload</span></span>
<span class="sb-key-hint sb-key-hint-disabled"><kbd class="sb-kbd">Q</kbd><span class="sb-key-hint-label">Quickhack</span></span>
<span class="sb-key-hint sb-key-hint-sm"><kbd class="sb-kbd sb-kbd-sm">C</kbd><span class="sb-key-hint-label">Crouch</span></span>
<span class="sb-key-hint sb-key-hint-lg"><kbd class="sb-kbd sb-kbd-lg">Space</kbd><span class="sb-key-hint-label">Jump</span></span>
</div>
<div class="sb-key-hints sb-key-hints-vertical sb-glass" style="padding: var(--sb-space-2) var(--sb-space-4); border-radius: var(--sb-radius-md)">
<span class="sb-key-hint"><kbd class="sb-kbd">1</kbd><span class="sb-key-hint-label">Primary</span></span>
<span class="sb-key-hint"><kbd class="sb-kbd">2</kbd><span class="sb-key-hint-label">Secondary</span></span>
</div>
Class / attribute Effect
sb-key-hint The prompt row: display type, uppercase.
sb-key-hint-label The action text.
sb-key-hint-sm, sb-key-hint-lg Smaller and larger prompt.
sb-key-hint-active, [data-active] The prompt that is currently available: brighter label and a primary keycap.
sb-key-hint-disabled, [aria-disabled="true"] Dimmed prompt.
sb-key-hints Wrapping row of hints.
sb-key-hints-vertical Column of hints.

sb-code is an inline code chip. sb-pre frames a block, either on a <pre> directly or on a <figure> that wraps a header, a <pre> and a caption. Wrap each line in <span class="sb-line"> to enable line numbers and per-line marks. The CSS does not highlight syntax; bring your own highlighter and keep the sb-line spans.

Inline sb-code, sb-code-primary, sb-code-secondary, sb-code-muted and sb-code-wrap lets a very long identifier like open77_resource_manifest_version break across lines.

<p>
Inline <code class="sb-code">sb-code</code>, <code class="sb-code sb-code-primary">sb-code-primary</code>,
<code class="sb-code sb-code-secondary">sb-code-secondary</code>, <code class="sb-code sb-code-muted">sb-code-muted</code>
and <code class="sb-code sb-code-wrap">sb-code-wrap lets a very long identifier like open77_resource_manifest_version break across lines</code>.
</p>
Class Effect
sb-code Inline code chip.
sb-code-primary, sb-code-secondary, sb-code-muted Ink variants.
sb-code-wrap Allows a long identifier to wrap.
fxmanifest.lua lua
resource "static-bloom-hud"
version "1.0.0"
web_ui_page "dist/index.html"
web_files { "dist/**" }
client_script "old.lua"
client_script "client.lua"
Line numbers come from CSS counters; highlight, add and delete mark lines.
Terminal
pnpm add @static-bloom/css
Packages: +3
pnpm build:css
pre.sb-pre with sb-pre-wrap: a very long line that would otherwise scroll sideways wraps instead, keeping the whole statement visible on narrow viewports.
function focus() {
  return 'sb-line-focus dims the other lines'
}
<figure class="sb-pre sb-pre-lines">
<div class="sb-pre-header">
<span class="sb-pre-filename">fxmanifest.lua</span>
<span class="sb-pre-lang">lua</span>
</div>
<pre><code><span class="sb-line">resource "static-bloom-hud"</span>
<span class="sb-line">version "1.0.0"</span>
<span class="sb-line sb-line-highlight">web_ui_page "dist/index.html"</span>
<span class="sb-line">web_files { "dist/**" }</span>
<span class="sb-line sb-line-del">client_script "old.lua"</span>
<span class="sb-line sb-line-add">client_script "client.lua"</span></code></pre>
<figcaption class="sb-pre-caption">Line numbers come from CSS counters; highlight, add and delete mark lines.</figcaption>
</figure>
<figure class="sb-pre sb-pre-terminal sb-pre-sm">
<div class="sb-pre-header"><span class="sb-pre-title">Terminal</span></div>
<pre><code><span class="sb-line sb-prompt">pnpm add @static-bloom/css</span>
<span class="sb-line">Packages: +3</span>
<span class="sb-line sb-prompt">pnpm build:css</span></code></pre>
</figure>
<pre class="sb-pre sb-pre-wrap"><code>pre.sb-pre with sb-pre-wrap: a very long line that would otherwise scroll sideways wraps instead, keeping the whole statement visible on narrow viewports.</code></pre>
<figure class="sb-pre">
<pre><code><span class="sb-line">function focus() {</span>
<span class="sb-line sb-line-focus"> return 'sb-line-focus dims the other lines'</span>
<span class="sb-line">}</span></code></pre>
</figure>
Class Effect
sb-pre The block frame (on <pre> or <figure>); scrolls sideways on overflow.
sb-pre-header Title bar of a figure.
sb-pre-filename, sb-pre-title, sb-pre-lang File name, a plain title and the language tag in the header.
sb-pre-actions Right-aligned slot in the header for buttons such as a copy button.
sb-pre-caption Caption under the block (<figcaption>).
sb-pre-lines Adds line numbers (CSS counters on .sb-line).
sb-pre-terminal Terminal styling.
sb-pre-sm Smaller type.
sb-pre-wrap Wraps long lines instead of scrolling.
sb-line One line of code.
sb-line-highlight Highlighted line.
sb-line-add, sb-line-del Added and deleted lines (diff).
sb-line-focus Focused line; the other lines dim.
sb-prompt Marks a command line in a terminal block with a prompt glyph.

The block is scrollable, so make it reachable from the keyboard: add tabindex="0" and a name to a <pre> that can overflow, unless it sits in a figure you control.

An avatar is a round (or square) user picture with an initials fallback and an optional status dot. Put the <img> first and the fallback after it; the fallback shows when there is no image or it fails to load. Give a meaningful alt to a lone avatar, and alt="" when the name is printed beside it.

xs sm md lg xl 2x
VS VS VS GR
JS AW RA PW
VS JS PW RA +6
<div class="sb-stack">
<div class="sb-cluster">
<span class="sb-avatar sb-avatar-xs"><span class="sb-avatar-fallback">xs</span></span>
<span class="sb-avatar sb-avatar-sm"><span class="sb-avatar-fallback">sm</span></span>
<span class="sb-avatar sb-avatar-md"><span class="sb-avatar-fallback">md</span></span>
<span class="sb-avatar sb-avatar-lg"><span class="sb-avatar-fallback">lg</span></span>
<span class="sb-avatar sb-avatar-xl"><span class="sb-avatar-fallback">xl</span></span>
<span class="sb-avatar sb-avatar-2xl"><span class="sb-avatar-fallback">2x</span></span>
</div>
<div class="sb-cluster">
<span class="sb-avatar sb-avatar-square sb-avatar-sm"><span class="sb-avatar-fallback">VS</span></span>
<span class="sb-avatar sb-avatar-square"><span class="sb-avatar-fallback">VS</span></span>
<span class="sb-avatar sb-avatar-square sb-avatar-lg"><span class="sb-avatar-fallback">VS</span></span>
<span class="sb-avatar sb-avatar-lg sb-avatar-ring"><span class="sb-avatar-fallback">GR</span></span>
</div>
<div class="sb-cluster">
<span class="sb-avatar sb-avatar-lg"><span class="sb-avatar-fallback">JS</span><span class="sb-avatar-status sb-avatar-status-online"></span></span>
<span class="sb-avatar sb-avatar-lg"><span class="sb-avatar-fallback">AW</span><span class="sb-avatar-status sb-avatar-status-away"></span></span>
<span class="sb-avatar sb-avatar-lg"><span class="sb-avatar-fallback">RA</span><span class="sb-avatar-status sb-avatar-status-busy"></span></span>
<span class="sb-avatar sb-avatar-lg"><span class="sb-avatar-fallback">PW</span><span class="sb-avatar-status sb-avatar-status-offline"></span></span>
</div>
<div class="sb-avatar-group">
<span class="sb-avatar"><span class="sb-avatar-fallback">VS</span></span>
<span class="sb-avatar"><span class="sb-avatar-fallback">JS</span></span>
<span class="sb-avatar"><span class="sb-avatar-fallback">PW</span></span>
<span class="sb-avatar"><span class="sb-avatar-fallback">RA</span></span>
<span class="sb-avatar sb-avatar-group-count"><span class="sb-avatar-fallback">+6</span></span>
</div>
</div>

With a picture:

<span class="sb-avatar sb-avatar-lg">
<img class="sb-avatar-img" src="/avatars/vs.png" alt="V. Silverhand" />
<span class="sb-avatar-fallback">VS</span>
<span class="sb-avatar-status sb-avatar-status-online"></span>
</span>
Class Effect
sb-avatar The circle, medium (2.5rem).
sb-avatar-xs, -sm, -md, -lg, -xl, -2xl 1.5, 2, 2.5, 3, 4 and 5rem.
sb-avatar-square Rounded square instead of a circle.
sb-avatar-ring Accent ring.
sb-avatar-img The picture, covering the avatar.
sb-avatar-fallback Initials or an icon.
sb-avatar-status Status dot in the corner; combine with a state class.
sb-avatar-status-online, -away, -busy, -offline Status color. The color is not the only cue: also expose the status as text.
sb-avatar-group Overlapping row of avatars with a canvas-colored ring.
sb-avatar-group-count The “+6” overflow avatar in a group.

A divider is a hairline. Use <hr> for a plain rule and an element with role="separator" when it carries a label.







<div class="sb-stack">
<hr class="sb-divider" />
<div class="sb-divider" role="separator">Labelled</div>
<div class="sb-divider sb-divider-start" role="separator">Start aligned</div>
<div class="sb-divider sb-divider-end" role="separator">End aligned</div>
<hr class="sb-divider sb-divider-strong" />
<hr class="sb-divider sb-divider-dashed" />
<div class="sb-divider sb-divider-dashed sb-divider-strong" role="separator">Dashed strong</div>
<hr class="sb-divider sb-divider-neon" />
<div class="sb-divider sb-divider-neon" role="separator">Neon labelled</div>
<hr class="sb-divider sb-divider-tight" />
<hr class="sb-divider sb-divider-loose" />
</div>
Class Effect
sb-divider 1px hairline, or a line either side of a centered label.
sb-divider-start, sb-divider-end Aligns the label to the start or the end.
sb-divider-strong Stronger line color.
sb-divider-dashed Dashed line.
sb-divider-neon Plasma-to-ultraviolet gradient tube with a bloom.
sb-divider-tight, sb-divider-loose Less and more space around the rule.
sb-divider-vertical Vertical rule for a flex row.
Left Middle Dashed Neon
<div class="sb-cluster" style="min-height: 4rem; align-items: stretch">
<span>Left</span>
<span class="sb-divider sb-divider-vertical"></span>
<span>Middle</span>
<span class="sb-divider sb-divider-vertical sb-divider-dashed"></span>
<span>Dashed</span>
<span class="sb-divider sb-divider-vertical sb-divider-neon"></span>
<span>Neon</span>
</div>