Describe the bug
Command.Item and Calendar.Cell / RangeCalendar.Cell both expose state via the data-selected attribute, but they mean different things:
Command.Item.data-selected = "this item is the currently active item" — flips on every pointer transition between items (via onpointermove → setValue, mutating aria-selected + data-selected on both the old and new item: 4 attribute mutations per transition).
Calendar.Cell.data-selected / RangeCalendar.Cell.data-selected = "this date is selected" — flips on click.
In isolation this is benign. But as soon as any consumer CSS contains a :has([data-selected]) rule (which is the natural Tailwind / CSS pattern for "highlight the cell when its day is selected"), Chromium registers data-selected document-wide for :has() invalidation. Every Command pointer transition then triggers Chromium to walk :has() invalidation across the page.
This reproduces today in production on https://www.shadcn-svelte.com/docs/components/combobox: hovering across the combobox items produces ~500 ms blocking UpdateLayoutTree events per item-to-item transition (≈7,000-element DOM). The combobox feels frozen while in use. The trigger is shadcn-svelte's [&:has([data-selected])]:bg-accent utility on the range-calendar cell, which ships in the global CSS bundle even on pages that don't render a calendar.
The slow path is in Chromium's :has() invalidator, but the fix that closes the entire class of problem lives in bits-ui: don't share data-selected between two unrelated primitives. Anyone pairing a Combobox/Command with a Calendar in the same app re-creates this bug regardless of CSS bundling.
Reproduction
Live, no setup required: https://www.shadcn-svelte.com/docs/components/combobox
- Click the combobox to open it.
- Move the cursor up and down across the framework items.
- Observe perceptible lag (~500 ms blocking) on each item transition.
Performance trace: each item-to-item transition fires one UpdateLayoutTree event of ~500 ms over ~7,000 elements. Cost scales linearly with surrounding DOM size, so the same Command in a smaller page is proportionally faster but not free.
Confirmed by isolated experiment: directly toggling data-selected on a Command.Item via JS on the same page (bypassing Command's handler) produces the same ~500 ms recalc. Stripping :has([data-selected]) from the page CSS drops it to ~1 ms. Renaming the attribute in Command source (verified locally) produces the same ~1 ms result without touching the CSS.
Suggested fix
Rename Command.Item's state attribute to something component-scoped — data-active, data-highlighted, or data-command-selected. Specifically in packages/bits-ui/src/lib/bits/command/command.svelte.ts around line 1480:
-"aria-selected": boolToStr(this.isSelected),
-"data-selected": boolToEmptyStrOrUndef(this.isSelected),
+"aria-selected": boolToStr(this.isSelected),
+"data-command-selected": boolToEmptyStrOrUndef(this.isSelected),
Note on the consumer-side fix
This can also be fixed at the consumer (shadcn-svelte) level by rewriting the [&:has([data-selected])]:bg-accent utility on the calendar cell to a direct data-selected:bg-accent against the cell — which already carries data-selected since bits-ui mirrors state to both Cell and Day. Verified locally; visual output identical, hover recalc cost drops from ~500 ms to ~1 ms.
I'm happy to open a parallel issue against shadcn-svelte covering that fix and a few related ones (the data-checked custom variant matching [data-state="checked"], two demo files using group-has-[[data-state=open]]/menu-item:) if it's useful — let me know.
The bits-ui-side rename is still worth doing because it closes the class of bug for any consumer pairing Command with a Calendar, regardless of CSS framework or how careful the consumer is with :has(). The shadcn-svelte fix is the workaround for this consumer; the rename is the structural fix.
(This was researched, verified and written by Opus 4.7, reviewed by me)
Reproduction
(above)
Logs
System Info
System:
OS: macOS 15.7.3
CPU: (10) arm64 Apple M4
Memory: 135.97 MB / 24.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 25.6.1 - /opt/homebrew/bin/node
npm: 11.9.0 - /opt/homebrew/bin/npm
pnpm: 10.32.1 - /opt/homebrew/bin/pnpm
bun: 1.3.13 - /opt/homebrew/bin/bun
Deno: 2.7.4 - /opt/homebrew/bin/deno
npmPackages:
@sveltejs/kit: ^2.55.0 => 2.55.0
bits-ui: ^2.16.3 => 2.16.3
svelte: ^5.54.0 => 5.54.0
Severity
annoyance
Describe the bug
Command.ItemandCalendar.Cell/RangeCalendar.Cellboth expose state via thedata-selectedattribute, but they mean different things:Command.Item.data-selected= "this item is the currently active item" — flips on every pointer transition between items (viaonpointermove→setValue, mutatingaria-selected+data-selectedon both the old and new item: 4 attribute mutations per transition).Calendar.Cell.data-selected/RangeCalendar.Cell.data-selected= "this date is selected" — flips on click.In isolation this is benign. But as soon as any consumer CSS contains a
:has([data-selected])rule (which is the natural Tailwind / CSS pattern for "highlight the cell when its day is selected"), Chromium registersdata-selecteddocument-wide for:has()invalidation. Every Command pointer transition then triggers Chromium to walk:has()invalidation across the page.This reproduces today in production on https://www.shadcn-svelte.com/docs/components/combobox: hovering across the combobox items produces ~500 ms blocking
UpdateLayoutTreeevents per item-to-item transition (≈7,000-element DOM). The combobox feels frozen while in use. The trigger is shadcn-svelte's[&:has([data-selected])]:bg-accentutility on the range-calendar cell, which ships in the global CSS bundle even on pages that don't render a calendar.The slow path is in Chromium's
:has()invalidator, but the fix that closes the entire class of problem lives in bits-ui: don't sharedata-selectedbetween two unrelated primitives. Anyone pairing a Combobox/Command with a Calendar in the same app re-creates this bug regardless of CSS bundling.Reproduction
Live, no setup required: https://www.shadcn-svelte.com/docs/components/combobox
Performance trace: each item-to-item transition fires one
UpdateLayoutTreeevent of ~500 ms over ~7,000 elements. Cost scales linearly with surrounding DOM size, so the same Command in a smaller page is proportionally faster but not free.Confirmed by isolated experiment: directly toggling
data-selectedon aCommand.Itemvia JS on the same page (bypassing Command's handler) produces the same ~500 ms recalc. Stripping:has([data-selected])from the page CSS drops it to ~1 ms. Renaming the attribute in Command source (verified locally) produces the same ~1 ms result without touching the CSS.Suggested fix
Rename
Command.Item's state attribute to something component-scoped —data-active,data-highlighted, ordata-command-selected. Specifically inpackages/bits-ui/src/lib/bits/command/command.svelte.tsaround line 1480:Note on the consumer-side fix
This can also be fixed at the consumer (shadcn-svelte) level by rewriting the
[&:has([data-selected])]:bg-accentutility on the calendar cell to a directdata-selected:bg-accentagainst the cell — which already carriesdata-selectedsince bits-ui mirrors state to bothCellandDay. Verified locally; visual output identical, hover recalc cost drops from ~500 ms to ~1 ms.I'm happy to open a parallel issue against shadcn-svelte covering that fix and a few related ones (the
data-checkedcustom variant matching[data-state="checked"], two demo files usinggroup-has-[[data-state=open]]/menu-item:) if it's useful — let me know.The bits-ui-side rename is still worth doing because it closes the class of bug for any consumer pairing Command with a Calendar, regardless of CSS framework or how careful the consumer is with
:has(). The shadcn-svelte fix is the workaround for this consumer; the rename is the structural fix.(This was researched, verified and written by Opus 4.7, reviewed by me)
Reproduction
(above)
Logs
System Info
System: OS: macOS 15.7.3 CPU: (10) arm64 Apple M4 Memory: 135.97 MB / 24.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 25.6.1 - /opt/homebrew/bin/node npm: 11.9.0 - /opt/homebrew/bin/npm pnpm: 10.32.1 - /opt/homebrew/bin/pnpm bun: 1.3.13 - /opt/homebrew/bin/bun Deno: 2.7.4 - /opt/homebrew/bin/deno npmPackages: @sveltejs/kit: ^2.55.0 => 2.55.0 bits-ui: ^2.16.3 => 2.16.3 svelte: ^5.54.0 => 5.54.0Severity
annoyance