liveBook · Manning

Scoping limit

As explained in CSS in Depth, Second Edition

An inner scoping limit is a boundary in a scoped CSS rule that prevents the rule from applying within descendant scopes.

A scoped CSS rule can still reach into a module nested inside it. If an outer module styles every heading in its scope, it may also style a heading that belongs to an inner module. Scope proximity resolves some conflicts, but it does not stop the outer rule from supplying a property the inner module leaves unspecified. An inner scoping limit prevents those outer selectors from reaching into the nested area. section 9.3 section 9.3.2

Add a limit with `to` and a second selector: `@scope (.highlight-block) to (.slot)`. Read this as styling from `.highlight-block` until `.slot`. The bounded area has a hole where the inner module begins—sometimes called “donut scoping.” section 9.3.2

Listing 9.18 Adding a scoping limit
@layer modules {
  @scope (.highlight-block) to (.slot) {
    :scope {
      padding: 1rem 1.5rem;
      background-color: #b3cbe6;
    }

    h1,                 #1
    h2,                 #1
    h3,                 #1
    h4 {                #1
      color: #264b73;
    }
  }
}

The heading rule can style headings in the highlight block without reaching into its `.slot` boundary. section 9.3.2

A dropdown containing other modules

Use `@scope (.dropdown) to (.drawer > *)` when a dropdown must style its drawer but not the elements inside it. The boundary starts at the drawer’s children, leaving the drawer itself available to the dropdown’s rules. section 9.3.2

Listing 9.19 Dropdown module written using scope
@layer modules {
  @scope (.dropdown) to (.drawer > *) {
    :scope {
      display: inline-block;
      position: relative;
    }

    .toggle {
      padding: 0.5em 2em 0.5em 1.5em;
      border: 1px solid #ccc;
      font-size: 1rem;
      background-color: #eee;
    }

    .toggle::after {
      content: "";
      position: absolute;
      right: 1em;
      top: 1em;
      border: 0.3em solid;
      border-color: black transparent transparent;
    }

    .drawer {
      display: none;
      position: absolute;
      left: 0;
      top: 2.1em;
      min-width: 100%;
      background-color: #eee;
    }

    :scope.is-open .toggle::after {
      top: 0.7em;
      border-color: transparent transparent black;
    }
    :scope.is-open .drawer {
      display: block;
    }
  }
}

The dropdown rules style the toggle and drawer, while the limit keeps them out of whatever the drawer contains. section 9.3.2

Choose a limit when a module should not affect child modules at all. If styles must cross that boundary, leave it open and manage precedence with selector specificity and cascade layers instead. Browser support for `@scope` is still emerging. section 9.3.4 section 9.5

How CSS in Depth, Second Edition covers it

How deep it goes
Explains `to` syntax, boundary placement, and why proximity alone cannot prevent unwanted styles.
Examples
Uses a highlight block with a slot and a dropdown whose drawer contains other content.
What it assumes
Introduces the `to` boundary while assuming familiarity with CSS selectors and module markup.
What you can do after
Operate/configure it by adding a `to` boundary that keeps module selectors out of nested content.
How it connects
Connects CSS scope to scope proximity, modular CSS, selector specificity, and cascade layers.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of CSS in Depth, Second Edition. The section links point to the parts of the book it draws on.