Skip to content

Conversation

@jveres
Copy link
Contributor

@jveres jveres commented Jan 12, 2026

This PR addresses significant performance issues when opening/closing dialogs, popovers, and menus in applications with large DOM trees (e.g., chat apps with hundreds of messages).

Changes:

  1. Presence layer animation caching (presence.svelte.ts):

    • Added TTL-based caching for getAnimationName() to reduce redundant getComputedStyle() calls from 4+ per animation cycle down to 1
    • Changed styles state from live CSSStyleDeclaration to a snapshot object { display, animationName } to prevent layout thrashing when accessing style properties
  2. CSS containment (dialog.svelte.ts, popover.svelte.ts, menu.svelte.ts):

    • Added contain: layout style paint to floating content elements to isolate style/layout calculations from the rest of the page
  3. TextSelectionLayer optimization (use-text-selection-layer.svelte.ts):

    • Removed document.body style modifications that were triggering full DOM style recalculations on every pointerdown
    • Now only sets userSelect on the dialog content itself

These changes dramatically improve animation performance in apps with large DOMs, reducing dialog open/close blocking time from ~150ms to ~15ms in tested scenarios.

@changeset-bot
Copy link

changeset-bot bot commented Jan 12, 2026

🦋 Changeset detected

Latest commit: e50b5a3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
bits-ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Jan 12, 2026

built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
bits-ui ✅ Ready (View Log) Visit Preview e50b5a3

* the entire DOM tree, which is extremely slow with large DOMs.
*
* Instead, we only set userSelect on the dialog content itself.
* This is a minor UX tradeoff (text selection can technically overflow
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say this isn't a minor tradeoff since the whole idea of this utility is to prevent this behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right - I understated the impact.

I've reverted the TextSelectionLayer change. The PR now only includes:

  1. Presence layer animation caching - TTL-based caching for getAnimationName() and style snapshots to prevent layout thrashing
  2. CSS containment - contain: layout style paint on floating content elements

I also discovered that preventOverflowTextSelection={false} already exists as a prop on Dialog/Popover content. For apps with large DOM trees where the body style modification causes performance issues, users can opt-out by setting this prop.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Will give this a review, appreciate you looking into this.

This PR addresses significant performance issues when opening/closing
dialogs, popovers, and menus in applications with large DOM trees
(e.g., chat apps with hundreds of messages).

Changes:

1. **Presence layer animation caching** (`presence.svelte.ts`):
   - Added TTL-based caching for `getAnimationName()` to reduce
     redundant `getComputedStyle()` calls from 4+ per animation
     cycle down to 1
   - Changed `styles` state from live `CSSStyleDeclaration` to a
     snapshot object `{ display, animationName }` to prevent
     layout thrashing when accessing style properties

2. **CSS containment** (`dialog.svelte.ts`, `popover.svelte.ts`,
   `menu.svelte.ts`):
   - Added `contain: layout style paint` to floating content
     elements to isolate style/layout calculations from the
     rest of the page

These changes dramatically improve animation performance in apps with
large DOMs, reducing dialog open/close blocking time from ~150ms to
~15ms in tested scenarios.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@huntabyte huntabyte merged commit d0a3e18 into huntabyte:main Jan 28, 2026
6 checks passed
@github-actions github-actions bot mentioned this pull request Jan 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants