Skip to content

feat(react-headless-components-preview): hybrid usePositioning with lazy CSS Anchor / floating-ui#36124

Draft
dmytrokirpa wants to merge 2 commits into
microsoft:masterfrom
dmytrokirpa:feat/react-headless-components-preview/hybrid-positioning
Draft

feat(react-headless-components-preview): hybrid usePositioning with lazy CSS Anchor / floating-ui#36124
dmytrokirpa wants to merge 2 commits into
microsoft:masterfrom
dmytrokirpa:feat/react-headless-components-preview/hybrid-positioning

Conversation

@dmytrokirpa
Copy link
Copy Markdown
Contributor

@dmytrokirpa dmytrokirpa commented May 8, 2026

Summary

Replaces the anchor-only usePositioning in @fluentui/react-headless-components-preview with a hybrid implementation that picks a positioning strategy at runtime and lazy-loads its chunk:

  • CSS Anchor Positioning when the browser supports it — chunk: fluentui-anchor-positioning.
  • floating-ui otherwise — chunk: fluentui-floating-ui-positioning (+ transitive floating-ui-dom).

A browser only ever loads the chunk for the path it ends up using.

Architecture

  • usePositioning.ts — single hook, fully synchronous, always calls the same React hooks. All positioning work happens imperatively inside one useIsomorphicLayoutEffect.
  • applyAnchorPositioning.ts — pure imperative helper that writes anchor-name / position-anchor / position-area / position-try-fallbacks etc., plus a placement observer that mirrors the browser-resolved placement back into data-placement after flip fires.
  • applyFloatingUIPositioning.ts — pure imperative helper that uses @floating-ui/dom's computePosition + autoUpdate with offset / flip / shift / size / arrow middleware.
  • lazyApply.ts — dispatcher with import()-driven chunk loading, runtime caching, and cancellation-safe scheduling.

Notable details

  • First-paint flash avoided — the hook sets visibility: hidden on the container synchronously before scheduling apply; each helper clears it once its first set of coordinates lands.
  • Arrow support — hook now returns an arrowRef callback alongside targetRef / containerRef. Floating-UI uses the arrow middleware (mirrors the original react-positioning arrow wiring); the CSS Anchor branch positions the arrow as a CSS-anchored element via position-anchor + anchor(center) so it tracks the trigger center even after position-try fallbacks.
  • preloadPositioning() — public helper that warms whichever chunk this browser will end up using; safe to call during SSR.
  • usePlacementObserver.ts removed — its rect-detection logic is now a private helper inside applyAnchorPositioning.ts.
  • React Compiler — hook opts out via 'use no memo' (matches the react-positioning package's hook).

Public API surface

@fluentui/react-headless-components-preview/positioning now exports:

  • usePositioning(options): { targetRef, containerRef, arrowRef }
  • preloadPositioning(): Promise<unknown> (new)
  • existing POSITIONS, ALIGNMENTS, getPlacementString, resolvePositioningShorthand, types

dmytrokirpa and others added 2 commits May 8, 2026 17:00
…azy CSS Anchor / floating-ui

Replaces the anchor-only `usePositioning` in the headless preview package with a
hybrid implementation that picks a positioning strategy at runtime and lazy-loads
its chunk:

- CSS Anchor Positioning helper when the browser supports it (chunk:
  `fluentui-anchor-positioning`).
- floating-ui-based fallback otherwise (chunk:
  `fluentui-floating-ui-positioning` + transitive `floating-ui-dom`).

Both strategies live in their own modules (`applyAnchorPositioning.ts`,
`applyFloatingUIPositioning.ts`) and are dispatched via `lazyApply.ts`. The hook
itself stays fully synchronous, always calls the same React hooks, and runs
positioning imperatively inside a single layout effect.

The container is set to `visibility: hidden` while the chunk is loading (and,
on the floating-ui path, while `computePosition` is in flight) to avoid a
visual jump before the surface is anchored. The apply helpers clear it once
positioning is committed.

Other notable changes:

- `usePositioning` now returns `arrowRef` (callback ref) alongside `targetRef`
  / `containerRef`. Floating-UI uses the `arrow` middleware; the CSS Anchor
  branch positions the arrow as another anchored element via
  `position-anchor` + `anchor(center)`.
- New `preloadPositioning()` helper warms the chunk for whichever strategy
  this browser will end up using.
- `usePlacementObserver.ts` was deleted; its rect-detection logic moved into
  `applyAnchorPositioning.ts` as a private helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-headless-components-preview
react-headless-components-preview: entire library
107.577 kB
31.646 kB
107.567 kB
32.01 kB
-10 B
364 B

🤖 This report was generated against 7b586d76279e090d7b459bc4c066bd9804e4f2aa

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Pull request demo site: URL

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.

1 participant