← all components

Shell & navigation

The frame every view sits inside: the ambient depth of the Well, the left rail that never scrolls away, and the two overlays that take the screen. If Scry has a signature, it is here — the whole application is painted onto slowly drifting water.

00

The ambient field

Two fixed layers sit behind everything and are never interactive. They are the reason Scry reads as depth rather than as a dark theme, and they are the first thing to check when someone says the app "feels" different after a change.

🌊
DepthField
A full-viewport canvas of slow motes drifting upward through three soft fog bands, with a rare shimmer. Reads the theme (dimmer, sediment-coloured motes on light) and skips its animation frame entirely under reduced motion rather than merely shortening it. Any view can pulse it by dispatching scry:descend, which is how the Ask descent makes the whole app breathe at once.
ui/src/components/DepthField.tsx
Dark · at rest
🕯️
Vignette
A fixed, pointer-transparent wash: accent light spilling in from above the frame, deep water closing below it. It has a hand-tuned light-theme variant rather than an opacity flip, because the dark version goes muddy on paper.
ui/src/styles/global.css · .vignette
Dark
Light
01

The rail

68px wide, blurred, and the only piece of chrome present on every surface. It carries the mark, seven view links plus Settings, and the running version — which is a deliberate choice, not decoration: pondo semver treats the number as an auditable count, so it belongs where it can be read at any moment.

Rail mark
The Well glyph in a 34px round, wrapped in a radial accent glow that breathes on a 6.5-second cycle. The pulse is the app's only permanently-running animation, and it is what makes an idle Scry window still feel alive.
ui/src/styles/global.css · .rail-mark
Resting
#
Version stamp
9px mono at the foot of the rail, injected at build time from package.json as __APP_VERSION__. Its title attribute reads "pondo semver — every change counted"; it turns accent on hover, which is the only affordance saying it means something.
ui/src/components/AppShell.tsx · vite.config.ts
Current
2.36.20
Hovered
2.36.20
02

Overlays that own the screen

Two layers can take the whole viewport, and while either is up the shell's navigation hotkeys are suppressed — a guard added deliberately so nothing navigates out from under a half-typed capture. Escape closes the topmost one first.

Command palette
Ctrl/⌘+P from anywhere, including inside a text field. Subsequence fuzzy matching, Obsidian-style: every query character must appear in order, with consecutive hits and word-start hits scoring higher, then a small length penalty. Results are grouped and capped at 40. Covers every navigation target plus capture, theme, motion, font size, seven accent presets, graph reset and settings reset.
ui/src/components/CommandPalette.tsx
Matching
esc
No match
esc
  • nothing in the Well matches that
Palette row
Group label in mono on the left, command label, an optional hotkey badge, then a trailing arrow. Rows arm on mouse-enter as well as on arrow keys, so pointer and keyboard share one selection cursor rather than fighting over two.
ui/src/components/CommandPalette.tsx · ui/src/styles/components/palette.css
Default
Active
03

Page furniture

Six views share one header shape, so a user who learns to read one page can read all of them. Eyebrow states which table or system the view is a window onto; the title states what the page is; the subtitle explains the model behind it in one paragraph.

Card surface
The one raised surface in the system: a vertical gradient from --card-top to --card-bottom, a soft hairline, 16px radius, and a wide low shadow. It transitions its background over 460ms, so flipping the theme reads as a tide rather than a strobe.
ui/src/styles/global.css · .card
Card
Every atom traces back

Consent state and classification are properties of the source, so they survive every downstream synthesis.

Card + rule
Coverage
embedding 0.0% · facets 100%
View-enter transition
Every route change remounts the scroll container under a fresh key, so the incoming view rises 10px into place over 460ms on an --ease-out curve. It is the same rise keyframe the brand lockup, the suggestion chips and the brief all reuse, which is why navigation and answering feel like one motion vocabulary.
ui/src/components/AppShell.tsx · @keyframes rise
Rise · replayed on load
Stats · mimir stats

Coverage, freshness, and how deep it goes