Skip to content

fix: ResizeObserver based container sizing - #9892

Open
yuvvantalreja wants to merge 5 commits into
mainfrom
yuvvan/resizeobserver-container-sizing
Open

fix: ResizeObserver based container sizing#9892
yuvvantalreja wants to merge 5 commits into
mainfrom
yuvvan/resizeobserver-container-sizing

Conversation

@yuvvantalreja

@yuvvantalreja yuvvantalreja commented Jul 21, 2026

Copy link
Copy Markdown
Member

PR Description

Fixes #9349

Refs: vega/vega#4318

Checklist

  • This PR is atomic (i.e., it fixes one issue at a time).
  • The title is a concise semantic commit message (e.g. "fix: correctly handle undefined properties").
  • npm test runs successfully
  • For new features:
    • Has unit tests.
    • Has documentation under site/docs/ + examples.

Tips:

@yuvvantalreja
yuvvantalreja marked this pull request as ready for review July 21, 2026 17:01
@yuvvantalreja
yuvvantalreja requested a review from a team as a code owner July 21, 2026 17:01
@domoritz

Copy link
Copy Markdown
Member

Heads up — the Vega side of this is being reworked, and it changes what this PR should emit.

vega/vega#4318 is being rebuilt around a first-class container:resize event source, so a spec can subscribe to container changes the same way it subscribes to window:resize:

{"on": [{"events": "container:resize", "update": "containerSize()[0]"}]}

Once that lands, the {"signal": "autosize"} handler here should be replaced by container:resize. That handler is really a workaround for a Vega gap rather than a fallback: view.resize() sets _autosize and touches the autosize signal but never re-evaluates the containerSize()-derived width/height signals, so listening on autosize is a way to force the re-read. Two costs come with it — it fires on any autosize change, not just container changes, and it still needs someone to call view.resize() by hand, which is why the runtime test here does await view.resize().runAsync(). With the event source, the observer fires on its own and that test can assert a real container change instead.

Worth keeping window:resize alongside it for now, though — not on the merits, but for compatibility. A spec emitting only container:resize on an older Vega gets Can not resolve event source: container and then no resize behaviour at all, and since Vega-Lite output runs on whatever Vega the user has, that would be a silent regression. Same for the (now rare) case of no ResizeObserver. Keeping both means old runtimes degrade to today's behaviour. On the merits the two are redundant: if a window resize changes the container's box, ResizeObserver already fires; if it doesn't, the window:resize handler re-reads the same value and the signal short-circuits. So the clean end state is to drop window:resize in the same PR that raises the Vega peer-dependency floor.

One note on the docs change: "In browsers where Vega uses ResizeObserver, container changes are detected automatically" isn't true of anything in this PR yet — it describes #4318. It becomes accurate once that ships, so it's worth landing them in that order.

Thanks for pushing on this — #9349 has been open since 2024 and the container-sizing story is better for it.

view.resize() touches the autosize signal but never re-evaluates the
containerSize()-derived width/height signals, so the autosize handler was a
workaround that also required calling view.resize() by hand. Vega's container
event source (vega/vega#4318) reports the container's actual size changes, so
subscribe to that instead.

window:resize is kept for Vega versions that predate the container event
source, where an unrecognised event source is silently dropped.

Requires the Vega release that ships vega/vega#4318; the runtime test fails
until then.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

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

Copy link
Copy Markdown

Deploying vega-lite with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8e6287f
Status: ✅  Deploy successful!
Preview URL: https://435d5d0a.vega-lite.pages.dev
Branch Preview URL: https://yuvvan-resizeobserver-contai.vega-lite.pages.dev

View logs

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
domoritz added a commit to vega/vega that referenced this pull request Aug 13, 2026
Adds a `container` event source, so a spec can react to its container
changing size the same way it reacts to a window resize:

```json
{"name": "width", "update": "containerSize()[0]",
 "on": [{"events": "container:resize", "update": "containerSize()[0]"}]}
```

`containerSize()` was previously only re-read on `window:resize`, which
misses every layout-driven change — a sidebar opening, a flex reflow, a
notebook cell, a resizable panel. That gap is why `"width": "container"`
has been unreliable and why the documented workaround was to fire a
synthetic window resize by hand.

A `ResizeObserver` is attached only when a spec subscribes to the
source, so nothing is observed otherwise. Notifications coalesce to one
dispatch per animation frame, and the observer is suppressed while the
view is running its own resize.

Refs vega/vega-lite#9349

## Layout shift: canvas and SVG roots no longer sit on the text baseline

Both renderers now set `vertical-align: bottom` on the root element.
**This changes the height of every rendered chart by ~6px, whether or
not the spec uses the new event source.**

An inline element sits on the text baseline, so its container reserves
room for the descender below it. That phantom ~6px was measured
identically for canvas (both CSS-sized and attribute-sized) and for SVG.
It disappears with `vertical-align: bottom`, `top`, or `middle`, and
with `display: block`.

`vertical-align: bottom` was chosen over `display: block` because it
preserves inline flow — a small chart in running text stays on its line
rather than moving to its own.

Why it belongs with this feature rather than in a separate PR: without
it, an auto-height container with `"height": "container"` is
unsatisfiable. The container's height is the chart's height plus 6px,
the chart reads that back, and it grows by 6px per render forever. The
feature would ship broken by default for that case, caught only by the
loop guard.

What to expect:

- Every embedded chart becomes ~6px shorter. Usually desirable — it is
space nothing was drawing into.
- Downstream image-comparison baselines will shift. Vega's own goldens
are unaffected (scenegraph and SVG strings, not DOM layout — 693/693
unchanged), but vega-embed, Vega-Lite and Altair screenshot tests will
need regenerating.
- A chart placed inline in running text aligns to the bottom of the line
box rather than the baseline, moving ~3–4px relative to surrounding
text.
- It is an inline style, so a stylesheet overriding `vertical-align` on
`.marks` now needs `!important`.
- Exported SVG is unaffected: `svg()` strips the style attribute before
serializing. That also fixes a latent bug where the export path restored
only `background-color` after clearing the attribute.

## Loop guard

A container whose size is genuinely determined by the view inside it has
no fixed point. After several consecutive rounds where the size sits
exactly where the previous render left it — which nothing external can
produce — the view stops matching and warns once:

> Container size depends on the view it contains; no longer resizing to
match it.

The warning is at `Warn` level, so it is invisible at Vega's default log
level of `Error`.

## Vega-Lite

vega/vega-lite#9892 emits `container:resize` alongside `window:resize`.
Both are kept until Vega-Lite's Vega peer dependency reaches the release
containing this source: on older runtimes an unknown event source
resolves to an empty selector match and silently does nothing, so
dropping `window:resize` early would remove resize behaviour without
warning.

---------

Co-authored-by: Dominik Moritz <domoritz@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Adopt ResizeObserver for "container" width/height?

2 participants