Sibling Combinator combined with :has
can be improperly marked as unused
We didn't collect sibling elements of a given element to then check the :has
selectors. This adds the logic for that. Fixes #14072
feat:
, fix:
, chore:
, or docs:
.pnpm test
and lint the project with pnpm lint
Fixes #14072
:has()
was matching only against descendants or siblings, but not sibling's descendants.
Also, in a case like :has(> .foo > .bar)
it was matching against children instead of descendants, so I had to drop the >
optimization. But, to ensure that in case like :has(> .foo)
it matches only the direct children, the inner selector is prepended with a selector of new type that matches only certain element.
feat:
, fix:
, chore:
, or docs:
.packages/svelte/src
, add a changeset (npx changeset
).pnpm test
and lint the project with pnpm lint
Fixes #14072, alternative of #15207
:has()
was matching only against descendants or siblings, but not sibling's descendants.
In this variant, I made apply_selector
and related functions bidirectional. So the same logic can be applied to all selectors.
Changing the direction for selectors passed into :has()
broke p:has(+ y)
for the test has-with-render-tag
and to fix it I added diving into the @render()
tags during gathering siblings, which fixed the render-tag-loop
test.
I also tried to cache the function retrieving neighbor elements and got the following cache hits rates on the test cases: descendant_forward: 94,23% descendant_backward: 55,91% children_forward: 84,00% children_backward: 33,51% sibling_forward: 69,42% sibling_backward: 59,94% next_forward: 41,40% next_backward: 2,44% Though, I'm not sure how to measure performance change.
feat:
, fix:
, chore:
, or docs:
.packages/svelte/src
, add a changeset (npx changeset
).pnpm test
and lint the project with pnpm lint
I've got some complex sibling combinator + :has
css selectors that match the DOM but are reported as unused by Svelte. Through further testing, it appears that some trivial cases are incorrectly marked as unused.
./packages/svelte/tests/css/samples/has/input.svelte
style
blockx:has(+ c) {
color: green;
}
x:has(~ c) {
color: green;
}
Notice that these are valid selectors based on the given DOM structure
css_unused_selector
errors. I've found that wrapping c
in :global()
seems to get rid of the errors, but I'm not sure if that should be required to get this selector working.No response
Svelte Version - 5.1.6
annoyance
Here is a REPL for a common use case:
<div class="form-field">
<label for="a">label</label>
<input id="a" type="text" required>
</div>
<style>
label:has(+ [required])::after {
content: '*';
}
</style>
@dummdidumm Could we please reopen?
Sibling :has()
selectors are still reported as unused and rules don't apply (unless wrapped in :global()
. Please see below basic example:
<section class="list">
<div class="list-item">should be red</div>
<div class="list-item"><h2>heading</h2></div>
<div class="list-item">...</div>
</section>
/* reported as unused */
.list-item:has( + .list-item h2) {
background: red ;
}
/* works only with global */
:global(.list-item:has( + .list-item h2)) {
background: red ;
}
Tested in: Svelte Version: svelte@5.2.7
(I upgraded from 5.0.0-next.260 to svelte@5.2.7, in 260 this was not an issue)