[whatwg] Selectors within <style scoped>

Ian Hickson ian at hixie.ch
Wed Sep 14 17:55:19 PDT 2011


The question posed in this thread is whether selectors in scoped style 
sheet blocks should be affected by ancestors of the scoped block. For 
example, should this be possible:

   <body class="archive">
    ...
    <section>
     <style scoped>
      section > h1 { border-bottom: solid; }
      body.homepage h1 { color: red; }
      body.archive h1 { color: gray; }
     </style>
     <h1>Hello</h1> <!-- this changes colour based on whether it's on a 
                         page with <body class=homepage> or <body 
                         class=archive> -->
     <p>Welcome.</p>
    </section>
    ...
   </body>

Several people suggested that this would be confusing, because of 
situations such as the following:

   <aside>
    <h1>Snippets from other blogs</h1>
    <section>
     <!-- content inserted from another blog -->
     <style scoped>
      section > h1 { border-bottom: solid; }
      p { text-align: left; }
      aside p { text-align: right; }
     </style>
     <p>Hello.</p>
     <aside> <p> I'm happy. </p> </aside>
    </section>
   </aside>

Here, the embedded content expects to just style its own aside, but ends 
up styling all of its contents because it happens to be inside another 
aside. This, it is argued, is undesireable.


On Thu, 16 Jun 2011, Lachlan Hunt wrote:
>
> This is the purpose of the :scope pseudo-class that is defined to match 
> the contextual reference elemnt, which for scoped stylesheets, will be 
> the parent of the style element. [...]
> 
> http://dev.w3.org/2006/webapi/selectors-api2/#the-scope-pseudo-class

Thus, to do the first example, we would use these rules:

      section:scope > h1, :scope section > h1 { border-bottom: solid; }
      body.homepage :scope h1 { color: red; }
      body.archive :scope h1 { color: gray; }

...and for the second example, we would use:

      section:scope > h1, :scope section > h1 { border-bottom: solid; }
      :scope p { text-align: left; }
      :scope aside p { text-align: right; }

Unfortunately, as some people implied on the thread, this is rather 
awkward and certainly not intuitive.

Thus the first proposal: to force all selectors in a scoped style block to 
not have any component simple selector matching any element that is an 
ancestor of the scoping element.

Unfortunately, this breaks the first example above. Thus, the second 
proposal:

On Thu, 16 Jun 2011, Dimitri Glazkov wrote:
>
> What if we do this:
> 
> 1) By default, <style scoped> implies that all selectors in this
> stylesheet are prefixed with ":scope".
> 2) Unless the ":scope" is already in the selector.

I think it might be better to phrase it as follows: 

 A sequence of simple selectors in a selector chain can only match the 
 scoped element and its descendants unless a sequence of simple selectors 
 later in the selector chain contains the :scope pseudo-class.

This avoids having to define any parsing mangling, but gets more or less 
the same effect (actually it gets a better effect, since it means that a 
selector chain consisting of just a simple selector can match both the 
scoping element and its descendants).

Thus, to do the first example, we would now use these rules which are a 
bit of a compromise between the intuitive (but wrong) original case and 
the more explicit case:

      section > h1 { border-bottom: solid; }
      body.homepage :scope h1 { color: red; }
      body.archive :scope h1 { color: gray; }

...and for the second example, we would just use the intuitive case:

      section > h1 { border-bottom: solid; }
      p { text-align: left; }
      aside p { text-align: right; }

However, as easy as that appears at first blush, I fear it would be seem 
quite magical to authors who have trouble enough understanding CSS as it 
is. Consider:

   <aside>
    <section>
     <style scoped>
      aside section h1 { ... } /* matches nothing */
      aside section:scope h1 { ... } /* matches h1 below */
     </style>
     <h1>Example</h1>
     ...
    </section>
   </aside>

Never before in CSS has making a selector more specific actually increased 
the number of elements it can match.


On Thu, 16 Jun 2011, Boris Zbarsky wrote:
> 
> Especially if we allowed the CSSOM in this situation to include the 
> implied ":scope" on selectors that did not include it in the text (in 
> which case, this is a simple parse-time transformation on the 
> stylesheet).

I think if we made the CSSOM reflect different selectors than what was in 
the style sheet, that would be very confusing for authors, especially if 
we rewrote the rules to match as described above (so some selector chains 
in the input would become two selector chains in the CSSOM).


On Thu, 16 Jun 2011, Tab Atkins Jr. wrote:
> 
> I think it's pretty easy to learn that bare selectors only apply to 
> children of the scoping element, not the scoping element itself.

With the rule as described above, there is no distinction between the 
element itself and its descendants. The boundary is between the scoping 
element and its parent, not at the element itself.


On Fri, 17 Jun 2011, Kornel LesiÅ~Dski wrote:
> 
> That feels magical and a bit backwards to me.
> 
> Why not scope all selectors except ones starting with :root?
> 
> .foo {scoped}
> 
> :root .foo {matches outside scope}

That seems just as magical. :-)

With the proposal above, you would have:

   .foo { ... } /* scoped */
   :root .foo:scope, :root :scope .foo { ... } /* matches outside scope */

Admittedly in the case where .foo might match either the scoping element 
or descendants of the scoping element, the full syntax is a bit longer.

For comparison, here's what the above examples would look like with your 
proposal. For the first:

      section > h1 { border-bottom: solid; }
      :root body.homepage h1 { color: red; }
      :root body.archive h1 { color: gray; }

...and for the second (same as with :scope's magic):

      section > h1 { border-bottom: solid; }
      p { text-align: left; }
      aside p { text-align: right; }

Personally I think having the magic relating directly to :scope is more 
understandable -- :scope is all about scoping. But only barely.

With :root doing the magic, you're really using :root as an @-rule; you 
might as well at that point actually do so:

     <style scoped>
      section > h1 { border-bottom: solid; }
      @global body.homepage h1 { color: red; }
      @global body.archive h1 { color: gray; }
     </style>

This does have the advantage of meaning there's no magic. Where do 
implementors stand on this? Are @rules an acceptable solution? (If so, 
we'd want to pass it by the CSSWG first.)


There was some discussion about whether to include the scoping element in 
the scope:

On Tue, 19 Jul 2011, Tab Atkins Jr. wrote:
> 
> I think it's best for that case to *not* match.  Otherwise, you have to 
> explicitly remember to add a :not(:scope) to every rule that might match 
> the scoping element.
> 
> It's very easy to style the scoping element by using :scope explicitly.

I think that would be confusing for the same reason we don't exclude the 
root element in regular selector matching.


On Wed, 20 Jul 2011, Kornel LesiÅ~Dski wrote:
> 
> I think it should match the scoping element, as I expect this pattern to be
> common until <style scoped> is widely supported:
> 
> <div id=widget>
>  <style scoped>
>    #widget foo {}
>  </style>
> </div>

True.


On Wed, 20 Jul 2011, Ashley Sheridan wrote:
> 
> While I agree that that might be a common pattern, I disagree that it's 
> actually a good one. Consider an ad service which wraps everything in a 
> custom <div> tag. If the scope allowed the immediate parent to be 
> included as part of the scope, then it could allow the advert to be 
> altered in a way that could negatively affect the users of the site the 
> ad appeared on.

That's possible anyway -- the advertisment could just not use scoped="", 
or use a hostile <script>.

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'


More information about the whatwg mailing list