Skip to content

fix(layout): scroll the palette on overflow instead of stretching the host page - #188

Merged
StephenTangCook merged 3 commits into
mainfrom
claude/block-palette-scroll-overflow-phfe86
Aug 15, 2026
Merged

fix(layout): scroll the palette on overflow instead of stretching the host page#188
StephenTangCook merged 3 commits into
mainfrom
claude/block-palette-scroll-overflow-phfe86

Conversation

@StephenTangCook

@StephenTangCook StephenTangCook commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

The block palette now scrolls inside its own rail, independently of the preview pane, instead of growing the builder — and the host page — to fit its full block list. Separately, the toolbar's Clear and View JSON buttons rest as bare icons and slide their label open on hover or keyboard focus.

Why

The palette stretched the page. The builder shell sizes itself with h-full. That resolves against a host that gives its container a definite height (the demo, Storybook) and computes to auto against one that doesn't — a plain <div> in ordinary document flow. In the auto case every internal overflow-y-auto goes inert: nothing is bounded, so nothing has anything to scroll, and the shell grows to its tallest child. That child is the palette. Measured in an unbounded host at an 800px viewport, the page came out 2607px tall with the rail at 2374px and asideScrolls: false — the reported "extends the page a huge amount." After the fix the same page is 982px with the rail bounded to 749px and scrolling its 2374px of content, and scrolling the rail leaves window.scrollY at 0.

The cap is max-h-[var(--bk-max-height,100svh)] on the shell. It bounds the unbounded case and stays inert whenever the host's own height is the smaller of the two, so nothing changes for hosts that already size the builder. --bk-max-height on any ancestor tunes it (calc(100svh - 4rem) to leave room for a page header) or removes it (none to keep the old grow-to-content behavior). Documented under a new Sizing section in the README.

A scrolling palette exposed a sticky-header bug. The search header was painted with the rail's own bg-muted/20, so it composited over whatever had scrolled beneath it — section headings read straight through the search box, with backdrop-blur softening them rather than hiding them. This already shipped for bounded hosts; it would have looked like a regression introduced here. The header is now an opaque bg-background base with the tint laid back on top — the same two layers the rail itself is made of, so the match stays exact under any theme rather than only where --muted sits near --background.

The toolbar's secondary cluster. Clear and View JSON each carried a label hidden below md and always shown above it, putting two full-width word buttons next to the primary action. They now rest as icons (40px vs 113px) and reveal the label on hover or keyboard focus. The reveal is a 0fr1fr grid track — a lone flexible column in an intrinsically-sized grid resolves to its item's max-content width, and the flex factor interpolates, which width: auto can't. Reduced-motion users get the same end states with no transition, and the aria-label on each button is unchanged and was always the accessible name, so nothing shifts for assistive tech.

Test plan

  • pnpm typecheck
  • pnpm lint
  • pnpm test — 498 passing across both projects (unit + storybook)

Two new real-browser test files measure the built, @scope-wrapped stylesheet, since jsdom implements neither layout nor svh, and Storybook renders against plain unscoped Tailwind:

  • test/builder-shell-scroll.test.tsx — the rail bounds and scrolls in an unbounded host; the cap stays inert under a host-supplied height; --bk-max-height both tightens and disables it; the sticky header paints at full alpha.
  • test/toolbar-expanding-labels.test.tsx — driven with a real mouse move and a real Tab press, because userEvent's synthetic pointer events never set :hover. Covers the collapsed resting width, hover expansion (neighbour unaffected), keyboard-focus expansion, and the unchanged accessible names.

Each new assertion was confirmed to fail against the pre-fix code (expected 'none' to be '800px', expected 51 to be 255, expected 113.14 to be less than 48).

The Chromium launch and stylesheet build these share with load-dialog-cascade.test.tsx moved to test/built-stylesheet.ts; that test is otherwise untouched and still passes.

Manual verification: screenshotted both host shapes in Chromium at 1280×800 — unbounded and bounded (definite-height ancestor, the demo's shape) — plus the sticky header parked mid-scroll before and after, and the toolbar at rest and hovered. Also confirmed the three new utilities (max-h-[var(--bk-max-height,100svh)], grid-cols-[0fr], group-hover:grid-cols-[1fr]) survive scripts/scope-utilities.mjs correctly anchored.

Notes for reviewer

The cap is a default, not a hard ceiling, but it is a behavior change for any host currently relying on the builder growing past the viewport in document flow — --bk-max-height: none restores that exactly.


Generated by Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

claude added 2 commits August 15, 2026 18:16
The builder shell sizes itself with `h-full`, which resolves against a
host that gives its container a definite height and computes to `auto`
against one that doesn't. In the `auto` case every internal
`overflow-y-auto` goes inert — nothing is bounded, so nothing has
anything to scroll — and the shell grows to its tallest child. That
child is the palette, whose full variant list runs a couple of thousand
pixels, so an embed in ordinary document flow stretched the host page by
that much instead of scrolling the rail independently of the preview.

Cap the shell at `max-h-[var(--bk-max-height,100svh)]`. It bounds the
unbounded case and stays inert whenever the host's own height is the
smaller of the two; `--bk-max-height` tunes or removes it (`calc(100svh -
4rem)` to leave room for a page header, `none` to opt out).

A scrolling palette also exposed a sticky-header bug: the search header
was painted with the rail's own `bg-muted/20`, so it composited over
whatever had scrolled beneath it and section headings read straight
through the search box. `backdrop-blur` was softening them, not hiding
them. Split the header into an opaque `bg-background` base plus the tint
— the same two layers the rail itself is made of, so the match stays
exact under any theme.

Covered by a real-browser regression test against the built stylesheet,
since jsdom implements neither layout nor `svh`. Its Chromium launch and
stylesheet build are shared with the load-dialog cascade test.
…over

Both buttons carried a label that was hidden below `md` and always shown
above it, which put two full-width word buttons in the toolbar's
secondary cluster. Rest them as bare icons instead and slide the label
open on hover or keyboard focus, so the cluster stays compact without
the icons becoming a guessing game.

The reveal is a `0fr` → `1fr` grid track: a lone flexible column in an
intrinsically-sized grid resolves to its item's max-content width, and
the flex factor interpolates, which `width: auto` can't. Reduced-motion
users get the same end states with no transition. The `aria-label` on
each button is unchanged and was always the accessible name, so nothing
shifts for assistive tech.

Measured in a real browser rather than a play function: the effect is
pure CSS, and the Storybook runner's `userEvent` dispatches synthetic
pointer events that never set `:hover`.
@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

✅ Deployed d8f8cc5 · Branch preview · Commit preview · Build logs

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
block-kitchen d8f8cc5 Commit Preview URL

Branch Preview URL
Aug 15 2026, 06:26 PM

Same content, roughly 40% fewer lines — the explanations had grown into
paragraphs that outweighed the code they sat on.
@StephenTangCook
StephenTangCook merged commit 48403e3 into main Aug 15, 2026
14 checks passed
@StephenTangCook
StephenTangCook deleted the claude/block-palette-scroll-overflow-phfe86 branch August 15, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants