sh0.dev has 180+ API endpoints, 10 Rust crates, Docker orchestration, backup engines, MCP server integration, and an AI chat system. The backlog is infinite. There is always another feature to build.
Today we spent an entire session making sidebars look consistent.
What Was Wrong
Nothing was broken. The dashboard worked. Every page did its job. But if you navigated from Settings to AI Chat to API Docs, you would see three different visual languages:
- Settings had a clean white card sidebar with
rounded-2xl, proper borders, and a shadow. - AI Chat had a dark
bg-dark-900sidebar withborder-white/5-- dark mode only, no light mode support. - API Docs had a plain
with no card styling at all. - Deploy had horizontal filter pills but no sidebar.
- Stacks had no filtering mechanism.
Each page was built in a different session, sometimes weeks apart. Each one solved its immediate problem. None of them looked at the others.
This is how visual debt accumulates. Not through bad decisions, but through isolated good ones.
The Pattern We Established
We defined one sidebar pattern and applied it everywhere it made sense:
bg-white dark:bg-dark-900 rounded-2xl border border-gray-200 dark:border-dark-700 shadow-sm p-3Active items:
``
bg-sh0-500/10 text-sh0-600 dark:text-sh0-400
``
Hover state:
``
hover:bg-gray-50 dark:hover:bg-dark-700
``
Seven properties. Applied to Settings, AI Chat, Deploy, and API Docs. The result: you can navigate the entire dashboard without your brain registering a context switch. The sidebar is always in the same place, always looks the same, always behaves the same.
What We Actually Changed
AI Chat -- The Biggest Overhaul
The AI chat page had the most issues. It was built dark-first with hardcoded French text, an oversized header eating vertical space, and a chat input with inline styles instead of Tailwind classes.
We:
- Removed the page header and chat header entirely -- the conversation list and chat input are the UI, not decorative headers
- Moved the wallet link to the global top bar (visible on every page)
- Restyled assistant bubbles from dark-only (bg-dark-700/50) to proper white cards with light/dark prose styles
- Replaced the hardcoded "L'IA peut se tromper" with an i18n key across 5 languages
- Gave the chat input a proper rounded-2xl border with focus ring
Deploy -- Category Sidebar
The deploy page had 237 items filtered by horizontal pills (All, Source Types, Frameworks, Databases, Apps). We moved these into a left sidebar card with sub-categories that appear when you select a main category. The content area gained the full width the pills were occupying.
Stacks -- Search and Status Filters
The stacks page didn't need a sidebar (and we deliberately chose not to add one -- that's a separate story). Instead, we added a search bar that filters by stack name, description, or app names, plus status filter pills: Running, Partial, Stopped, Building, Error. Each pill shows its count and only appears if stacks with that status exist.
Collapsible Layout
The final piece: a hamburger icon in the top header that collapses everything -- the main nav sidebar and all page-level context sidebars. One click, full-width content. The state persists in localStorage.
This matters especially for the AI chat, where you want maximum space for the conversation. But it also helps on smaller laptop screens where the 80px nav + 224px context sidebar eat a third of your viewport.
Why Not Just Ship Features?
Three reasons.
1. Inconsistency Erodes Trust
sh0 manages production deployments. When a user sees three different visual styles across three pages, they subconsciously wonder: "If the UI is inconsistent, is the deployment engine consistent?" The question is unfair -- the Rust backend is rock-solid regardless of how the sidebar looks. But perception is reality for a product that asks you to trust it with your infrastructure.
2. Polish Compounds
Every future page we build now inherits the established pattern. The monitoring page, the team management page, whatever comes next -- the developer (me) has a clear template to follow. The 2 hours we spent today save 30 minutes on every page built from now on. After 4 pages, it has paid for itself.
3. We Were About to Demo
sh0 is approaching the point where real users interact with it. A polished dashboard at demo time is not vanity -- it's the difference between "this looks like a product" and "this looks like a prototype." First impressions are formed in seconds, and they are formed by visual consistency, not feature count.
What We Deliberately Did Not Do
- No design system library. We don't need Storybook or a component library for a dashboard with 12 pages. The pattern is documented by example in the settings page.
- No CSS refactoring. We didn't extract common classes into
@applydirectives or create utility components. The Tailwind classes are explicit and readable inline. - No sidebar on every page. The stacks page doesn't have one because the stacks are the content. Consistency means applying the same solution to the same problem, not the same solution to every problem.
- No dark-mode-only. Every new style uses
bg-white dark:bg-dark-800pairs. The dark-only era of the dashboard is over.
The Numbers
One session. 16 files changed. 887 insertions, 462 deletions. 5 pages restyled. 1 new shared store. 5 translation files updated. 0 features removed. 0 regressions.
The dashboard looks like one product now, not five prototypes stitched together.
The Lesson
There is a specific moment in a product's life when polish becomes the highest-leverage work you can do. It's not at the start (when the architecture isn't settled), and it's not at the end (when users have already formed opinions). It's right before real users arrive.
We are at that moment. And spending a session making every sidebar match is not time wasted -- it's the last thing you do before you open the door.
---
This post documents the sh0.dev dashboard UI/UX sprint of March 25, 2026. All changes shipped in a single commit to zerosuite-inc/sh0-core.