diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index c51de75fd6..d1bf7783e0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -371,6 +371,16 @@ function relative_selector_might_apply_to_node( for (const complex_selector of complex_selectors) { const selectors = truncate(complex_selector); + const left_most_combinator = selectors[0]?.combinator ?? descendant_combinator; + // In .x:has(> y), we want to search for y, ignoring the left-most combinator + // (else it would try to walk further up and fail because there are no selectors left) + if (selectors.length > 0) { + selectors[0] = { + ...selectors[0], + combinator: null + }; + } + if ( selectors.length === 0 /* is :global(...) */ || apply_selector(selectors, rule, element, stylesheet, check_has) @@ -379,7 +389,7 @@ function relative_selector_might_apply_to_node( // and now looking upwards for the .x part. if ( apply_combinator( - selectors[0]?.combinator ?? descendant_combinator, + left_most_combinator, selectors[0] ?? [], [relative_selector], rule, diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index 403a7a46c9..b590b2e50a 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -85,6 +85,20 @@ export default test({ column: 17, line: 81 } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "x:has(> z)"', + start: { + line: 88, + column: 1, + character: 968 + }, + end: { + line: 88, + column: 11, + character: 978 + } } ] }); diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index 51824840f1..41f1ca7628 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -75,3 +75,10 @@ /* (unused) .unused x:has(y) { color: red; }*/ + + x.svelte-xyz:has(> y:where(.svelte-xyz)) { + color: green; + } + /* (unused) x:has(> z) { + color: red; + }*/ diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte index 05064c6508..87bc9bb34c 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -81,4 +81,11 @@ .unused x:has(y) { color: red; } + + x:has(> y) { + color: green; + } + x:has(> z) { + color: red; + }