I would like to use the CSS framework Bulma with LitElement. I know I can use an External Stylesheet However, they state it is bad practice to do it this way. I also have the problem that I had to import it into each element, which doesn't feel right.
So I copied the whole Bulma file content into a js module, but the styles are not applied after importing it.
import { css } from 'lit-element'
export default css`
@-webkit-keyframes spinAround {
from {
transform: rotate(0deg);
}
...
Importing the style as link tag works but is as mentioned bad practice.
import { LitElement, html, css } from 'lit-element'
import './src/table.js'
import styles from './styles.js'
class LitApp extends LitElement {
constructor() {
super()
this.tHeader = ['status', 'name', 'alias']
this.data = [
['connect', 'john', 'jdoe'],
['disconnect', 'carol', 'carbon'],
['disconnect', 'mike', 'mkan'],
['disconnect', 'tina', 'tiba'],
]
}
static get styles() {
return [
styles, // does not work
css`
:host {
padding: 5vw;
min-height: 100vh;
}
table.table {
width: 100%;
}`
]
}
render() {
return html`
<link rel="stylesheet" href="./node_modules/bulma/css/bulma.min.css">
<div class="columns">
<div class="column is-8 is-offset-2">
<div class="card">
<div class="card-content">
<agent-table .theader=${this.tHeader} .data=${this.data}></agent-table>
</div>
</div>
</div>
</div>`
}
}
customElements.define('lit-app', LitApp)
Furthermore, the Table does not receive the styles and I had to import the file again, which I would like to avoid.
class AgentTable extends LitElement {
constructor() {
super()
this.tHeader = []
this.data = []
}
static get properties() {
return {
theader: { type: Array },
data: { type: Array },
}
}
render() {
return html`
<table class="table is-narrow">
<thead>
<tr>${this.tHeader.map((header) => html`<td class="is-capitalized">${header}</td>`)}</tr>
</thead>
<tbody>
${this.data.map((row) => html`<tr>
${row.map((cell) => html`<td class="is-capitalized">${cell}</td>`)}
</tr>`)}
</tbody>`
}
}
customElements.define('agent-table', AgentTable)
I generally struggle to find a good approach to apply a CSS framework. I read back and forth on this page but I don't get it to work.
Sorry, If this is a bit ambiguous or hard to understand I struggle to voice this question properly.
I would like to use the CSS framework Bulma with LitElement. I know I can use an External Stylesheet However, they state it is bad practice to do it this way. I also have the problem that I had to import it into each element, which doesn't feel right.
So I copied the whole Bulma file content into a js module, but the styles are not applied after importing it.
import { css } from 'lit-element'
export default css`
@-webkit-keyframes spinAround {
from {
transform: rotate(0deg);
}
...
Importing the style as link tag works but is as mentioned bad practice.
import { LitElement, html, css } from 'lit-element'
import './src/table.js'
import styles from './styles.js'
class LitApp extends LitElement {
constructor() {
super()
this.tHeader = ['status', 'name', 'alias']
this.data = [
['connect', 'john', 'jdoe'],
['disconnect', 'carol', 'carbon'],
['disconnect', 'mike', 'mkan'],
['disconnect', 'tina', 'tiba'],
]
}
static get styles() {
return [
styles, // does not work
css`
:host {
padding: 5vw;
min-height: 100vh;
}
table.table {
width: 100%;
}`
]
}
render() {
return html`
<link rel="stylesheet" href="./node_modules/bulma/css/bulma.min.css">
<div class="columns">
<div class="column is-8 is-offset-2">
<div class="card">
<div class="card-content">
<agent-table .theader=${this.tHeader} .data=${this.data}></agent-table>
</div>
</div>
</div>
</div>`
}
}
customElements.define('lit-app', LitApp)
Furthermore, the Table does not receive the styles and I had to import the file again, which I would like to avoid.
class AgentTable extends LitElement {
constructor() {
super()
this.tHeader = []
this.data = []
}
static get properties() {
return {
theader: { type: Array },
data: { type: Array },
}
}
render() {
return html`
<table class="table is-narrow">
<thead>
<tr>${this.tHeader.map((header) => html`<td class="is-capitalized">${header}</td>`)}</tr>
</thead>
<tbody>
${this.data.map((row) => html`<tr>
${row.map((cell) => html`<td class="is-capitalized">${cell}</td>`)}
</tr>`)}
</tbody>`
}
}
customElements.define('agent-table', AgentTable)
I generally struggle to find a good approach to apply a CSS framework. I read back and forth on this page https://lit-element.polymer-project/guide/styles but I don't get it to work.
Sorry, If this is a bit ambiguous or hard to understand I struggle to voice this question properly.
Share Improve this question edited Apr 17, 2020 at 6:39 Penny Liu 17.5k5 gold badges86 silver badges108 bronze badges asked Jan 6, 2020 at 21:06 The FoolThe Fool 20.6k6 gold badges73 silver badges120 bronze badges 4- I don't know lit-element, but I assume your problem is that these elements use shadow DOM? – connexo Commented Jan 6, 2020 at 21:49
- yes, those are web ponents. So they use shadow dom. – The Fool Commented Jan 6, 2020 at 21:55
- 3 Web ponents don't have to use shadow DOM. They can, but it's not mandatory. Google "contructable stylesheets". – connexo Commented Jan 6, 2020 at 21:57
- @connexo if its not using a shadow dom, its a custom element not a web ponent. Web ponent is a custom element that uses a shadow dom. But yes adopted style sheet could be probably a solution. – The Fool Commented Mar 9, 2020 at 21:26
3 Answers
Reset to default 2There are a few problems with importing styles with the link tag. This is why it's a bad practice:
- A new stylesheet will be created every time an instance of your element is created. LitElement uses constructable stylesheets (if supported) to share a single stylesheet instance across all elements.
- The stylesheet is loaded after the element is rendered, so there can be a flash-of-unstyled-content
- The ShadyCSS polyfill won't work
- The href attribute is relative to the main document
- Adding few thousands css rules to a custom element that only needs a few it's not the way it's meant to be. CSS frameworks should be split by custom-elements.
That said, you can do what you did there and copy the whole Bulma inside a js module. The problem it's that you have to escape the character "" of that CSS.
css`
.breadcrumb li + li::before {
color: #b5b5b5;
content: "\0002f";
}
`
This doesn't work as expected but doesn't fail because template literals allow you to read the raw strings as they were entered, without processing escape sequences. So there aren't any JS errors.
Escape those and it'll be fine:
css`
.breadcrumb li + li::before {
color: #b5b5b5;
content: "\\0002f";
}
`
Note, the browser support for the below is currently awful. As far as I understand, it only runs on chrome by default. Firefox has it implemented, but you need to start Firefox with an additional flag. In this post by the creator of svelte, the styling problem also es up. There are some fascinating bits to take away from this whole discussion.
As hinted by some a while ago and already been under suspicion, I had another go at the problem.
So I am using adopted stylesheets now in this variant. As showcased here.
This seems like a legit way in some cases.
'use strict';
// define some web ponent
class WebComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.text = ''
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<!-- setting some custom css just to see if it stays -->
<style> p { padding: 1rem; background-color: pink; } </style>
<!-- this is expected to bee large font size from tailwindcss -->
<p class="title is-size-1">${this.text}</p>`
}
}
customElements.define('web-ponent', WebComponent)
// create two instances of the ponent to see if adopted style
// will change for both, meaning they are both referencing the same
// thing in memory
const webComponentA = document.createElement('web-ponent')
webComponentA.text = 'cowsay'
const webComponentB = document.createElement('web-ponent')
webComponentB.text= 'web'
// construct a blank style sheet
const sheet = new CSSStyleSheet();
(async () => {
// get some css styles as text, maybe with fetch if purely in the browser
// or via module resolver like rollup & webpack
const url = 'https://cdn.jsdelivr/npm/[email protected]/css/bulma.min.css'
const response = await fetch(url)
await sheet.replace(await response.text());
// set the styles on both ponents and add them to the dom
webComponentA.shadowRoot.adoptedStyleSheets = [sheet];
webComponentB.shadowRoot.adoptedStyleSheets = [sheet];
document.body.append(webComponentA, webComponentB)
})().catch(console.warn);
// some time later, purge the style sheet
// and watch both ponents loosing the framework styles
setTimeout(() => sheet.replaceSync(``), 3000);
Unfortunately, one has to use a constructed style sheet
like shown above. It doesn't work if you try to give the document root style sheet. I.E.
webComponent.shadowRoot.adoptedStyleSheets = [document.StyleSheet.item(0)]
DOMException: Failed to set the 'adoptedStyleSheets' property on 'ShadowRoot': Can't adopt non-constructed stylesheets.
I had this same question almost 4 years and a half later (integrating Bulma CSS with Lit); and in case it helps someone else, here is what I did.
I found the documentation on this page https://lit.dev/docs/ponents/shadow-dom
"Each Lit ponent has a render root—a DOM node that serves as a container for its internal DOM.
By default, LitElement creates an open shadowRoot and renders inside it, producing the following DOM structure:
<my-element>
#shadow-root
<p>child 1</p>
<p>child 2</p>
There are two ways to customize the render root used by LitElement:
- Setting shadowRootOptions.
- Implementing the createRenderRoot method."
So the fastest option, just make sure all the ponents in the hierarchy (top -> down) overrides the createRenderRoot
function and return this
, to avoid the elements being wrapped (isolated) by a shadowRoot
.
createRenderRoot() {
return this;
}