Bottom-edge height pill on tiles; fullscreen fit runs every column to the bottom

Tiles get a third resize handle: a pill on the bottom edge (hover-only,
between the two corner handles) that moves that edge. With nothing directly
below, it changes just the tile's height. When other tiles share the edge,
the drag trades rows with them so the column keeps its total height — the
vertical counterpart of the seam pill. Clamped to every tile's minimum
height; blocked by collisions like the corner handles.

Fullscreen auto-fit reworked so columns actually reach the bottom of the
screen. The old rule only stretched tiles whose bottom edge was within two
rows of the viewport bottom, which on a display taller than the browser
window the layout was designed in left a blank band below every column.
Now the bottom-most tile of each column stretches to the bottom regardless
of distance. Short tiles (h <= 2, the clock) are never stretched into a
ribbon: under another tile they drop to the bottom edge and the tile above
grows into the gap (stopping at anything else below it); alone in their
columns they stay put. The fit re-runs from a ResizeObserver on the grid
area, so the browser's animated fullscreen transition and the menubar
collapse both settle into a correct fit (replaces the resize/transitionend
listeners).

README documents the pill and the fullscreen behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNYUv9EyrGSFmCnmH7WYKH
This commit is contained in:
Claude
2026-09-08 21:20:52 +00:00
parent 1292ba33ba
commit 66f45609f0
3 changed files with 198 additions and 35 deletions
+34
View File
@@ -411,6 +411,39 @@ body.menu-hidden #topbar {
border-radius: 0 0 0 3px;
}
/* bottom-edge pill: drag to move the tile's bottom edge (height only);
sits between the two corner handles */
.tile-resize-bottom {
position: absolute;
left: 24px;
right: 24px;
bottom: 0;
height: 10px;
z-index: 5;
display: flex;
align-items: flex-end;
justify-content: center;
cursor: ns-resize;
touch-action: none;
opacity: 0;
transition: opacity 0.15s ease;
}
.tile-resize-bottom::after {
content: "";
width: 44px;
max-width: 60%;
height: 5px;
margin-bottom: 2px;
border-radius: 999px;
background: var(--muted);
transition: background 0.15s ease;
}
.tile:hover .tile-resize-bottom { opacity: 0.6; }
.tile .tile-resize-bottom:hover,
.tile.resizing .tile-resize-bottom { opacity: 1; }
.tile .tile-resize-bottom:hover::after,
.tile.resizing .tile-resize-bottom::after { background: var(--text); }
/* ── seam divider between adjacent tiles ───────────────────────────── */
/* When two tiles share a vertical edge, an invisible strip sits on the seam;
@@ -452,6 +485,7 @@ body.menu-hidden #topbar {
editing affordances (corner handles, seam pills) are parked. */
body.fullscreen .tile-resize,
body.fullscreen .tile-resize-left,
body.fullscreen .tile-resize-bottom,
body.fullscreen .tile-divider { display: none; }
body.fullscreen #grid { padding-bottom: 0; }
#gridwrap.no-scroll { overflow-y: hidden; }