aobalabs design system
Shared visual vocabulary for sites in the aobalabs family. Pick a theme from the switcher to preview each component in the tenant palette it will ship in.
How to use this page. Each demo shows a live snippet and its copy-paste source. Click through the themes to verify a primitive reads correctly across every tenant before adopting it.
Foundations
Color tokens
All colours go through CSS custom properties so each tenant theme
swaps the palette at :root[data-theme="<tenant>"].
Use semantic tokens (--text-muted, --danger)
over raw hex anywhere outside the package's themes.css.
Surface
-
--bg-primarypage background -
--bg-elevatedcards, nav drawer, inputs -
--bg-tagchips, subtle fills
Text
-
--text-primarybody, headings -
--text-mutedmeta, hints, labels -
--text-faintde-emphasised meta, dropped
Border
-
--border-subtledefault field, card edge -
--border-focusfocused input, active field
Semantic
-
--accentprimary action, active state -
--successdone, paid, healthy -
--warningsnoozed, due soon -
--dangererrors, destructive actions
Primitives
Buttons
Four variants, each backed by a Go const on
ButtonVariant. The variant value IS the CSS class
suffix so adding a new style is one Go const + one CSS rule.
Primary
the dominant action on a screen — Capture, Save, Create
@Button(ButtonPrimary, "button", false) { Capture }Secondary
supporting action — Back, Cancel, alternate path
@Button(ButtonSecondary, "button", false) { Cancel }Ghost
low-emphasis action, used in dense rows
@Button(ButtonGhost, "button", false) { Edit }Destructive
irreversible action — Drop, Archive, Delete
@Button(ButtonDestructive, "button", false) { Drop }Links
Default, muted (for row meta), arrow (for section-to-section navigation). The arrow variant lifts on hover to telegraph the destination.
Default link
inline action or navigation target
<a class="link" href="#">view all captures</a>Muted link
metadata link, used in row meta
<a class="link link--muted" href="#">2 days ago</a>Arrow link
section-to-section navigation
<a class="link link--arrow" href="#">go to triage</a>Chips
Compact state and kind labels. Soft variants (success / warning / danger) pair a background tint with the matching text colour so the tone reads at a glance in dense rows.
Status chip
current entity state in dense lists
@Chip(ChipStatus, "active")Kind chip
discriminator for task / capture variants
@Chip(ChipKind, "payment")Location chip
shorthand for hierarchical location refs
@Chip(ChipLocation, "Sendai")Soft chip — success
outcome state — paid, done, healthy
@Chip(ChipSuccess, "paid")Soft chip — warning
transitional state — snoozed, due soon
@Chip(ChipWarning, "snoozed")Soft chip — danger
blocked / overdue / failed
@Chip(ChipDanger, "overdue")Keyboard chord
Single visual unit for any keypress hint. Compose multi-key chords as one string ("⌘K") rather than chaining multiple Kbd calls — the design treats each chip as one unit.
Single key
single keypress with no modifier
@Kbd("Esc")Chord
modifier + key combo — render as one chip
@Kbd("⌘K")Inline in prose
embedded in body copy — sized down a touch
Press @Kbd("⌘B") to toggle the nav.Status dot
Tiny coloured circle that pairs with adjacent label text. Four tone variants. Site-specific score-coloured variants (e.g. a 0–10 motivation scale) live in the consuming site, not here.
Active
currently in scope — default colour, no chip needed
@StatusDot(StatusDotActive, "active") in progressSnoozed
deliberately deferred — warm tone
@StatusDot(StatusDotSnoozed, "snoozed") deferred to next weekDone
completed — success tone
@StatusDot(StatusDotDone, "done") shipped 2 days agoDropped
intentionally abandoned — muted tone
@StatusDot(StatusDotDropped, "dropped") dropped — not pursuingTabs
Segmented control for in-page view switching. The active tab
carries the underline; one Tab per --view=… filter.
View filter tabs
segmented control for in-page view switching — list view filter, future activity-feed filters
<nav class="tabs" aria-label="Filter">
<a class="tab tab--active" href="#">Active</a>
<a class="tab" href="#">Snoozed</a>
<a class="tab" href="#">Dormant</a>
<a class="tab" href="#">Done</a>
<a class="tab" href="#">Dropped</a>
<a class="tab" href="#">All</a>
</nav>Patterns
Entity card
Standard chrome for detail pages: title + optional kind chip on the header, key-value fields in a two-column grid below.
Entity card
the chrome of a detail page — title + kind chip + key-value fields
Yuki Tanaka
person- yuki@example.jp
- Lives in
- Sendai, Miyagi
- Last contact
<article class="card">
<header class="card-header">
<h3 class="card-title">Yuki Tanaka</h3>
<span class="chip chip--kind">person</span>
</header>
<dl class="card-fields">
<div class="card-field">
<dt class="card-field-label">Email</dt>
<dd class="card-field-value">yuki@example.jp</dd>
</div>
<div class="card-field">
<dt class="card-field-label">Lives in</dt>
<dd class="card-field-value">Sendai, Miyagi</dd>
</div>
<div class="card-field">
<dt class="card-field-label">Last contact</dt>
<dd class="card-field-value"><time class="time" datetime="2026-05-12">May 12 · 18:20</time></dd>
</div>
</dl>
</article>List row
Whole-row clickable surface — wrap row contents in
<a class="list-row"> for a single hit area.
Hovers lift to --bg-tag.
Clickable row
whole-row link — label on the left, meta on the right
<ul class="list list--rows" role="list">
<li>
<a class="list-row" href="#">
<span class="list-row-label">Renew passport</span>
<span class="list-row-meta">due Jun 1</span>
</a>
</li>
<li>
<a class="list-row" href="#">
<span class="list-row-label">Pay NHK bill</span>
<span class="list-row-meta">Jan 12</span>
</a>
</li>
</ul>Breadcrumb
Single-page nav chrome with a back arrow. Optional right-aligned action button (Edit / Archive); more than one action wants a dedicated toolbar instead.
Back link
single-step nav up — leading arrow + muted link
<nav class="breadcrumb" aria-label="Breadcrumb">
<a class="link link--muted breadcrumb-back" href="#"><span aria-hidden="true">←</span> All tasks</a>
</nav>Breadcrumb with action
back link + right-aligned action — the standard show-page header
<nav class="breadcrumb" aria-label="Breadcrumb">
<a class="link link--muted breadcrumb-back" href="#"><span aria-hidden="true">←</span> All projects</a>
<a class="button button--secondary breadcrumb-action" href="#">Edit</a>
</nav>Page section
Standard subsection chrome. PageSection uses an
<h3>; PageSectionTop uses an
<h2> for the single page-level heading on new
/ edit pages.
Section (h3)
in-page subsection beneath the breadcrumb
Members
Section body content goes here.
<section class="page-section">
<h3 class="section-heading">Members</h3>
<p>Section body content goes here.</p>
</section>Top section (h2)
the single page-level heading on a new/edit page
Edit profile
Top-level form content.
<section class="page-section">
<h2 class="section-heading">Edit profile</h2>
<p>Top-level form content.</p>
</section>Button row
Flex container for action button groupings. Pairs of primary / destructive on detail pages, or grouped secondary actions in a card footer.
Action row
groups action buttons — typically Complete + Drop on detail pages
<div class="button-row">
<button class="button button--primary" type="button">Complete</button>
<button class="button button--destructive" type="button">Drop</button>
</div>