dna-poster

DNA

Progressive Web Components.

Styles

DNA can render plain <style> tags in a template, but what about style encapsulation?

Since components can be extended and stylesheets inherited, we need to be able to scope CSS rules based on the final component definition.

💁 You may don't need to use DNA's scoped style if you are already using a styling strategy to build and distribuite your CSS files. Other good options are CSS-in-JS, CSS Modules and BEM with lazy loading styles.

Scoped styles

Every <style> tag rendered to a component is scoped.

import { Component, customElement, html, property } from '@chialab/dna';

@customElement('x-card')
class Card extends Component {
    @property() title = '';

    render() {
        return html`
            &lt;style>
                h1 { color: cadetblue; }
            &lt;/style>
            &lt;h1>${this.title}&lt;/h1>
        `;
    }
}

The previous component will br rendered as follow:

&lt;h1>Unaffected heading&lt;/h1>
&lt;div is="x-card" title="Alan Turing">
    &lt;style>
        [is='x-card'] h1 { color: cadetblue; }
    &lt;/style>
    &lt;h1>Alan Turing&lt;/h1>
&lt;div>

In the resulting HTML of the example above, the first H1 color will not be affected by the scoped style declaration.

How does it work?

DNA uses the component definition name to prefix all CSS rules with an unique selector for the element. For example, the rule

h1 { color: cadetblue; }

is transformed into

[is="x-card"] h1 { color: cadetblue; }
The :host pesudo selector

If you need to style the component root itself, you can use the :host selector like in Shadow DOM contexts:

:host {
    color: #333;
    font-family: sans-serif;
}

You can also define styles for component states with :host(*selector*) rule:

:host(:hover) {
    background-color: #e5e5e5;
}

:host(:hover) span {
    color: red;
}

The :host selector will respect component definition also for inherited styles.

Other styling techniques

Every component node has the is attribute populated with the defined name of the component class. You can use an attribute selector to scope your CSS, for example using the SASS nesting:

/* CUSTOM ELEMENT */
x-card {
    h1 {
        color: cadetblue;
    }
}

/* BUILT IN ELEMENT */
[is='x-card'] {
    h1 {
        color: cadetblue;
    }
}
Plain CSS

Some bundlers import CSS files in JavaScript and auto-append <link> tags. In this case, you have to manually set the scope for your rules:

/* CUSTOM ELEMENT */
x-card .title {
    color: cadetblue;
}

/* BUILT IN ELEMENT */
[is='x-card'] .title {
    color: cadetblue;
}
&lt;link href="x-card.css" rel="stylesheet" />
CSS Modules

If you are using a transpiler, you can also use CSS Modules, which imports CSS classes as JavaScript references in order to use them in a template:

.title {
    color: cadetblue;
}
import { Component, customElement, html, property } from '@chialab/dna';
import { title } from './x-card.css';

@customElement('x-card', {
     extends: 'div',
})
class Card extends Component {
    @property() title = '';

    render() {
        return html`&lt;h1 class="${title}">&lt;/h1>`;
    }
}