Monday, August 25, 2014

At the Bottom of the Rabbit Hole, I Find Polyfill-Next-Selector


The more I investigate styling and theming Polymer elements, the less a grasp I feel like I have on it. The core-style continues to vex. Beyond that, I am also having difficulty applying consistent CSS styles on my elements.

I am unsure if my current problems are with Polymer or with the Core Elements and Paper Elements built on top of Polymer. The most recent issue that I find is that the <paper-button> inside of a sample Polymer element that I am trying to create do not share styles. Specifically, the border is not included on all paper-buttons:



Both the top buttons and the dialog buttons are <paper-button> elements. Both should be covered by my core-style CSS:
<polymer-element name="lorem-dialog">
  <template>
    <paper-button label="Bottom" on-tap="{{toggleDialog}}"></paper-button>
    <paper-button label="Center" on-tap="{{toggleDialog}}"></paper-button>

    <paper-dialog heading="Bottom Dialog" transition="paper-dialog-transition-bottom">
      <!-- ... -->
      <paper-button label="Accept" affirmative autofocus></paper-button>
    </paper-dialog><
    <!-- ... -->
    <core-style id="theme">
      :host /deep/ paper-button {
        color: {{g.primaryColor}};
        border: 5px solid {{g.secondaryColor}};
        padding: 2px 5px;
      }
    </core-style>
    <core-style ref="theme"></core-style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
The core-style CSS is applied with the /deep/ CSS modifier. My understanding of /deep/ is that my styles should penetrate any shadow DOMs that are under the current element. And it kinda/sorta does what I expect. The primary color, themed from the containing page, is set to orange and it does apply to all paper-button elements. But darn it, why are the borders not in place for the paper-buttons in the dialogs?

My first thought is that perhaps this is a native shadow DOM vs. polyfill issue, so I load the demo page up in Firefox. This only makes things worse:



The borders are applied to all paper-buttons, but none of the core-style global settings are in place. Bleh.

Taking a step back, I know from last night that core-style does not like the way that I am using it. I take that out of the equation by removing the id/ref core-style pair of tags, replacing them with a straight forward <style> tag:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <style>
      :host /deep/ paper-button {
        color: orange;
        border: 5px solid green;
        padding: 2px 5px;
      }
    </style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
That solve my Firefox problem. The polyfills now correctly style my paper-button elements:



But this remains unchanged in Chrome (stable through unstable). On the surface, it would seem that Polymer styles are just broken at this point, but I refuse to indulge in select-is-broken thoughts. At least not yet.

If I peek at one of the paper-button elements in the Chrome inspector, I find that my border style is being overridden:



But what is that double-colon content thingy? I have no idea how that applies to my paper-button elements, let alone is more specific (in a CSS selector sense) than my /deep/ paper-button selector. Furthermore, I am hindered by the odd lack of file and line number for this selector. How can I understand and work around the darn thing if I cannot even find it?

To the ack machine Robin!

Amazingly, that actually finds something:
$ ack -l ::content                                                  
bower_components/platform/platform.js
bower_components/platform/platform.js.map
bower_components/paper-dialog/paper-dialog.css
bower_components/core-style/elements.html
bower_components/core-component-page/core-component-page.html
Only one of those is a CSS file and just so happens to be the element (paper-dialog) that is wrapping my misbehaving paper-buttons. Taking a look at the offending CSS, I find:
polymer-next-selector { content: ':host > *'; }
::content > * {
  font: inherit;
  border: 0;
}
That is exactly the definition of the culprit that I found in the DOM inspector. A quick review of the other matching files reveals that none of them have the same style definition. So the question becomes… what the heck is a polymer-next-selector? More importantly, how do I override it?

The answer to the first question seems to be that polymer-next-selector is also known as polyfill-next-selector. It seems to be a way to apply styles in the polyfills, but which work natively with the shadow DOM if present. Only, the precedence on these things is crazy high.

Try as I might, I cannot target the distributed nodes of the paper-dialog from my custom element that tries to use paper-dialog. I am surprised that targeting the distributed nodes of paper-dialog has no effect:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <style>
      :host /deep/ paper-button {
        color: orange;
        border: 5px solid green;
        padding: 2px 5px;
      }

      paper-dialog::content > * {
        border: 5px dashed blue;
      }
    </style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
In the end, I give up trying to work with ::content and simply add an !important to the border style:
<polymer-element name="lorem-dialog">
  <template>
    <!-- ... -->
    <style>
      :host /deep/ paper-button {
        color: orange;
        border: 5px solid green !important;
        padding: 2px 5px;
      }
    </style>
  </template>
  <script>
    Polymer('lorem-dialog', {/* ... */});
  </script>
</polymer-element>
That does the trick:



But it feels like a cheat.

I cannot figure out why the ::content distributed node selector has such high precedence or why my attempts to influence it failed. I am probably overlooking some selector combinator that would allow me to target paper-button distributed into paper-dialog, but I wish it were easier to find that combinator. And if there is no easier way to do this, then why is paper-dialog using such a high precedence selector to ensure that none of its nodes have borders?

I have a solution, but I have more questions than answers to go along with that solution. Which makes this feel like a cheat.




Day #163

No comments:

Post a Comment