Skip to content

v3.4.0 - Linear-like UI

Choose a tag to compare

@theyashgupta theyashgupta released this 01 Sep 20:10
e2bd146

You look at this board all day. It worked, but it never felt finished: padding a little loose here, a hover that did nothing there, a red label you could not quite read against a dark card. v3.4 is the milestone where every one of those judgments stopped being a vibe and became a measured number. The board and the detail panel now follow one written design contract, and every value in it was fixed against the real rendered app, at four breakpoints, before a single component changed.

See it

Dispatch v3.4.0 Linear-like UI

Higher-quality video: dispatch-v3.4.0-linear-ui.mp4

A denser board that earns it

Card padding, card gap, and column headers all tightened to contract values (6px vertical padding, 6px gap, 28px headers). On a mid-size window that buys back a full extra card row per column. Type sizes did not shrink. The density comes from spacing and line height, so nothing got harder to read. The candidates were tried live against a seeded 18-card board and the winner was measured, not guessed.

Every state is now deliberate

Hover, pressed, and keyboard focus have explicit values on every interactive element: cards, column headers, buttons, segment controls, resize handles. Along the way the milestone fixed real defects that had been invisible: the active view toggle swallowed its own hover, cards showed the browser's default focus ring instead of the app's, and an undocumented shadow appeared when you hovered a card. Pressed states did not exist anywhere before this release. Now one formula covers them all.

Motion with a budget

Cards now slide when they change columns (a 150ms FLIP transition, driven by real board events). Column counts pulse when they change. The detail panel opens in 200ms and closes in 150ms, with separate enter and exit easings. Everything animates through CSS only, on purpose: the prefers-reduced-motion setting kills all of it, and the test harness proved that with a positive control that the kill-switch cannot reach.

Red you can read

Destructive red used to pull double duty as both a fill and a text color, and as text it failed the contrast floor on several dark surfaces. The roles are now split. Text red is a lighter shade measured at 4.5:1 or better on every surface it renders on, including hover and pressed tiers. The danger button got a darker fill so its white label reads at 5.62:1. The contrast instrument now checks 75 token pairs on every npm run check, with zero failures and a permanently empty residual list.

Status colors are also locked down for good: a new repo invariant fails the build if any status-meaning color literal ever lands outside the token file.

Vault rows you can actually read

The Vault page in Settings had a row layout that truncated long key names to a few characters and hid the name entirely while editing. Rows now follow the standalone Envault tool's pattern: the full key name always renders and wraps, the purpose sits beneath it and is edited in place, and the value action is a plain text button that says "Set value" or "Rotate". The sealed-value guarantee is unchanged. There is still no way to read a stored secret back.

Audited, surface by surface

The milestone closed with a per-surface audit: 22 component surfaces and 57 contract keys, each with a recorded verdict from live measurement. It closed at zero open findings. The audit found and fixed real issues on the way, including two mono-size violations the previous milestone's census had missed.

Limits, stated plainly

The Settings dialog sidebar has no narrow-viewport layout. At 390px it pushes row controls off screen. This predates v3.4 and is on the backlog. The board's column resize handles do not render below 1024px, a pre-existing structural cut. The terminal viewport deliberately sits on the darkest background tier, recorded as an accepted exception, not an oversight.


Full changelog: v3.3.0...v3.4.0

Requires Node ≥ 22.22, tmux, ttyd, git, and the Claude Code CLI. Setup is in the README.