Andrew's Digital Garden

Donut scope

Donut scope is all about picking where a scope starts and ends. We want to apply styling to a certain area (the 'donut'), while allowing content inside of that area to be unstyled (the 'donut hole'). The 'donut hole' is where user-created content lives, whether that be:

  • content passed the component
  • 3rd party embeds (+ styling)
  • user-generated copy
  • etc

Think of a Tabs component, where the individual Tab buttons and Tab Panels require styling, but content within should be unopinionated.

This helps work against the cascade - you're basically asking for a certain range to cascade in, but stop before the internal slot.

This is achievable with the [[20211115082404-css-not-selector]] in some forms:

  • foo :not(.foo .foo *) to match things inside one .foo wrapper but not two
  • .container :not(.content *) to get simple shallow donut scope

The latter of these two doesn't seem to work without passing two selectors: .content, .content *. Unsure why, but the behaviour of :not is a bit more confusing when no element is passed before the :not.

article p:not(blockquote *) would match all p elements inside of an article, but not descendents of a blockquote.

![[CSS not.png]]

[[css]]

Referred in

Donut scope