Simplify the app chrome: one job per corner, and glass that follows the theme - #152
Merged
Conversation
app/.prettierignore excludes this file (auto-generated by openapi-typescript, prettier --write from the repo root skipped that ignore file since it isn't at the repo root). Regenerate to restore raw openapi-typescript output.
Fixes the footer's narrow-viewport grid (right cluster was drifting into a dead third track once the center credit line hid), corrects stale comments left behind by the gem/reset-key merge and an earlier chip max-width fix, scopes .btn-chip-gem's sizing so it actually wins over .gem-icon's specificity-tied 1em default, and consolidates the three hardcoded copies of the repo URL into one REPO_URL constant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
At the prose color they were indistinguishable from the text around them. --cc-text-primary puts them two steps above that prose; hover keeps --cc-text-strong plus the underline. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
.btn-chip .gem-icon is (0,2,0) against .gem-icon's (0,1,0), so it wins on specificity outright, not on import order at a tie. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The chip's flex gap spaces every child equally, so the gem read as a glyph inside the name. Only this gap widens; the name, branch pill, and affordance stay a tight cluster. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Below 900px the hero stacks above the cards, and the styles written for the side-by-side composition mislead in that arrangement: - The action column kept a viewport-height cap and align-self: center, so the cards sat a screen below the hero with the recents card stretching to absorb the slack. - Grid's default align-content: stretch inflated the auto rows to fill min-height, opening a screen-tall gap between hero and cards. It now centers only while the content fits: `safe` keeps a full recents list from clipping the hero above the scroll origin. - Hero text sat at the page gutter while card titles sat a further 16px in, so two vertically adjacent blocks disagreed on their left edge. The hero now carries the card's own padding. - The close button floated in a band the brand row also occupied. The top inset now clears it. - The flat 90% veil rendered the city as dead black. The scrim runs down the page instead, thinning toward a horizon at the bottom. The page is fixed to the viewport, so the insets clear safe areas. Recents is capped at MAX_RECENT_SOURCES, which bounds the page length without a scroller nested inside the card. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scrubber and the mode toggle each hand-rolled a frosted fill, and they disagreed: 78% against 82% of the same colour. The timeline's is the reference, so 78% and blur(12px) become tokens, and .surface-glass joins the surface roles as the fill for a panel with the canvas behind it. Now shared by the scrubber, the mode toggle, the Shortcuts, Debug and Loading cards, and the landing cards. The timeline's stale-scrub toast keeps its semantic error fill and takes only the blur radius. Chrome laid out in the app body (header, footer, activity bar, sidebars, editor pane) stays opaque: a blur there has nothing to sample. The modal header drops its separate chrome fill so each card reads as one panel rather than an opaque band over glass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A media query carries no extra specificity, so the breakpoint block sat above the base rules and lost to them: the action column kept its viewport cap and centering, and the recents card kept stretching, which left the very nested-scroll trap the breakpoint exists to remove. It moves to the end of the file. Also drops the safe-area terms: index.html sets no viewport-fit=cover, so every inset resolved to 0 and the page does not span the status bar. Adds an unprefixed align-content ahead of the `safe` variant for engines that drop the whole declaration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Per-node stats sat in the footer, across the screen from the pane describing the same node, and their content-sized track was what the centered credit line had to compete with. They move into the selection pane as <PaneStats>, passed through Pane's existing footerSlot, so the file and road panes each gain a fixed stats row under their scrolling body. That frees the footer to split by meaning rather than by space: header project switcher, copy, origin ......... about · shortcuts footer status · version · debug ....... made by 🦄 thalida. About and the shortcuts button are meta any user might want, so they join the header opposite the project controls. Version answers the same question as the status dot, so it sits beside it, with the debug button (developer-only, and conditional on debug mode, which would otherwise resize the header cluster depending on how the app was loaded). The credit keeps the quiet corner, without the // that made it a comment. The footer returns to two tracks, so the 581-900px band no longer ellipsizes anything early. One behaviour change: the footer stats followed hover, and the sidebar opens only on selection, so hovering a building no longer previews its stats. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It was one flat line of path + a stat, joined by dots, with no width cap. A deep path made an arbitrarily wide card, and the only clamp was a flip to the other side of the cursor, which cannot help a card too wide to fit on either side: it pushed it off the opposite edge instead. Now: the name in strong text, the containing folder beneath it, and the stats last. The path drops whole middle segments to fit a budget, since truncating the tail would hide the end nearest the file. A max-width and per-line ellipsis back that up, because the budget counts characters and only approximates rendered width, and the placement clamps to the viewport after flipping. Stats come from the same builders the selection pane uses, so hover and pane cannot report different numbers, and hovering recovers some of the preview lost when the stats moved out of the app footer. Files pass dates: false to stay to one line. Commits lead with their subject, which is the most identifying thing about them and was previously unused. fileStatItems learned media dimensions, which the old tooltip special cased and the pane got wrong: a PNG reported 0 lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tooltip rode on --cc-bg-tooltip: translucent at 94% with no blur, so the city showed through raw and muddied the smallest text in the app. It floats on the canvas like the panels do, so it takes .surface-glass, and the token it was the only consumer of goes away. A directory stat row now leads with its kind. A folder named `app` and a file named `app` look alike, and the counts that follow only mean something once you know which one you are reading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tooltip's border was oklch(0.32 0.058 274.6): three times the surface's chroma and hue-shifted bluer. Absolute colours were fine when the panels were opaque, but a glass fill picks up whatever is behind it while the border holds one fixed hue, so the edge read cool against a panel that no longer was. --cc-glass-border is alpha, like the fill, so the edge shifts with the surface. The modal and landing cards had the same mismatch through --cc-border-subtle, less visibly because its chroma is lower. Declared per consumer rather than on .surface-glass, so the borderless glass (scrubber, mode toggle) stays borderless and no cascade order decides the colour. --cc-border-tooltip stays for the InfoPane blockquote that still uses it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
themes.css swaps --cc-bg-app/-chrome/-sidebar across four presets but overrode no border token, so picking warm or green gave you a warm panel ringed by a cool blue border, and every divider, input outline and slider track in the app stayed at hue ~276 regardless. Borders and tracks now derive from --cc-bg-sidebar with relative color syntax: lightness as an offset, chroma as a multiplier, hue straight through. Both constants are measured off the default surface, so the cool theme is unchanged to the eye while the others carry their own cast. A preset only has to swap the surface; no border restates anything. The multiplier matters: today's borders are more saturated than the surface they sit on, so passing chroma through unchanged would have washed out the default theme. Behind @supports, since relative color syntax has no per-declaration fallback. A custom property holding a value the engine cannot parse fails at substitution rather than at parse time, which would drop every border to currentColor instead of to the absolute value above it. This supersedes the alpha glass border: it was neutral, and neutral was the one thing that would not follow the surface. The existing hierarchy, a brighter edge on the tooltip than on the panels, now tints along with everything else. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Started as "simplify the header, move the project switcher left, make the gem open the switcher, put version/about/credit in the footer" and grew from there as each change surfaced the next thing.
Header and footer
The gem and the project chip would both have opened the switcher, so they merged into one control: the gem is now the chip's leading glyph.
ResetViewButtonis deleted;R/Homestill reset the camera, as does the gem in the city.Per-node file stats moved out of the footer and into the selection pane, where they sit beside the node they describe. That freed both bars to split by meaning:
aboutand the shortcuts button are meta any user might want, so they took the header's empty right side. Version answers the same question as the status dot, so it sits beside it, along with the debug button (developer-only, and conditional, which would otherwise resize the header cluster depending on how the app was loaded).The version is real:
ConfigResponsegained aversionfield offapi.__version__, so a released image reports its own tag instead of a bundled constant.Selection panes
FilePreviewPaneandStreetPanegained a fixed stats row under their scrolling body, viaPane's existingfooterSlot. The stat builders are shared with the hover tooltip, so hover and pane cannot report different numbers.Hover tooltip
Was one flat line of path + a stat with no width cap, and its only clamp was a flip to the other side of the cursor, which cannot help a card too wide to fit either way. Now three lines (name, folder, stats), a path that drops middle segments rather than its tail, a max-width, and a clamp that actually clamps.
A real bug fell out:
fileStatItemsreported "0 lines" for images. It now reports pixel dimensions, which fixes the pane too.Glass
TimeTravelBarandSceneModeToggleeach hand-rolled a frosted fill and disagreed (78% vs 82% of the same colour). One--cc-glass-fill/--cc-glass-blurpair and a.surface-glassrole now serve the scrubber, the mode toggle, the Shortcuts / Debug / Loading cards, the landing cards, and the tooltip. Chrome laid out in the app body stays opaque, since a blur there has nothing to sample.Borders follow the theme
themes.cssswaps the three surface tokens across four presets but overrode no border token, so picking warm or green gave you a warm panel ringed by a cool blue border, app-wide. Borders and tracks now derive from the surface with relative colour syntax: lightness as an offset, chroma as a multiplier, hue straight through. Both constants are measured off the default surface, so cool is unchanged to the eye. Behind@supports, because a custom property holding an unparseable value fails at substitution rather than parse time, which would drop every border tocurrentColor.Landing page on small screens
Below 900px the hero stacks above the cards, and the styles written for the side-by-side composition misled there: a viewport-height cap and centering stranded the cards a screen down, grid's default
align-content: stretchopened a screen-tall gap, hero text and card titles disagreed on their left edge, and the close button shared a band with the brand row.Behaviour changes worth a look
hov ?? sel, and the sidebar opens only on selection. The rebuilt tooltip covers most of what it did..modal-backdrophas its ownblur(4px), making it a backdrop root, so the card's blur samples the flat dim rather than the city. Landing cards and the tooltip are genuinely glass. Fixing it means dropping the backdrop's blur.Verification
just test(370 pytest, 2892 vitest) andjust lintgreen; the full pre-push gate passed. Two rounds of code review were applied, one of which caught a Critical: the small-screen overrides sat above the rules they override, so half that fix silently never applied.jsdom computes neither grid layout nor
backdrop-filter, so the layout and glass work is not unit-testable and was checked in a real browser.🤖 Generated with Claude Code