@@ -136,9 +136,10 @@ function truncate(node) {
136136 * @param {Compiler.Css.Rule } rule
137137 * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement } element
138138 * @param {Compiler.Css.StyleSheet } stylesheet
139+ * @param {boolean } [contains_render_tag]
139140 * @returns {boolean }
140141 */
141- function apply_selector ( relative_selectors , rule , element , stylesheet ) {
142+ function apply_selector ( relative_selectors , rule , element , stylesheet , contains_render_tag ) {
142143 const parent_selectors = relative_selectors . slice ( ) ;
143144 const relative_selector = parent_selectors . pop ( ) ;
144145
@@ -152,7 +153,19 @@ function apply_selector(relative_selectors, rule, element, stylesheet) {
152153 ) ;
153154
154155 if ( ! possible_match ) {
155- return false ;
156+ contains_render_tag ??= element . fragment . nodes . some ( ( node ) => node . type === 'RenderTag' ) ;
157+ if ( contains_render_tag ) {
158+ // If the element contains a render tag then we assume the selector might match something inside the rendered snippet
159+ // and traverse the blocks upwards to see if the present blocks match our node further upwards.
160+ // (We could do more static analysis and check the render tag reference to see if this snippet block continues
161+ // with elements that actually match the selector, but that would be a lot of work for little gain)
162+ const possible = apply_selector ( parent_selectors , rule , element , stylesheet , true ) ;
163+ if ( possible ) return true ; // e.g `div span` matched for element `<div>{@render tag()}</div>`
164+ // Continue checking if a parent element might match the selector.
165+ // Example: Selector is `p span`, which matches `<p><strong>{@render tag()}</strong></p>` and we're currently at `strong`
166+ } else {
167+ return false ;
168+ }
156169 }
157170
158171 if ( relative_selector . combinator ) {
@@ -171,6 +184,13 @@ function apply_selector(relative_selectors, rule, element, stylesheet) {
171184 crossed_component_boundary = true ;
172185 }
173186
187+ if ( parent . type === 'SnippetBlock' ) {
188+ // We assume the snippet might be rendered in a place where the parent selectors match.
189+ // (We could do more static analysis and check the render tag reference to see if this snippet block continues
190+ // with elements that actually match the selector, but that would be a lot of work for little gain)
191+ return true ;
192+ }
193+
174194 if ( parent . type === 'RegularElement' || parent . type === 'SvelteElement' ) {
175195 if ( apply_selector ( parent_selectors , rule , parent , stylesheet ) ) {
176196 // TODO the `name === ' '` causes false positives, but removing it causes false negatives...
@@ -222,6 +242,10 @@ function apply_selector(relative_selectors, rule, element, stylesheet) {
222242 }
223243 }
224244
245+ // We got to the end of this under the assumption higher up might start matching,
246+ // but turns out it didn't - therefore the selector doesn't apply after all.
247+ if ( contains_render_tag ) return false ;
248+
225249 // if this is the left-most non-global selector, mark it — we want
226250 // `x y z {...}` to become `x.blah y z.blah {...}`
227251 const parent = parent_selectors [ parent_selectors . length - 1 ] ;
0 commit comments