Polymer: Extending and Overriding Styles in Polymer 2.0

Created on 11 Jun 2017  路  9Comments  路  Source: Polymer/polymer

What is the proper way to extend an element and adjust styles, in Polymer 2.0? From the upgrade docs:

Make sure your element's styles are defined inside the template. Defining styles outside the template was deprecated in Polymer 1.x and is no longer supported.

So if you have to define styles inside of the template, it seems impossible to extend an element and override just the styles? Say I'm reusing an element FooElement and want to change some styles that aren't exposed as variables. I know, I'll subclass FooElement and make it look how I want for my own usage.

<dom-module id="foobar">
  <template>
    <style>
      p {
        color: red;
      }
    </style>
  </template>
  <script>
    class FoobarElement extends FooElement {
      static get is() { return 'foobar'; }
    }

    window.customElements.define(FoobarElement.is, FoobarElement);
  </script>
</dom-module>
````

This will result in...nothing being rendered for `FoobarElement`. By forcing `style` to be inside of the `template`, I end up nuking the DOM for the element when all I want to do is tweak the style.

I could hack it into `ready()` via something like:

ready() {
super.ready();

var newStyles = document.createElement('style');
this.root.appendChild(newStyles);
newStyles.innerHTML = p { color: red; } ;
}
```

But isn't the point of Polymer to make this kind of stuff easier, not more hackish?

2.x needs investigation p2 wontfix

Most helpful comment

We haven't added declarative sugar for composing templates when subclassing in Polymer 2.0 (although we've done experiments with this in the past). That said, you have full control imperatively via the static get template getter.

Example:

<dom-module id="my-sub-element">
  <template id="styles">
    <style>
      p {
        color: red;
      }
    </style>
  </template>
  <script>
  let subTemplate;
  class MySubElement extends MySuperElement {
    static get template() {
      if (!subTemplate) {
        // start with clone of superclass template
        subTemplate = MySuperElement.template.cloneNode(true);
        // get some stuff you want to insert into super template
        const subStyle = Polymer.DomModule.import('my-sub-element', 'template#styles').content;
        // insert stuff in template before the super template's <style>
        // (or after, or wherever, you write the necessary code here)
        const superStyle = subTemplate.querySelector('style');
        superStyle.parentNode.insertBefore(subStyle, superStyle);
      }
      return subTemplate;
    }
  }
  customElements.define('my-sub-element', MySubElement);
  </script>
</dom-module>

Right now, this is the "right" way to do it (doing it this way means it's done statically once for the class, so you get all the efficiency of template cloning for the instances). Just because it's not "declarative" doesn't make it a hack; you have the full expressiveness of JavaScript here, and in the fullness of time we may encode the common expressiveness needed into a declarative template composition library if a de-facto one doesn't spring up in the community first.

All 9 comments

Have a look at vaadin-themable-mixin for some inspiration.

I'm not sure that we have done the due diligence on exploring the right pattern here. @sorvell, we should make sure this pattern is easier to accomplish.

@azakus, the way I've seen this kind of functionality allowed in other frameworks would have syntax something like:

<dom-module id="foobar">
  <template>
    <style>
      p {
        color: red;
      }
    </style>

    [[ template.super ]]
  </template>
  ...snip...
</dom-module>

Where [[ template.super ]] would insert the contents of the template of the element being extended. Although with CSS preference orders I guess if it just did a straight insert you'd have to put your style after the [[ template.super ]] to get the desired.

We haven't added declarative sugar for composing templates when subclassing in Polymer 2.0 (although we've done experiments with this in the past). That said, you have full control imperatively via the static get template getter.

Example:

<dom-module id="my-sub-element">
  <template id="styles">
    <style>
      p {
        color: red;
      }
    </style>
  </template>
  <script>
  let subTemplate;
  class MySubElement extends MySuperElement {
    static get template() {
      if (!subTemplate) {
        // start with clone of superclass template
        subTemplate = MySuperElement.template.cloneNode(true);
        // get some stuff you want to insert into super template
        const subStyle = Polymer.DomModule.import('my-sub-element', 'template#styles').content;
        // insert stuff in template before the super template's <style>
        // (or after, or wherever, you write the necessary code here)
        const superStyle = subTemplate.querySelector('style');
        superStyle.parentNode.insertBefore(subStyle, superStyle);
      }
      return subTemplate;
    }
  }
  customElements.define('my-sub-element', MySubElement);
  </script>
</dom-module>

Right now, this is the "right" way to do it (doing it this way means it's done statically once for the class, so you get all the efficiency of template cloning for the instances). Just because it's not "declarative" doesn't make it a hack; you have the full expressiveness of JavaScript here, and in the fullness of time we may encode the common expressiveness needed into a declarative template composition library if a de-facto one doesn't spring up in the community first.

Just because it's not "declarative" doesn't make it a hack; you have the full expressiveness of JavaScript here

True, that doesn't make it a hack in it of itself. It's more that it feels like a common use case that has an obscure solution; one that isn't anywhere in the documentation, or even hinted at (AFAIK), and requires a decent understanding of how Polymer works.

Just my two cents as someone new to Polymer trying to do things which, to me, seem like intuitive usage of components. CSS just feels like an after thought when it comes to applying a consistent style across reusable components for your project.

Also, take a look at the template-mixin. I'm using this to extend paper-elements.

const superStyle = subTemplate.querySelector('style');

that is not working for me and just returning undefined

I tried this const superStyle = subTemplate.content.querySelector('style'); and works for me. Is it ok to use this instead? New to Polymer here.

We haven't added declarative sugar for composing templates when subclassing in Polymer 2.0 (although we've done experiments with this in the past). That said, you have full control imperatively via the static get template getter.

I actually like the "experiments" behavior. Is the experiment still usable? Or is there any replacement that is known about at this time? A declarative method would be my preferred method of going about extending/inheriting templates.

Edit, to clarify my preference. I use polymer because it takes away writing how the template changes by manipulating strings from JavaScript. Having the template being the view and the JavaScript only the data to which the template is reactive to, makes using Polymer a very clean, easy to maintain, fast to produce experience.
Concatting strings and returning html from JavaScript obfuscates the clarity the