Re: [csswg-drafts] [css-scoping] Support for CSS namespaces

After various considerations of ways this might be implemented, I came up with an idea that seems quite simple, with minimal additional complexity for the browser.

The main goal of a namespace is rule isolation.  That is, the child space should not leak into the parent, and the parent space should (optionally) not leak into the child.  We have most of the tools to handle that already.  A secondary goal would be to avoid anything that touches the HTML markup, but instead keep the implementation entirely within CSS.

So, the additional tools:

1) Add a new function that's usable with the `@when` block conditional (issue #112), called `defined()`.  `defined()` accepts a property, and returns true or false depending on whether the property is defined. Or, more generally, applies that condition check for every single rule selector within the block it's a part of.  For standard browser properties, this would always be true, and thus not interesting.  However the real value is using it in conjunction with custom properties.

```
<html>
    <section id="one">
        <p>Stuff</p>
    </section>
    <section id="two">
        <p>Stuff</p>
    </section>
</html>


#one {
    --my-namespace: exists;
}

@when defined(--my-namespace) {
    p { color: red; }
}
```
You have the full flexibility of normal selections in order to assign the custom property (eg: an attribute selector on an ID or data- attribute in order to identify where the element is being put in the HTML), rather than being fixed in place in the HTML using a `namespace` attribute or whatever.  This also means it can apply to various nodes all at the same time without having to edit each one directly (as with the earlier suggestion of the explicit `namespace` attribute).

Custom properties are already set up to dynamically handle DOM scoping.  In the above example, the paragraph in section #one would be red, but section #two would not be.  It's using a custom property to control an entire set of rules, rather than one property value at a time.

Given that scoping is already handled for custom properties, this means that the `@when` block implicitly prevents the child scope rules from leaking into the parent scope. For every rule inside the `@when` block, selectors are only valid when the custom property is defined, which neatly isolates the rule applications.

We can also (if we wish) prevent the parent scope rules from leaking into the child scope with another already existing property: `all: unset`.

```
p {
    background-color: black;
}

@when defined(--my-namespace) {
    * { all: unset; }
    p { color: red; }
}
```

The `*` selector would only select elements for which `--my-namespace` was defined.  That prevents anything from the parent scope from affecting the child (such as the black background-color) — except for propagating any other custom properties (which aren't affected by `unset`).  That means that the child scope can accept custom properties as configuration parameters while still being shielded from parent leakage.  For reusable widgets, you'd probably want standardized naming conventions to avoid collisions (eg: --company-product-parameter-name), but that's outside the scope of the rules.

2) One additional component that I think would be necessary would be some way to address the 'root' element of a given custom property. That is, elements which have the custom property defined, but which do not have an ancestor element with the custom property defined.  This would be repurposing the `:root` of the document, but since the document `:root` would not be valid within this block (and thus meaningless in its original sense) unless it had the custom property defined within it (in which case this becomes entirely redundant), it seemed appropriate to allow the `:root` within this block to mean something analagous.  There's no guarantee of a single root node, though (eg: a widget might be inserted multiple times in a single page), so there might be a preference for a different term instead.

Whether it's called `:root` or something else, though, there should be a way to reference the highest-level node(s) for which the custom property is defined.


That seems to be all that should be necessary to implement namespaces, while keeping things clean and flexible enough to use the feature for more general purposes.  Codewise, most of the work is already done, given custom properties. There should be no issue with multiple `defined()` blocks overlapping, which opens up additional use cases.  And everything should work pretty normally with javascript, so no extra fanciness needed there.

If you need to add rules that 'penetrate' the boundary (without modifying the original file) as suggested with the `>>>` in the original scoping proposal, you can just put them into another `@when defined() {}` block for the same custom property, and the normal cascade takes care of things from there. 

The only possible tricky bit is how this gets applied with the `@when` block structure, since they're likely to treat conditions as a one-time case, rather than applying to everything within the block.  I would actually consider this as a suggestion that `@when` and `@if` could be considered two separate ideas.  `@if` is a one-time condition check that just separates out parts of the CSS, and `@when` is a condition that has to apply to everything within the block.  I know there was a question of which word should be used, and this could be a reason to treat them separately, depending on how the rules want to approach things.


-- 
GitHub Notification of comment by Kinematics
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/270#issuecomment-290969197 using your GitHub account

Received on Sunday, 2 April 2017 07:10:31 UTC