CssScoper
Rewrites a component's stylesheet so its rules can only reach that component. A component declares plain CSS against the house tokens and never repeats its own name in a selector; this puts the name in, once, for every top-level rule. That is what lets two components written by two strangers use `.field` for different things on the same page without either author knowing the other exists — the alternative being the convention-and-goodwill scheme that produced one 4,849-line bundle nobody could safely edit. Three rules govern what is rewritten, and the two exceptions matter more than the rule: - A rule's SELECTOR is prefixed; its BODY is copied verbatim. Native CSS nesting therefore keeps working untouched — a nested rule already inherits its parent's now-scoped selector, so prefixing it again would scope it twice. - Conditional at-rules (`@media`, `@supports`, `@container`, `@layer` with a block, `@scope`) are recursed into, because their bodies hold ordinary rules. - Everything else at-rule-shaped is copied verbatim. `@keyframes` is the reason: its "selectors" are `from`, `to` and percentages, and prefixing those silently kills the animation. `:host` names the component's own root, the way it does in shadow DOM. Its plain-CSS inertness is the point: a component stylesheet that somehow reaches the page unscoped styles nothing, instead of styling the document like a stray `:root` would.
CssScoper::scope()
public function scope(string $css, string $scope): stringConfines a stylesheet to one component, returning CSS the page can embed as-is.
Parameters
| Name | Type | Description |
|---|---|---|
| $css | string | The component's stylesheet, as authored. |
| $scope | string | A selector matching the component root (e.g. `[data-milpa-component="input"]`). |