GeezerVison is showing github.com — fix(hub-ui,json-render-ui): FloatingPopover escapes a transformed ancestor's containing block; native Select mode by antfubot · Pull Request #228 · devframes/devframe
Conversation
What changed
FloatingPopover (hub-ui) resolves the outermost ancestor that forms a containing block for position: fixed descendants and <Teleport>s its panel into that ancestor's parent. With no such ancestor, it renders in place as before.
Select (json-render-ui) gains native, rendering a real <select> instead of FormSelect/FormCombobox. It has no icon, description or searchable support.
SelectPropsSchema (json-render) gains the matching native field, so a spec can opt an element into it and the tsnapi API snapshot is updated.
- New Storybook story (
EscapesTransformedAncestor) demonstrating the escape fix against a transformed, overflow-clipping ancestor.
Why it changed
A position: fixed panel is positioned relative to its nearest ancestor carrying a non-none transform/filter/contain, not the viewport, and is clipped by that ancestor's overflow. Any of FloatingPopover's current consumers — the dock overflow panel, group popover, context menu, edge-position dropdown — would be mispositioned and clipped if rendered inside such an ancestor. Escaping the outermost rather than the nearest such ancestor matters because escaping one can land inside another. Walking parentElement (not parentNode) naturally stops at a shadow root's boundary, so the teleport target stays inside the dock's shadow root — where its stylesheet is scoped — rather than escaping to document.body and losing its styling.
native on Select is an escape hatch worth having regardless: the browser draws a native option list outside the page's layout, so no ancestor can clip or misposition it at all — the dependable choice for a Select embedded in a host layout this component doesn't control.
Porting notes
This ports vitejs/devtools#518 ("fix(core): FloatingPopover escapes a transformed ancestor's containing block; native Select mode" by @dvcolomban), per #205. That PR targeted packages/core/src/client/webcomponents/** in vitejs/devtools, which moved here as packages/hub-ui/src/client/components/floating/** and packages/json-render-ui/src/components/Select.ts in the v0.9 migration.
Adapted rather than ported verbatim:
- The original PR's
surface prop on FloatingPopover (tooltip | menu) and Select's dropdown switching to surface="menu" don't have an equivalent here — this fork's Select renders through @antfu/design's FormSelect/FormCombobox (reka-ui popovers), not through FloatingPopover, so there's no consumer to point a menu surface at. FloatingPopover's own five menu-style consumers (dock overflow, group popover, context menu, edge-position dropdown) are left as-is — restyling their padding is a separate, visually-verifiable change I didn't want to bundle into a positioning bug fix.
native's styling is written against this fork's design-system semantic classes (border-base, bg-base, color-base, …) per this repo's conventions, rather than the upstream fix's inline styles.
- Worth flagging:
@antfu/design's FormSelect/FormCombobox (reka-ui SelectPortal/ComboboxPortal) default to portalling into document.body, which suffers the same class of positioning/clipping bug inside a shadow root or transformed ancestor. Fixing that is out of scope here (it's an upstream dependency, not first-party code) but may be worth its own follow-up issue.
Credit to @dvcolomban for the original fix and rationale.
Closes #205
Prepared with the help of an agent.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
…estor's containing block; native Select mode
`FloatingPopover` (hub-ui) is positioned `fixed` against its anchor's viewport rect, but a
`transform`/`filter`/`contain` ancestor makes itself the containing block for that
`position: fixed`, so the panel ends up positioned relative to — and clipped by — that
ancestor instead of the viewport. `resolveFixedEscapeTarget` walks up from the anchor to the
outermost such ancestor (escaping only the nearest one can land inside another) and
`<Teleport>`s the panel to its parent; walking `parentElement` stops at a shadow root's
boundary, so a dock's popover never escapes the shadow root its stylesheet is scoped to. With
no such ancestor, the panel renders in place as before.
`Select` (json-render-ui) gains `native`, rendering a real `<select>` instead of
`FormSelect`/`FormCombobox`: the browser draws its option list outside the page's layout, so
no ancestor can clip or reposition it at all — a dependable escape hatch for a `Select`
embedded in a host layout this component doesn't control, at the cost of `icon`,
`description` and `searchable`, which have no native equivalent.
Ports vitejs/devtools#518, adapted to this fork: that PR's `surface` prop and `Select`'s
switch to it don't have an equivalent here — this fork's `Select` renders through
@antfu/design's `FormSelect`/`FormCombobox` (reka-ui popovers), not through
`FloatingPopover`, so there's no consumer to point at a menu surface. (Those reka-ui
popovers default to portalling into `document.body`, which suffers the same class of bug
inside a shadow root or a transformed ancestor — worth a follow-up, but out of scope for a
first-party fix here.)
Closes #205
Co-authored-by: dvcolomban <[email protected]>
netlify
Bot
commented
Aug 14, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Reviewers
antfu
antfu approved these changes
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|