v6.0.0-alpha.1: :has() control-state rules cause multi-second style recalculation on pages with many form controls (switch/check/radio/range/form-field)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 15/100
- Issue type
- Bug
- Clarity
- Needs clarification
- Activity status
- Active
- Tech stack
- bootstrap, css
- Domain
- frontend, performance
Research direction
The report names the :has() control-state selectors in bootstrap.css, such as :has(input:checked), :has(input:disabled) and :has(.check:checked), and says the cost appears when a theme variable or the html attribute changes. Start by building a repro that reproduces the slowdown, since the reporter's minimal page did not, by varying control count, nesting and checked/disabled state. Done means a reproduction that shows the cost and a measured explanation of which rules cause it.
Written by the indexing model from the issue text.
Description
Summary
On a real admin page that uses Bootstrap 6.0.0-alpha.1, changing one attribute on takes about 12 seconds before the next frame is painted. Removing only the Bootstrap rules that use :has() on form controls brings the same change down to about 1.9 seconds. Removing our own :has() rules changes nothing.
I have not yet reproduced this on a minimal page that uses only Bootstrap's stylesheet, so I am reporting it as a measured regression on a real page, with the page-level evidence below. I would welcome guidance on how to build a smaller repro.
Environment
Bootstrap: 6.0.0-alpha.1 (npm, [email protected])
Browser: Google Chrome (stable), Windows 11
Page: an admin dashboard with a sidebar, a topbar, a customizer panel (a drawer with radio inputs, switches, checkboxes and a range), and several cards containing form fields and tables
Stylesheet size: the compiled admin CSS contains about 130 :has() selectors, about 107 of them from Bootstrap
Steps to reproduce (on our page)
Open the page.
Open the customizer and click a different accent swatch (this changes data-accent on , which changes a set of custom properties used across the page).
Measure the time from the click to the next painted frame (two requestAnimationFrame callbacks after the click).
Measured results
Time from click to next painted frame, one accent change, 1440x900, median of repeated runs:
Condition Click-to-paint
Unmodified page about 12,800 ms
Only our own :has() rules removed (61 rules) about 12,850 ms
Only Bootstrap's :has() rules removed (107 rules) about 1,940 ms
Other measurements from the same page:
Removing only the validation-state rules (.is-invalid, .is-valid, :user-invalid, :user-valid): no change (about 12,600 ms).
Removing only the rules for switch, check, radio, range, form-field, form-adorn, chip-input and form-ghost: about 2,050 ms.
Page load to DOMContentLoaded on the same page: about 9 s (Bootstrap 5.3.8 on the same page: about 1.8 s).
Observations
The cost comes from a few hundred :has() selectors that test form-control state (for example :has(input:checked), :has(input:disabled), :has(.check:checked)) on ancestors such as form fields and switch wrappers.
Every time the accent changes, many custom properties change. Each element with a matching :has() rule then has to be re-checked, which is where the time goes.
The same family is present in Bootstrap 5.3.8 in a different form, but on this page 5.3.8 does not show the delay.
What I tried to reproduce it minimally, and what happened
I built a page with 2,000 .form-field > .check checkboxes, loaded only bootstrap.css from 6.0.0-alpha.1, and measured a click that changes an attribute on and a click that changes --primary-bg on . The click-to-paint time was about 6 to 13 ms for the attribute change and about 110 to 125 ms for the variable change, with and without the :has() rules. So the minimal page does not show the regression. The difference must come from something the minimal page lacks: the number and nesting of controls, the :checked / :disabled state of the matched inputs, or the variables that change. I have not identified which, and I'd value the maintainers' view.
Expected
Changing a theme variable or an attribute on should not take seconds on a page with a few thousand form controls.
Actual
About 12 seconds to the next painted frame on our page, reduced to about 2 seconds when Bootstrap's control-state :has() rules are not present.
Notes
I can share the page and the measuring script on request.
We are considering changing our own markup to set state classes from JavaScript, so this may be a non-issue for us in the long run, but the Bootstrap rules remain in the stylesheet for everyone else.
- Dominant language
- MDX
- Stars
- 175k
- Forks
- 78.7k
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 103
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from twbs/bootstrap
-
Select input form caret wrong position on RTL modePossibly taken A pull request linked to this issue is open or already merged. Openbug confirmed css v6
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
twbs/bootstrap#43327 · 1 comment ·
Maintainers usually reply within 1 day
-
v6: OTP input keeps the rest of a selection when you type over itPossibly taken @thejesh23 claimed this 1 day ago. Openbug confirmed has-pr js v6
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
twbs/bootstrap#43323 · 2 comments ·
Maintainers usually reply within 1 day
-
v6: Strength ignores common symbols as special charactersPossibly taken @thejesh23 claimed this 1 day ago. Openbug confirmed docs has-pr js v6
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
twbs/bootstrap#43322 · 1 comment ·
Maintainers usually reply within 1 day
-
v6 alpha - hover-lift effect on menu dropdown only works when mouse is over the buttonPossibly taken @Aryan9891 claimed this 5 days ago. Openbug confirmed css has-pr v6
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Maintainers usually reply within 1 day
-
Dialog closes when a file input inside it has its picker cancelledPossibly taken @samyferhat claimed this 5 days ago. Openbug confirmed has-pr js v6
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
twbs/bootstrap#43278 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
Similar issues
-
Panic on live viewport resize when unstyled elements exist in display: none subtreePossibly taken @GoCoder7 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
DioxusLabs/blitz#1488 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
brave/adblock-lists#3068 ·
Maintainers usually reply within 1 day
-
Art style modal: show the character avatars it reads, and name them in the Use references buttonOpenenhancement good first issue priority: low size: XS
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
Refit MenuOpen(RFE) Enhancement Refit Project
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
MegaMek/mekhq#10934 · 1 comment ·
Maintainers usually reply within 1 day