4

I'm trying to use :host-context() and adjacent sibling selectors to style an element like this:

<x-foo id="1" active></x-foo>
<x-foo id="2"></x-foo> <!-- use :host-context() to target #2 when adjacent sibling, #1 is [active] -->
<x-foo id="3"></x-foo>

My element definition looks something like:

<dom-module id="x-foo">
    <template>
        <style>
           :host-context(x-foo[active] + x-foo) {
               background-color: yellow;
           }
        </style>
        Hello
    </template>
    <script src="x-foo.js"></script>    
</dom-module>

However this doesn't quite work. Why not?

Casey
  • 1,802
  • 3
  • 22
  • 35
  • 3
    `:host-context(...):host` is redundant - `:host` has a functional form `:host(...)` for when you only want to match the host element against the selector argument. – BoltClock Aug 17 '16 at 11:47
  • Yeah, that was just cruft I forgot to trim from my demo. Fixed. – Casey Aug 18 '16 at 05:02

2 Answers2

10

:host() and :host-context() only accept a compound selector as their argument, and not a complex selector.

x-foo[active] + x-foo is a complex selector that contains two compound selectors, x-foo[active] and x-foo, separated by the adjacent sibling combinator. :host() and :host-context() can accept either compound selector, but not any combinators.

Unfortunately, because the shadow host's siblings do not exist in the shadow context, you won't be able to write something like x-foo[active] + :host. As a consequence, I don't think you will be able to accomplish what you're looking to do in a shadow context. (In the light context, of course, you can just write x-foo[active] + x-foo, but that defeats the purpose of prepackaging CSS into a web component.)

It's not clear to me why :host() and :host-context() weren't specced to allow complex selectors in the first place, since if they did, what you have would have just worked.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • In a shadow context you can also just write `x-foo[active] + x-foo`, because the `background-color` in inherited from its parent. – Supersharp Aug 17 '16 at 13:03
  • @Supersharp: x-foo is the shadow host in this context. You cannot match the shadow host with anything but the :host pseudos in a shadow context. And backgrounds aren't inherited - they just show through when the child element is transparent. (An *alarming* number of people seem to think backgrounds are inherited for some reason, and I can't tell if it's because those people misunderstand the word "inheritance", or if those people have forgotten what a background is and how it works.) – BoltClock Aug 17 '16 at 14:17
  • Thanks -- I was afraid this was the case. – Casey Aug 18 '16 at 05:01
-1

A workaround would be to add a global <style> rule.

You add it when you want (in the attachedCallback/connectedCallback for example). In the above snippet I put the <style> tag in the <template> of the custom element, and I move it once x-foo is defined.

customElements.define( 'x-foo', class XFoo extends HTMLElement {
  constructor() {
    super()
    this.attachShadow( { mode: 'open'} )
    this.shadowRoot.appendChild( tpl.content.cloneNode( true ) )
  }
} )

customElements.whenDefined( 'x-foo' ).then( () =>
  document.head.appendChild( tpl.content.getElementById( 'global' ) )
)
<x-foo id="1" active></x-foo>
<x-foo id="2"></x-foo>
<x-foo id="3"></x-foo>

<!-- custom element v1 -->
<template id="tpl">
  <style id="global">
    x-foo[active] + x-foo { background: yellow ; color: red }
  </style>

  <style>
    :host { background: lightblue ; display: inline-block }
    span { color: initial }
  </style>

  <span>Hello</span>
</template>

Note: I have used a custom element (v1) instead of Polymer, it works in Chrome v53+.

Community
  • 1
  • 1
Supersharp
  • 29,002
  • 9
  • 92
  • 134