LifeLab · lens · aesthetics + workspace
Three aesthetic directions for lenses, all built on one move: stop encoding nesting depth and start encoding what a keystroke will do. Nested-darker was a nice trick that answers a question nobody asks — how deep am I? — while spending the loudest visual channel in the app, and it bottoms out at mud by depth three. The questions people actually ask a lens are where will this block land, what does × mean here, and where do my keys go.
Darker = heavier = more important. That's backwards: a nested lens is usually less important than the river it sits in. Recede by fading ink, not by stacking surfaces — the terminal move, not the card move. Paper stays one continuous sheet.
Depth is free from indentation already. A gutter rule plus 18px of indent tells you depth at any level. Spend colour and weight on contract instead: solid = owned here, dashed = a window onto elsewhere, dotted = a predicate's result.
If the view is a lens, the title is an expression. project/LifeLab + {todo :urgent:} is the page header. Editing the header re-frames the workspace in place — which is exactly the job tabs were doing badly.
Zoom replaces navigation. Press ⏎ on any lens header and that lens becomes the whole view; the frame you were in stays as a gutter breadcrumb, esc drops back. No history stack, no tab bar — one canvas, one focus.
What ×, drag and typing do. Never ambiguous.
gutter rule styleWhere the keys land. Exactly one lens is hot.
ink contrastThe block's canonical page. Survives every sort.
right-margin pathInteresting to the renderer, not the reader.
indent onlyproject /
+ Add to project/LifeLab — TODO and :urgent: applied
What changed: subpages and linked stopped being two fixed footer sections and became ordinary lenses in the river — you can fold them, filter them, reorder them, or delete them like anything else. Depth costs nothing but indent, so depth six still reads. The dim isn't decoration: only the full-contrast region takes your keystrokes, and hovering any dim lens brings it to 80% so you can read without committing. Gutter tells you the deal — solid you own, dashed is a window onto another page, dotted is a predicate.
project /
+ Add to LifeLab/frontend — TODO applied
+ Add to project/LifeLab…
The inversion: the page surface drops half a step (#f2f1e8) and every lens plate goes lighter (#fdfcf8) — so a transcluded page reads as light coming through a hole in the sheet rather than a card set on top of it. Depth alternates paper/light rather than accumulating, so it never muddies; past two levels only the gutter and label distinguish. Crop-mark corners say "this is a framing of something that exists elsewhere" without a heavy border. Focus is a 2px olive cap on the plate's left edge, not a change of fill.
review /
+ Add…
+ Summon a page or tag into this row…
Why a spine: once the fold, contract, and count live in a vertical rail instead of a horizontal header, a lens no longer needs full width — so render:cols can lay two or three lenses across the river and they still read as part of one document. This is the tab-killer in its most literal form: the things you'd have opened in tabs are all on screen, in the flow, at the position you put them, with drag-reparent working between them.
render:cols on a review page — layered on A, not a replacement for it.A flash lens: dashed, transient, never written to the file. Reading a linked page costs one keystroke and zero navigation.
![[…]]{todo}The dashes solidify into a referenced gutter. The transient becomes text — and text is the durable definition.
Zoom is not navigation: the lens didn't change, its frame did. The breadcrumb is the lens you were inside, one esc away.
drop targets — drag means three different things
"Make it match" is the gesture the computed contract has been missing: dropping onto a predicate's chips edits the block until it qualifies rather than moving it. Same drag, two outcomes, disambiguated by the pixel you release on.
why this removes the need for tabs
Ship A as the substrate. It's the only one that scales to arbitrary depth, it removes machinery rather than adding it, and it re-founds the model correctly: subpages and linked stop being special footers and become ordinary lenses you can fold, filter, and reorder. Borrow B's plate for one case only — a lens containing non-text content (code, table, image), where ink-fade can't establish a boundary; keep it lighter-not-darker so depth never muddies. Keep C as render:cols, an opt-in mode for review pages, not the default river.
The open question I'd want to test first: whether ink-fade reads as unfocused or as disabled. If the second, the fallback is fading only the chrome (labels, counts, gutters) and leaving block text at full contrast — quieter than dimming everything and still unmistakable about where the keys go.