Search code examples
cssangularsass

Apply CSS styles to descendants but not if a child is of a certain selector


I'm asking this question for an Angular application but I believe this relates to CSS in general.

I have an Angular component with a number of child components, and each has the standard view encapsulation. As an example, I want to change the line-height of all p tags within the parent component, including children components, to a different value.

Something like the following works for this (SCSS):

:host {
    ::ng-deep {
        p {
            line-height: 2em;
        }
    }
}

However, I don't want to change this style to effect a certain child component, let's call it foobar. So I hoped something like the following would work:

:host {
    ::ng-deep {
        :not(foobar) {
            p {
                line-height: 2em;
            }
        }
    }
}

I understand this doesn't work because the p element can be descendants of other elements and so the rule is still applied.

I also know I can do the following:

:host {
    ::ng-deep {
        p:not(foobar p) {
            line-height: 2em;
        }    
    }
}

However, if I want to override a number of elements/classes etc. then this becomes rather unmanageable.


So question is; is there a good/modern way to do this using SCSS or more modern selectors? Anything on the Angular side that can help (aside from turning off viewEncapsulation)?


Solution

  • I resolved this by using a SCSS mixin:

    @mixin not-foobar {
        &:not(foobar #{nth(nth(&, 1), length(nth(&, 1)))}) {
            @content;
        }
    }
    

    with example usage:

    :host {
        ::ng-deep {
            p {
                @include not-foobar {
                    line-height: 2em;
                }
            }    
        }
    }
    

    The mixin basically creates the p:not(foobar p) { selector but means I only need to include the mixin where it's needed, rather than using the selector and possibly setting it incorrectly.

    The #{nth(nth(&, 1), length(nth(&, 1)))}) part of the mixin basically selects the immediate parent. Using just & set the whole parent which led to unwanted side effects in my case.