Skip to content
Development

Six small UX fixes in one sitting

By Victor Da Luz
sveltetauriuxdev-loggreenhouse

After the navigation redesign, the backlog had a run of small, well-specified UI/UX fixes from the same review pass - the kind where the fix is already spelled out in the issue and there’s no design decision left to make. I picked six of them in size order and worked through the full cycle for each: branch, implement, verify against the real running app, merge, close. No stops between them - each one really was as contained as it looked going in.

The list

Ultra-wide layout caps: the dashboard’s zone grid had no max-width, so on a wide monitor everything stretched edge to edge. Capped it at 110rem, centered. Locale-aware touch timestamps: formatTouchTime was calling toLocaleString(), which follows the OS language, not the app’s own language setting - a Portuguese UI on an English Mac was showing English dates. Switched to Intl.DateTimeFormat(getLocale(), ...). Emoji to SVG: the brand mark and streak indicator were emoji, which render however the OS feels like rendering them and don’t take a color from your design tokens. Replaced both with inline stroke SVGs matching the rest of the icon set. Dismissible errors plus deferred loading: action-error banners had no way to dismiss them short of triggering another action, and every view entry showed a loading card even for fetches that finish in under 50ms. Added a close button to the banners, and gated each loading card behind a 150ms timer so fast fetches just update in place. First-run hero: a brand-new vault showed three passive “nothing here yet” lines and nothing else. Added a proper first-run state - one sentence plus a bigger Capture button - that only shows when the vault is genuinely empty (not just temporarily low).

And a copy pass with a real redundancy bug: seven small wording fixes (dialog titles that didn’t match their triggers, a button label with an unnecessary parenthetical, an ambiguous “6m” that read as six months next to “300d”). The interesting one: a vaulted or released item’s card was showing the same word three times - the pipeline stage name, a “Dormant 20d” chip, and a “Dormant” badge, all in one row. Fixed it so the badge only shows when there’s no duration to chip instead.

What made this batch actually go fast

Every one of these had a single, unambiguous acceptance criterion baked into the issue text - “use Intl.DateTimeFormat instead of toLocaleString,” “cap at max-width: 110rem.” Nothing needed a design call. That’s the difference between a fix that takes fifteen minutes and one that needs a conversation first - the badge redundancy was the closest thing to a judgment call in the batch, and even that resolved cleanly once I traced through exactly which combination of chip + badge was showing the same word twice.

The one thing I’d flag from a process standpoint: several of these touched shared components (ItemCard’s badge logic, the loading-state pattern duplicated across four view components) where a change to fix one thing has second-order effects on the tests that already existed. The first-run hero, for instance, added a second “Capture an idea” button that coexists with the topbar one - which broke six unrelated tests that queried that button by name assuming there’d only ever be one match. None of that showed up as design risk going in; it only surfaces once you actually run the suite and read what breaks.

Result

All six merged to main, CI green throughout, 166 frontend tests passing (up from 158 before this batch - each fix that introduced new behavior got a test proving that behavior, not just proving the old tests still pass).

Related reading

Development

Toast + undo for Greenhouse's vault action

The app's first transient-feedback surface, the aria-live rule that makes a conditionally-rendered toast silently unannounced, and a prop that goes stale the moment you rename.

Read
Development

A dashboard that stopped telling time

A $derived block that read Date.now() exactly once, a midnight that meant different things to the frontend and the Rust engine, and the grammatically broken sentence that proved it.

Read