Lit-html: Sharing styles

Created on 7 Dec 2017  Â·  10Comments  Â·  Source: Polymer/lit-html

One of the benefits of a js templating approach is that it is easier to share code between js, css and html. I work a lot of css animations and I want to, for example, share animation timing constants between my css and js. However, the following does not work currently within lit:

const timing = 500;
const template = html`
  <style>
    .some-element {
      ...some styling
      animation-duration: ${timing}ms;
    }
  </style>
`;

setTimeout(() => {
   ...do some work after animation
}, timing);

As the ${...} gets parsed by lit. This behavior is good for html, as we want to prevent XSS. But does the same apply for styles?

High Bug

Most helpful comment

It seems to me that the interpolation only works the first time, and subsequently fails to update on property changes.

All 10 comments

you could do it like this

const myStyle = `
    .some-element {
      /* ...some styling */
      animation-duration: ${timing}ms;
    }
`;

const template = html `
  <style>
    ${unsafeHTML(myStyle)}
  </style>
`;

also maybe better then a timeout would be
myElement.addEventListener('animationend', () => { //... })

animationend would be better for this indeed, but I use this as an example for using constants inside style. For example, let's say I want to share a bit of styling between two elements:

const sharedStyles = `
  .my-custom-button {
    /*  ... button styling */
  }
`;

class MyElementA extends MyLitElement {
  get style() {
    return html`<style>${sharedStyles}</style>`;
  }
  render() {
     return html`
       ${this.style}
       <button class="my-custom-button">Button A</button>
    `;
  }
}

class MyElementB extends MyLitElement {
  get style() {
    return html`<style>${sharedStyles}</style>`;
  }
  render() {
     return html`
       ${this.style}
       <button class="my-custom-button">Button B</button>
    `;
  }
}

We could use unsafeHTML but that looks weird and opens up the HTML for XSS. But does the same apply for styles?

What version of lit-html do you have?

This should be working now:

const template = html`
  <style>
    .some-element {
      ...some styling
      animation-duration: ${timing}ms;
    }
  </style>
`;

Although, I'd prefer to use CSS custom variables when possible.

@justinfagnani Thanks for the tip, I will test it with the newest lit-html. I'm using whatever @daKmoR has set up, we're working in the same place :)

I like css variables, but I wonder about the runtime cost. If you have dynamic variables, or want to enable customization it makes sense to use variables. But for sharing static constants this approach might be cheaper and simpler.

Also variables don't cover bundled sets of styles. In Polymer that's solved through CSS Mixins, but that requires a lot of runtime magic which I'm worried about.

currently we are using the latest npm release (0.7.1) maybe we want to switch to master as there is already so much goodness in it :)

Matter unfortunately now has a bug that is going to break bindings in styles... Sorry about that. We need test coverage there.

For what it's worth, static CSS environmental variables using (env(…)) are hopefully eventually coming in CSS Variables Level 2. They cannot be overridden by the inheritance cascade, and they will also be able to be used in media queries. See w3c/csswg-drafts#1693. But, in the meantime, surely browser engines will aggressively cache and optimize dynamic CSS variables too.

@justinfagnani Style bindings seem to work fine for me? Both of the following examples correctly set a border on the body in Firefox and Chrome, and the CSS rule looks fine in all major browsers:

const d = document.createElement('div');
document.body.appendChild(d);

// note: replace .rules with .cssRules for Firefox

render(html`<style>body { border-left: ${2}px solid red; }</style>`, d);
console.log(d.innerHTML);
//    <style>body { border-left: 2px solid red; }</style>
console.log(d.children[0].sheet.rules[0].cssText)
//    Chrome/Firefox: body { border-left: 2px solid red; }
//    Safari: body { border-left-width: 2px; border-left-style: solid; border-left-color: red; }
//    IE: body { border-left-color: red; border-left-width: 2px; border-left-style: solid; }

// contrived example to get comment nodes
render(html`<style>${'body { '}${'border-left: 2px solid blue; '}${'}'}</style>`, d);
console.log(d.innerHTML);
//    <style><!---->body { <!---->border-left: 2px solid blue; <!---->}<!----></style>
console.log(d.children[0].sheet.rules[0].cssText)
//    Chrome/Firefox: body { border-left: 2px solid blue; }
//    Safari: body { border-left-width: 2px; border-left-style: solid; border-left-color: blue; }
//    IE: body { border-left-color: blue; border-left-width: 2px; border-left-style: solid; }

EDIT: Just to clarify: I'm not saying we shouldn't remove the comments from the style tags—they definitely shouldn't be there. I'm just saying that it technically works.

It seems to me that the interpolation only works the first time, and subsequently fails to update on property changes.

lit-html style support has been fixed for a while, though ShadyCSS still has limitation where styles can't dynamically change.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

justinfagnani picture justinfagnani  Â·  4Comments

pmkroeker picture pmkroeker  Â·  5Comments

cbelden picture cbelden  Â·  4Comments

dakom picture dakom  Â·  4Comments

fopsdev picture fopsdev  Â·  5Comments