Release notes

Audit & release notes

A design system isn't done at launch — it's done when it stays right under pressure. This page is the public artifact of every maintenance pass since v1.0 shipped — each finding, decision, and fix, in the open. Newest first.

Cumulative totals · all releases v1.0 → v1.5 · latest pass Reserve green (semantic-color pass)
Findings logged
18
Across a11y, motion, contrast, naming, color, and architecture.
Fixes shipped
15
Tokens, component patches, and a three-tier architecture.
Deferred
3
Out-of-scope or needs broader RFC.
Breaking changes
0
Every v1.0 class still renders.

Why this page exists

Most of the systems I work on live behind NDAs. I can't show the dashboards, the monitoring panels, or the operator tools that pay the bills. Tiny Wire is the public-safe twin — same constraints, same density, same warm operational tone — built so the process is visible without leaking a single line of client work.

This audit is the proof. It shows what shipping a v1.0 and then maintaining it actually looks like: catching the regressions, defending the decisions, and writing down enough context that the next person (or the next quarter's me) doesn't have to relitigate them.

v1.5 — Reserve green

2026-06-14 · semantic-color pass, zero breaking changes (back-compat aliases kept).

Building a conceptual attestation prototype on the system surfaced the finding the manual eye had normalized: green was doing double duty — the brand accent and the semantic "healthy / attested / safe" signal. When the brand mark, primary buttons, focus rings, and the "this is safe" chip all share one green, the trust signal stops popping. v1.5 reserves green strictly for a new --success family (attested / approved / safe / healthy / running) and moves brand + all interaction onto the existing cobalt — so the rarer green now reads as a real verdict. Token names are unchanged and the old .alert-brand / .banner-brand / .tag-brand remain as aliases, so the prototype inherited the entire shift through the semantic tokens — no per-screen edits. A free win fell out of it: moving brand off green closed the longest-deferred a11y finding (brand-on-white text, 3.54:1 → cobalt 8.3:1).

IDFindingSeverityStatus
F-017 Green carried both the brand accent and the "safe / attested" status meaning — overloaded, so the trust signal never popped. Reserved green for a new --success family; brand + all interaction moved to cobalt; back-compat aliases kept. Medium Fixed
F-018 The green-as-text contrast issue moved with the role: --success (green-500) as text on light tints is still ~3.5:1. Guidance shipped — --success-dark for text, bare --success for non-text (dots, borders, fills); a lint rule against bare --success on text is queued. Low Deferred · lint rule

v1.4 — Live demos

2026-06-13 · docs-experience pass, no token or class changes.

A structured UX critique (FLOWIE) found the docs were full of false affordances — demos that looked interactive but did nothing — plus wayfinding gaps and a scroll order that didn't track the nav. All fixed without touching the CSS-only system: the docs gained a thin vanilla-JS layer so every demo is honest (tabs, accordion, popover, command search, dialog/sheet now work), code blocks copy in one click, long pages get an auto table of contents + back-to-top, Foundations gets token search, and every page's scroll order now equals its sidebar order. The orphaned sidebar-rail page and the dead "view diff" links were fixed too. Full list in the changelog.

v1.3 — Pedigree pass

2026-06-12 · additive, zero breaking changes.

A systematic rigor + signature pass. The neutrals consolidated into one coherent warm-neutral ramp; a single cobalt accent replaced the two overlapping blues and now owns focus; the warning brown became a disciplined amber; and the whole set was restructured into a three-tier primitive → semantic → component architecture — values only, every public token name preserved, so downstream prototypes inherit it unbroken. Type moved to a 1.2 modular display scale with em tracking and a mandatory tabular-numerals token; the default radius sharpened 10→8; floating overlays dropped their solid border for a hairline and let elevation do the lifting. Four issues surfaced and were fixed in the same pass:

IDFindingSeverityStatus
F-013 Dark --text-tertiary failed AA on --surface-inset (4.05:1). Lifted #948682 → #A0928E; the live checker now passes every pair in both themes. High Fixed
F-014 lib/tokens.js had drifted from globals.css — three values (light/dark tertiary, dark disabled) shipped pre-AA hexes to JS consumers. Resynced. Medium Fixed
F-015 The .select chevron was a hardcoded #8A7E7D (a stale token value) that read low-contrast in dark mode. Repointed to a mid-neutral legible in both themes. Medium Fixed
F-016 Floating overlays stacked a solid border and a full elevation shadow — a doubled container signal. Border → --border-hairline; elevation does the lifting. Low Fixed

v1.2 — Sidebar rail

2026-06-11 · additive, zero breaking changes.

Added .sidebar--rail — the collapsed, icon-only form of the app sidebar (56px, driven by --sidebar-w). It reuses every existing .sidebar* rule and token: labels hide, icons center and grow to 20px, the active state is unchanged. See the Sidebar component and the rail demo; the full entry lives in the changelog. Scope was the rail only — the deferred a11y findings below remain queued for a later release.

v1.1 — Findings

Twelve issues, grouped by theme. Click a filter to scope the list. Every fixed finding links to the line that changed.

Filter
F-001 Critical Accessibility Motion
No prefers-reduced-motion support
Fixed

The monitoring dot ripple, the skeleton shimmer, the modal entrance, the toast slide-up, and the cross-fade on theme change all kept running regardless of OS reduced-motion settings.

A user with a vestibular disorder visiting the docs site would see ambient pulses they can't turn off. This is a WCAG 2.3.3 fail.

Added a global @media (prefers-reduced-motion: reduce) rule in globals.css that crushes every animation and transition to ~1ms. Components don't need to opt in.

Ambient pulses freeze, entrances become instant, but state changes still happen — just without the choreography.

lib/globals.css · +9 lines View on GitHub ↗
Try disabling motion in your OS — this dot stops pulsing.
F-002 Critical Accessibility
Focus ring overrode invalid-state borders
Fixed

The global :focus-visible rule used border-color: var(--brand) !important, which silently overrode the red border on an aria-invalid="true" input the moment a user focused it.

So the only state where the user most needs to know the field is wrong — when they're standing on it — was hiding the signal.

Dropped the !important border override. The 3px shadow ring (--shadow-focus) is now the universal focus indicator, and components own their own border colour — so invalid inputs stay red whether focused or not.

Before — focused invalid
After — focused invalid
lib/globals.css · −1 line View on GitHub ↗
F-003 High Accessibility Contrast
.btn-link read as disabled text
Fixed

The link button used --text-tertiary (#8A7E7D on white = 3.85:1 — failing WCAG AA for body copy at 4.5:1). It also visually clashed with disabled-text styling, so users hesitated to click it.

Switched to --info (#1B4079) — a deep navy that carries link semantics across the system and clears AA comfortably. Hover now lifts to --text-primary with a thicker underline.

Before
Read more 3.85:1
After
9.4:1
lib/components.css · btn-link View on GitHub ↗
F-004 High Motion
Global colour transition slowed every hover
Fixed

v1.0 had a permanent :root * rule that transitioned background-color, border-color, color, fill, and stroke over 220ms — so the cross-fade on theme change would be smooth.

But that rule also ran on every hover, focus, and click — slowing the intended 150ms button hover and making icon colour changes feel laggy.

Demoted to a one-shot .theme-swapping class the toggle adds on click and removes after 280ms. Theme cross-fade still looks smooth; every other interaction is back to snap.

Toggle the theme — notice the swap still feels deliberate, but button hovers are now instant.

lib/globals.css + docs/docs.js View on GitHub ↗
F-005 High Contrast
Dark-mode disabled text was invisible
Fixed

In dark mode, --text-disabled (#5F5450) on --surface-muted (#2A211E) landed at ~2.3:1. Disabled buttons looked empty. Even users without low vision struggled to read the label.

Lightened to #756763 (~4.1:1) — still visibly de-emphasised, but the label survives. Light-mode --text-disabled already passed; left alone.

Before
After
lib/globals.css · --text-disabled (dark) View on GitHub ↗
F-006 Medium Contrast
Tertiary text was borderline on AA
Fixed

--text-tertiary (#8A7E7D on white) landed at 3.85:1 — fine for UI elements, but the same token was being used for helper text and placeholders, where AA wants 4.5:1 for normal body copy.

Darkened one step to #7A6E6D (4.65:1). Visually still reads as a third-tier label; functionally now AA-compliant. Cascades to every helper, hint, secondary nav link, and breadcrumb.

lib/globals.css · --text-tertiary View on GitHub ↗
F-007 Medium Accessibility
Switch and slider had no keyboard focus ring
Fixed

Both controls killed their default outline in v1.0 but never added a custom ring. The global :focus-visible shadow was clipped by the switch's own background fill, and never reached the slider thumb at all. Keyboard users couldn't tell what was focused.

Switch now carries a two-stop shadow (surface gap + ring) to clear its background. Slider explicitly applies a 4px ring to both the -webkit- and -moz-range-thumb pseudos.

After — tab onto these
lib/components.css · .switch, .slider View on GitHub ↗
F-008 Medium Naming
Missing .banner-warn
Fixed

Banners shipped with danger, brand, and info — but no warning variant. Meanwhile .alert-warn and .chip-warn both exist. A consumer reaching for "banner-warn" by analogy would get nothing.

Added it. Inherits from the same --warning token; same border + tint formula as the other banner variants.

lib/components.css · +5 lines View on GitHub ↗
F-009 Low Naming
.dot-amber wasn't amber
Fixed

The .dot-amber class pointed at --warning — which is a warm brown, not amber. Anyone naming a status by colour and getting brown out is going to file a bug.

Added a semantic .dot-warn for the brown, and re-pointed .dot-amber at the actual --chart-amber token (#B98326). Existing consumers using .dot-amber will see a colour shift — flagged in the changelog as a deprecation.

.dot-warn
.dot-amber
.dot-red
lib/components.css · .dot-* View on GitHub ↗
F-010 Low Accessibility
No indeterminate checkbox state
Fixed

The "header" checkbox in a data table — the one that's neither fully checked nor fully empty when some rows are selected — had no visual treatment. Native browsers render a dash; v1.0 didn't style it.

Added .checkbox:indeterminate with a centred 8×2 white dash on the brand-green fill. Matches the checked state's visual weight.

lib/components.css · +5 lines View on GitHub ↗
F-011 Medium Accessibility
Toast / spinner / skeleton ARIA hooks not documented
Deferred · backlog

Live-region components ship as CSS only — no role="status", aria-live, or aria-busy guidance in the docs. Consumers who copy the markup verbatim ship inaccessible feedback.

Adding placeholder selectors in CSS would lie about coverage. The real fix is doc updates with full examples per component — a writing pass, not a styling pass. On the backlog alongside the toast queue manager.

Tracking issue: #TW-014.

docs/components.html · backlog
F-012 Medium Accessibility
Dialog has no focus-trap implementation
Deferred · v2.0

Tab order escapes the dialog into the page behind it. The dialog also doesn't restore focus to the trigger on close. Without JS, this is unavoidable — v1.0 is CSS-only by design.

Fixing it properly means shipping a small JS controller (data-dialog-trigger + data-dialog-target), which is a v2.0 question: do we want Tiny Wire to have behaviour, or stay CSS-only? Drafting an RFC instead of shipping something half-baked.

RFC · TW-018 (drafting)

How I audit

Three passes, three goals. I run this same loop on every system I maintain, internal or public.

01
Read the contract

Walk every component and ask: what does the class promise? Every CSS rule that contradicts the promise — silent overrides, missing states, name/value drift — gets logged.

02
Stress the edges

Tab through every interactive element. Toggle to dark mode mid-interaction. Trigger every aria-invalid, disabled, and indeterminate. The edges are where v1.0 leaks.

03
Defend what stays

Not every finding is fixed in the same release. Deferred items get a tracking ID, a scheduled version, and a reason — so the next quarter's me doesn't relitigate them from scratch.

What I won't ship (and why)

A design system is as much about what you refuse to add as what's in. These came up during the audit and stayed out.

A third type weight ramp

Bricolage + DM Sans is the system. Adding a mono display family "for code-forward dashboards" was floated and rejected — every font is a maintenance contract.

A separate "compact" density

Tiny Wire is already dense by default (13px base, 32px controls). A "compact" override would double the surface area of every state matrix. If 13px is too big, override the token at scope.

Hand-drawn iconography

Examples use Lucide-style strokes inline. The system doesn't ship icons — bundling a set means owning a set. Consumers pick their own.

A second brand colour

The temptation is real — every operational tool has a "secondary accent" moment. But every additional brand-mapped token doubles the override matrix for re-skinning. Override --brand at the consumer's scope instead.

What's next

The next release is in flight. The deferred findings get their fix, plus the additions below.

TargetTypeNotesStatus
ARIA documentation pass Docs Toast, spinner, skeleton, alert — one block per component on the components page. In progress
Segmented control New component A formal name for what .btn-group + .btn.active is doing. Comes with keyboard arrow nav. Drafting
Date / range picker New component The most-requested gap from the v1.0 feedback round. Drafting
Theme builder Tooling Live token editor in the docs site. Drop a brand colour, see every component re-skin. Backlog
Focus-trap RFC (TW-018) Spec The "does Tiny Wire ship JS?" decision. Drafting. Backlog

Every release ships with a page like this. The point isn't the changelog — every repo has one of those. The point is to make the reasoning visible: what changed, what didn't, and what I refused to add.