Skip to content

Version Packages (next) - #2515

Merged
stipsan merged 1 commit into
nextfrom
changeset-release/next
Jul 30, 2026
Merged

Version Packages (next)#2515
stipsan merged 1 commit into
nextfrom
changeset-release/next

Conversation

@squiggler-app

@squiggler-app squiggler-app Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

@sanity/[email protected]

Major Changes

  • #2505 b8c74ba Thanks @stipsan! - Require React 19.2+, compile with the React 19 compiler target, and drop react-compiler-runtime.

    With babel-plugin-react-compiler targeting '19', the compiler uses React’s built-in runtime instead of the separate react-compiler-runtime package. Peer dependency ranges are tightened to ^19.2 (and react-dom for @sanity/ui).

Patch Changes

@sanity/[email protected]

Major Changes

  • #2505 b8c74ba Thanks @stipsan! - Require React 19.2+, compile with the React 19 compiler target, and drop react-compiler-runtime.

    With babel-plugin-react-compiler targeting '19', the compiler uses React’s built-in runtime instead of the separate react-compiler-runtime package. Peer dependency ranges are tightened to ^19.2 (and react-dom for @sanity/ui).

  • #2505 8b3366d Thanks @stipsan! - Remove the private @sanity/ui/_visual-editing entrypoint.

    Import from @sanity/ui instead. Tree-shaking no longer needs this slim
    subset now that displayName side effects are gone and unused components
    are dropped from consuming bundles.


Note

Medium Risk
Major semver bumps and tightened React peers affect all consumers; removing _visual-editing breaks any app still on that import path.

Overview
Automated next prerelease that publishes @sanity/[email protected] and @sanity/[email protected], with changelog entries for already-merged work and .changeset/pre.json updated to include the react-19-compiler-target and remove-visual-editing-export changesets.

@sanity/ui documents two major breaking changes: peers move to React 19.2+ with the React Compiler '19' target (dropping react-compiler-runtime), and the private @sanity/ui/_visual-editing export is removed—consumers should import from @sanity/ui instead.

@sanity/themer bumps to 1.0.0-next.2 with the same React 19.2 / compiler story and a dependency bump to @sanity/[email protected].

Reviewed by Cursor Bugbot for commit e6bbd51. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
icons Ready Ready Preview Jul 30, 2026 12:18pm
sanity-ui-storybook Ready Ready Preview Jul 30, 2026 12:18pm
ui-docs Ready Ready Preview Jul 30, 2026 12:18pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
sanity-ui-workshop Ignored Ignored Jul 30, 2026 12:18pm

Request Review

@squiggler-app
squiggler-app Bot force-pushed the changeset-release/next branch from 267da28 to e6bbd51 Compare July 30, 2026 12:17
@stipsan
stipsan merged commit b3076f7 into next Jul 30, 2026
15 checks passed
@stipsan
stipsan deleted the changeset-release/next branch July 30, 2026 12:20
cursor Bot pushed a commit that referenced this pull request Jul 31, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
stipsan added a commit that referenced this pull request Jul 31, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 2, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 2, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 5, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 6, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 6, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
cursor Bot pushed a commit that referenced this pull request Aug 7, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
stipsan added a commit that referenced this pull request Aug 10, 2026
* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix(ui): ship dist/styles.css unminified for diffable CSS output (#2528)

* fix(ui): preserve overflow: hidden fallback in dist/styles.css

lightningcss's OverflowHandler collapses duplicate overflow declarations
to the last value no matter which targets it is given (unlike colors, it
never consults compat data for the clip keyword), so no browser target
setting can keep the hand-written fallback. Skip the lightningcss pass
instead (minify: false + target: false) so the stylesheet ships exactly
as authored, keeping overflow: hidden ahead of overflow: clip for
browsers in the support matrix without clip support (e.g. iOS Safari 15).

Co-authored-by: Cody Olsen <[email protected]>

* chore: format spinner.tsx (pre-existing oxfmt drift)

Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): keep lightningcss lowering, only disable minification of styles.css

overflow: clip is Baseline, so the hidden fallback doesn't need to
survive the build. Keep minify: false so CSS output diffs easily between
published versions, and drop target: false so the lightningcss pass runs
again with the @sanity/browserslist-config lowering targets.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component (#2525)

* chore: enter changesets prerelease mode on next

Enter the `next` prerelease tag, point Changesets and CI/release
workflows at the `next` branch, and add a sample major changeset for
@sanity/ui so the Version Packages bot can open the first pre-release.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2504)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat(ui): tombstone deprecated props, hooks, and components (major) (#2509)

* chore(ui): tombstone remaining deprecated props as never

Remove runtime fallbacks for deprecated space, grid, MenuButton
popover, Popover boundaryElement, Badge mode, and Avatar tone props.
Defaults move onto their replacements (gap, gridTemplate*, popover).

Co-authored-by: Cody Olsen <[email protected]>

* fix(storybook): migrate deprecated space/columns/useElementRect props

Replace Stack/Inline/Menu/Tree/TabList/Button `space` with `gap`, Grid
`columns`/`rows` with `gridTemplateColumns`/`gridTemplateRows`, and
`useElementRect` with `useElementSize` (border size). Drop matching
no-deprecated suppressions; leave theme/_compat suppressions alone.

Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): tombstone deprecated APIs with never types (major)

Finish call-site migrations, add throwing-hook tests, and a major
changeset documenting removed props/hooks/components and their
replacements.

Co-authored-by: Cody Olsen <[email protected]>

* revert(ui): keep PortalProvider boundaryElement for PortalContext

PortalProvider.boundaryElement still writes PortalContextValue, so it
cannot be tombstoned like other deprecated props.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop PortalProvider from tombstone changeset notes

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2512)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat: React 19 compiler target and ^19.2 peer deps (#2507)

* feat: target React 19 compiler and require react ^19.2

Compile @sanity/ui and @sanity/themer with reactCompiler target '19',
drop the now-unnecessary react-compiler-runtime dependency, and tighten
react (and react-dom for ui) peer ranges to ^19.2 across published packages.

Co-authored-by: Cody Olsen <[email protected]>

* chore: mark @sanity/themer bump as major in changeset

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave @sanity/icons and @sanity/logos peer ranges unchanged

Only @sanity/ui and @sanity/themer need the React 19.2 peer bump for the
compiler target change.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui): remove private `_visual-editing` entrypoint (#2513)

* feat(ui): remove private `_visual-editing` entrypoint

Drop `@sanity/ui/_visual-editing` now that tree-shaking from the main
export is sufficient for visual-editing consumers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: stop deep-linking workspace packages into transitive deps

Set linkWorkspacePackages to true (not deep) so sanity and
@sanity/visual-editing keep registry @sanity/ui until those
packages are updated for the removed _visual-editing entrypoint.

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* fix(ui): restore Popover max-width after boundaryElement migration (#2514)

fix(ui): restore Popover max-width from floatingBoundary

After tombstoning `boundaryElement`, max-width only came from
BoundaryElementProvider, so migrating to floatingBoundary/referenceBoundary
alone dropped constrainSize/preventOverflow width. Fall back to the floating
boundary for size, and wrap the Aligned story in BoundaryElementProvider.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2515)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* feat!: drop CommonJS from @sanity/ui, require Node.js >=22.12 (#2510)

* feat!: ship ESM only and require Node.js >=22.12

Drop CommonJS build outputs and `require` export conditions from
@sanity/ui, @sanity/color, and @sanity/logos. Raise published package
engines to >=22.12 to match sanity.

Co-authored-by: Cody Olsen <[email protected]>

* revert: leave color, logos, and icons unchanged

Scope the ESM-only / engines bump to @sanity/ui and @sanity/themer only.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): drop custom chunkFileNames, use tsdown defaults

Hashed shared-chunk filenames already prevent the entry/chunk
collision that dist/_chunks/ was guarding against (#2262).

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): restore UserConfig annotation on tsdown config

Match themer/logos so the inferred config type stays portable in the
package TypeScript program (TS2883).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2516)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix: revert @sanity/themer to last published prerelease

* Revert "fix: revert @sanity/themer to last published prerelease"

This reverts commit e325e53.

* feat: replace AnimatePresence with AnimateActivity (#2508)

* feat: replace AnimatePresence with AnimateActivity

Vendor Motion's AnimateActivity (Activity + exit animations) and use it
in Tooltip, Popover, and Toast. Requires React 19.2 (handled separately).

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimateActivityProps module-private for knip

Co-authored-by: Cody Olsen <[email protected]>

* fix: keep AnimatePresence for ToastProvider

Toasts are mount/unmount list items; AnimatePresence fits that better
than Activity-based show/hide. AnimateActivity stays for Tooltip/Popover.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lazy-mount AnimateActivity until first open

Keep tooltips/popovers out of the DOM until first shown so hidden
Activity nodes do not leak duplicate text into Testing Library queries
(e.g. delay-group stories). After first open, Activity still preserves
exit animations and state on subsequent hides.

Co-authored-by: Cody Olsen <[email protected]>

* fix: drop lazy-mount ref, assert tooltip visibility in stories

Hidden Activity content is intentionally pre-rendered in the DOM, so the
tooltip stories wait for visibility instead of text presence. Flipping
animate off while open skips the exit animation, which is acceptable.

Co-authored-by: Cody Olsen <[email protected]>

* fix: await async expect in tooltip story play functions

Co-authored-by: Cody Olsen <[email protected]>

* feat: keep closed tooltips/popovers mounted with Activity

Wrap the animate=false paths in plain <Activity> instead of unmounting,
so open/close preserves state in both modes. Reset the menu's active
item via effect cleanup (Activity unmounts effects on hide) so
shouldFocus and selected registration behave like a fresh mount on
reopen. Update tooltip/menu tests to assert visibility rather than DOM
presence, and gate the Recursive popover story's content on open since
hidden activities pre-render (an unconditional recursive content would
render an infinitely deep hidden tree).

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: document why native useEffectEvent is blocked, pin the freshness contract (#2506)

* feat!: use React's native useEffectEvent

Remove the use-effect-event dependency in favor of React's built-in
useEffectEvent, and raise the react/react-dom peer dependency minimum
to ^19.2.

Co-authored-by: Cody Olsen <[email protected]>

* fix: route useEffectEvent through an internal shim

React 19.2's native useEffectEvent never updates past first-render
values when the calling component is wrapped in forwardRef or memo
(react/react#34818) — the commit phase only applies effect-event
updates to plain function-component fibers. DialogCard and Tooltip are
forwardRef components, so Escape handling read a stale isTopLayer and
one Escape cascade-closed every nested dialog.

Keep the use-effect-event dependency removed, but route the three call
sites through a small internal shim (useRef + useInsertionEffect) until
react/react#34831 ships in a stable release.

Co-authored-by: Cody Olsen <[email protected]>

* fix: lint errors in useEffectEvent test, update changeset

Co-authored-by: Cody Olsen <[email protected]>

* docs: update changeset for internal effect-event shim

Co-authored-by: Cody Olsen <[email protected]>

* revert: keep use-effect-event, native useEffectEvent unusable in forwardRef/memo

Audit of all call sites: useGlobalKeyDown and useClickOutsideEvent are
public hooks that execute in the caller's fiber (all internal callers —
DialogCard, Menu, Breadcrumbs — are forwardRef components, and external
callers can be anything), and Tooltip is a forwardRef component. React
19.2's native useEffectEvent never updates past first-render values in
forwardRef/memo fibers (react/react#34818), so no site can use the
native hook until the fix ships in a stable release.

Restore the use-effect-event dependency at all three sites with TODO
comments, drop the internal shim, and add a regression test that pins
the fresh-values contract for forwardRef/memo callers.

Co-authored-by: Cody Olsen <[email protected]>

* chore: restore lockfile to match next

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat(ui)!: drop @juggle/resize-observer, use native ResizeObserver (#2518)

* fix(ui): drop @juggle/resize-observer, use native ResizeObserver

ResizeObserver is available in all browsers Sanity UI targets, so the
polyfill and its custom typings are no longer needed.

Co-authored-by: Cody Olsen <[email protected]>

* refactor(ui): remove _ResizeObserver wrapper, use native ResizeObserver directly

Co-authored-by: Cody Olsen <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* feat: ban forwardRef, createRef, createElement, Component, PureComponent with oxlint (#2519)

feat: ban forwardRef, createRef, createElement, Component, PureComponent via oxlint

Adds react entries to eslint/no-restricted-imports in .oxlintrc.json and
migrates every @sanity/ui component from forwardRef to React 19 ref-as-prop
function components. @sanity/icons and @sanity/logos keep forwardRef through
a scoped override since their react peer ranges still include React 18,
where refs only attach to forwardRef-wrapped function components. The class
ErrorBoundary keeps an inline disable (error boundaries have no function
equivalent), and the useGlobalKeyDown freshness-contract test keeps its
deliberate forwardRef usage with an inline disable.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore(deps): update renovatebot/github-action action to v46.2.0 (next) (#2521)

* Version Packages (next) (#2517)

* feat(ui): move heavy-dependency components to dedicated subpath entry points (#2522)

* feat(ui): extract static CSS with vanilla-extract into @sanity/ui/styles.css (#2523)

Sets up the vanilla-extract build pipeline in @sanity/ui via
@sanity/tsdown-config's vanillaExtract option, with inject disabled so the
stylesheet is never self-imported: consumers add
`import '@sanity/ui/styles.css'` themselves, on the same export path the
vanilla-extract based next major already uses.

Migrates the first slice of fully static styled-components CSS (SrOnly,
Spinner, SpanWithTextOverflow) to colocated .css.ts modules, and wires
.css.ts compilation into every consumer of the package's TypeScript source:
storybook, the icons app, the ui/themer vitest configs, the docs studio
(sanity dev/build), and the docs Next app (Turbopack, via
@vanilla-extract/next-plugin).

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* chore: ban class components via react/prefer-function-component

Replace the no-restricted-imports ban on Component/PureComponent with
react/prefer-function-component (allowErrorBoundary), so ErrorBoundary
no longer needs an inline disable.

Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2524)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>

* refactor(ui): use function declarations instead of named const expressions (#2526)

Replace `export const Foo = function Foo` (and matching internal helpers)
with `export function Foo`. Leave polymorphic `XxxComponent = function Xxx`
casts as-is so display names and type assertions keep working.

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>

* Version Packages (next) (#2529)

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

* fix(ui): emit types for styles css (#2536)

* Version Packages (next) (#2538)

* chore: exit changesets prerelease mode, restore main release config

Prep for merging the 4.0 branch to main: `changeset pre exit` (the next
`changeset version` on main produces stable 4.0.0), point the Changesets
baseBranch back at main, drop the `next` push triggers from the CI and
release workflows, and delete the placeholder kickoff changeset that
only existed to start the prerelease cycle.

Co-authored-by: Cody Olsen <[email protected]>

* docs: make pending changesets easier to scan

Rewrite each pending 4.0 changeset with a bold one-line takeaway first,
short bullets for what breaks, and an explicit what-to-do section, so
the stable 4.0.0 changelog is skimmable.

Co-authored-by: Cody Olsen <[email protected]>

* feat(docs): serve the docs site from the v4 nav document

Swap primaryNavId from 'v3' to 'v4' so /ui renders the v4 nav. The
stale "UI4" content release (rzyecOZ0D, staged against the legacy main
nav in Oct 2025) is archived in the dataset, replaced by a fresh empty
"UI 4" release (rG3Odb604) for staging the v4 content updates.

Co-authored-by: Cody Olsen <[email protected]>

* docs: add v3-to-v4 migration guide, link it from the docs navbar

MIGRATION.md walks through the 4.0 upgrade as an ordered checklist
(runtime requirements, styles.css import, moved entry points, removed
deprecated APIs, behavior changes, themer 1.0). The docs navbar links
to it on GitHub next to the repository button.

Co-authored-by: Cody Olsen <[email protected]>

* chore: keep @sanity/themer on the 0.x line, bumping minor to 0.3.0

Downgrade the themer bumps in the pending changesets from major to
minor. Exiting pre mode increments from the current package version, so
reset packages/themer/package.json from the 1.0.0-next.7 prerelease to
the published stable 0.2.1 - `changeset version` now produces 0.3.0
(verified with a dry run). The migration guide section is retitled
accordingly.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop the styles.css declaration changeset

The declaration fix only ever shipped on 4.0.0-next.* test releases -
v3 has no styles.css, so it is noise in the stable 4.0.0 changelog. The
declaration itself still ships with the build.

Co-authored-by: Cody Olsen <[email protected]>

* docs: require('@sanity/ui') keeps working on modern Node

Dropping the CJS build does not break require() consumers: the root
export resolves via a default condition and Node >=22.12 supports
require(esm). Only a too-old Node version breaks. Correct the changeset
and migration guide accordingly.

Co-authored-by: Cody Olsen <[email protected]>

* chore: split multi-package changesets per package

Give @sanity/ui and @sanity/themer their own changesets for the ESM /
Node 22.12 requirement, the React 19.2 requirement, and the subpath
entry point republish. Most @sanity/ui consumers have no relationship
to @sanity/themer, so cross-package mentions in the changelog are
noise. Verified with a dry-run version: bumps unchanged (ui 4.0.0,
themer 0.3.0) and the ui changelog no longer mentions themer.

Co-authored-by: Cody Olsen <[email protected]>

* chore: drop the unminified styles.css changeset

Like the declaration changeset, this only made sense within the
4.0.0-next.* test releases: v3 has no styles.css, so for anyone
upgrading from v3 the stylesheet has always been unminified.

Co-authored-by: Cody Olsen <[email protected]>

* docs: describe styles.css without assuming future majors

Drop the claims that more styles move into the stylesheet over time and
that the export path matches a vanilla-extract-based next major - the
changeset and migration guide should only describe what UI4 ships.

Co-authored-by: Cody Olsen <[email protected]>

* docs: point at the v3 maintenance branch

A copy of main now lives on the v3 branch (its release-v3 dist-tag
setup lands in #2551). Document it where the 4.x line talks about
versions: the migration guide points people staying on 3.x at it, and
CONTRIBUTING/AGENTS describe the maintenance-branch and dist-tag layout
alongside the existing v2 line.

Co-authored-by: Cody Olsen <[email protected]>

* chore: regenerate lockfile after rebasing onto main

Co-authored-by: Cody Olsen <[email protected]>

* docs: incorporate the v2-to-v3 migration guide

Co-authored-by: Cody Olsen <[email protected]>

* chore: combine the 4.0 changesets into a single release note

Merge the nine @sanity/ui changesets into one major changeset so the
4.0.0 changelog reads as a single coherent release note (linking the
migration guide), and the three @sanity/themer changesets into one
minor changeset. Delete the leftover pre.json - without preState the
version math is unchanged (verified by dry run: ui 4.0.0, themer
0.4.0). The dependency-update changesets inherited from main are left
as-is.

Co-authored-by: Cody Olsen <[email protected]>

* chore: exclude @sanity/themer from the 4.0 release

Keep the published @sanity/themer exactly as main ships it: restore its
react peer range, engines, react-compiler-runtime dependency, compiler
target 18 (and the matching knip ignore entry), drop its changeset and
the migration guide section, and add it to the Changesets ignore list
so the @sanity/ui major does not auto-bump and republish it as a
dependent. Only in-repo dev shims remain (subpath imports of the
workspace ui and the vanilla-extract vitest plugin), which the monorepo
needs to build and test themer against ui v4 source. A follow-up PR
releases themer against ui 4 and removes the ignore.

Co-authored-by: Cody Olsen <[email protected]>

* docs: explain why React 19.2 is the minimum version

The 19.2 floor comes from the <Activity> component, which shipped in
React 19.2.0 and which v4 uses for Tooltip/Popover mounting. State this
in the release note and the migration guide.

Co-authored-by: Cody Olsen <[email protected]>

* docs: describe styles.css as a move off styled-components

Consumers do not need to know the build-time tooling. Say what matters
to them: these components no longer use styled-components, more follow
in future 4.x minors, and UI5 ships without runtime CSS-in-JS.

Co-authored-by: Cody Olsen <[email protected]>

* fix: point MenuButton boundaryElement deprecation at the v4 Popover API

The tombstone said popover={{boundaryElement}}, but PopoverProps.
boundaryElement is itself removed in v4 - the working replacements are
floatingBoundary/referenceBoundary. Also correct the _visual-editing
migration note: the moved components (Menu*, Popover) import from their
new entry points, not the root. Both spotted in review by @jordanl17.

Co-authored-by: Cody Olsen <[email protected]>

* docs: state the exports-aware moduleResolution requirement

Dropping typesVersions means moduleResolution "node" (node10) cannot
resolve the subpath entry points' types - the root entry still resolves
via the kept top-level types field. Document that v4 needs node16,
nodenext or bundler. Raised in review by @jordanl17.

Co-authored-by: Cody Olsen <[email protected]>

* Update config.json

* Update queries.ts

* Update constants.ts

* Update avatar.tsx

* Update button.tsx

* Update card.tsx

* Update code.tsx

* Update container.tsx

* Update flex.tsx

* Update grid.tsx

* Update heading.tsx

* Update inline.tsx

* Update kbd.tsx

* Update label.tsx

* Update stack.tsx

* Update text.tsx

* Update CHANGELOG.md

* Update package.json

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Cody Olsen <[email protected]>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
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