Fixing a CSS grid panel that silently hid behind another panel
A visual review of Greenhouse’s project detail screen (the per-project view where you advance a stage, log a touch, or vault a project) flagged three things at once: an unbalanced layout, a ragged button stack, and a touch history that looked broken. None of them were separate bugs. Two came from the same root cause.
The problem
The detail view is a two-column layout: a narrow left column for the stage and its actions, a wide right column meant to hold a Files panel and a Touch history panel stacked on top of each other. Except the Touch history panel wasn’t showing up in the right column at all. It was rendering at the bottom of the left column, underneath the actions panel, while the right column sat there with just Files and a few hundred pixels of empty background below it. From a screenshot it read like a rendering bug. It wasn’t - it was CSS doing exactly what it was told.
Why this happened
The markup had three flat sibling elements inside a display: grid; grid-template-columns: <narrow> <wide> container: the actions panel, the Files panel, and the Touch history panel. With no explicit column assignment, CSS grid auto-placement fills cells row by row: child 1 goes to column 1 row 1, child 2 goes to column 2 row 1, and child 3 wraps to the next open cell - which is column 1, row 2. Under the actions panel. Not next to Files where it visually needed to be.
This is a totally standard CSS grid gotcha and it’s easy to miss because nothing about the individual rules looks wrong. You have to count the children against the column count to catch it, and that’s not something a linter or type checker flags.
The fix
Wrap the two panels that should share a column into one container:
.side-col {
display: flex;
flex-direction: column;
gap: var(--space-8);
}
Now the grid only ever sees two items - the actions panel and the wrapped side column - so auto-placement can’t misfire regardless of how many panels end up inside that wrapper later.
The other two findings
The action buttons (Advance, Touch, Vault, Show in Finder) were different intrinsic widths, which read as visually unplanned. Made them all stretch to a consistent width, and added a divider before the Vault button specifically, since vaulting is the one “exit” action in the group and it was sitting shoulder-to-shoulder with “I worked on this” - not a place you want a mis-click.
Separately, a touch history where nobody left a handoff note rendered as five identical bare timestamps in a row. Also read like a bug. Added a quiet “Touched - no note” label for that case instead of leaving it blank.
Verifying it
None of these three findings are something a test suite can confirm - they’re about how the screen actually looks. Greenhouse has a WebDriver automation setup for exactly this: build the app with a debug-only automation feature flag, point it at a throwaway SQLite vault seeded with a project that has both noted and note-less touches, and drive a real click into the detail view to screenshot it. Seeding meant writing directly into the vault’s SQLite schema rather than clicking through the app’s own timers, since idea maturity gates are measured in days and a live browser session can’t fast-forward a clock.
The screenshot confirmed all three: Files and Touch history now stack together in the right column, the action buttons line up, and note-less touches read as an intentional label instead of a rendering glitch.
Related reading
When the important text is the one that disappears
A kanban card with a stage tag, a neglect chip, an Available badge - and no name. Flexbox doesn't know 'shrink gracefully' and 'shrink to nothing' are different outcomes.
Six small UI items, and the two near-misses hiding inside them
A CSS block that grep said was dead but a test depended on, and a single line of localStorage that broke thirty-nine unrelated tests because of a Node upgrade.
A focus ring that shouldn't have been full-strength
A bright green line under the topbar on every launch, a screenshot attempt that captured the wrong windows entirely, and a swatch that proved the color math but not the answer.