Stylesheet
The stylesheet getter is a way to define the style for the component.
class SubmitButton extends WebComponent {
static observedAttributes = ['label'];
get stylesheet() {
return `
<style>
:host {
display: inline-block;
}
:host button {
background: blue;
color: #222;
}
</style>
`;
}
get template() {
return '<button type="submit">{label}</button>'
}
}
Note: You don't need to place the CSS inside the style tag when defining thestylesheet
property.WebComponent
will automatically place it inside thestyle
tag for you whether inside the shadow root or the head tag.
Syntax Highlight
To help with syntax highlight for editors like VSCode cwco
exposes
css
and html
.
import {html, css} from "cwco";
class SubmitButton extends WebComponent {
static observedAttributes = ['label'];
get stylesheet() {
return css`
:host {
display: inline-block;
}
:host button {
background: blue;
color: #222;
}
`;
}
get template() {
return html`<button type="submit">{label}</button>`;
}
}
If you are using editors like VSCode, such help works great with plugins like inline-html and lit-html
If you use jetbrains IDE, HTML and CSS syntax highlight is automatically done without anything.
CSS Object
Released with version 1.6.0
.
You may also return an object representation of your CSS which comes with a few advantages:
- Allows style nesting with
&
prefix; - Easier to extend from parent component;
class SubmitButton extends WebComponent {
static observedAttributes = ['label'];
radius = 5;
get stylesheet() {
return {
':host' {
display: 'inline-block';
button {
backgroundColor: 'blue';
color: '#222';
borderRadius: '[radius]px';
'&:hover {
backgroundColor: 'purple'
}
}
}
'@keyframes fadeInAndOut': {
from: {opacity: 0.6},
to: {opacity: 1}
}
}
}
get template() {
return '<button type="submit">{label}</button>'
}
}
Selectors
Selectors can be any valid CSS selector including @
block like @media
and @keyframes
.
{
'form button' {
backgroundColor: 'blue';
borderRadius: '5px';
animation: fadeInOut 0.5s ease;
},
'@keyframes fadeInOut': {
from: { opacity: 0.5},
to: { opacity: 1}
}
}
Camelcase property names
Any CSS selector property must be camel-cases following CSS properties references.
{
button {
backgroundColor: 'blue';
borderRadius: '5px';
}
}
// above becomes
button {
background-color: blue;
border-radius: 5px;
}
Nesting selectors
Nesting selectors is the quickest and recommended way to group style to increase specificity without having to repeat parent selectors
{
button {
backgroundColor: 'blue';
span: {
display: 'inline-block'
}
}
}
// above becomes
button {
background-color: blue;
}
button span {
display: inline-block;
}
Parent selector (&)
You may also use the parent selector to attach selector states and do more nesting.
{
button {
backgroundColor: 'blue';
'&:hover': {
backgroundColor: 'red';'
},
'& + span' {
display: 'inline-block'
}
}
}
// above becomes
button {
background-color: blue;
}
button:hover {
background-color: red;
}
button + span {
display: inline-block;
}
With the parent select spacing matters so &:hover
is not the same as & :hover
.
All &
will be replaced with whatever parent selector chain.
mode none
If the mode of the component is set to none
, the style is then placed inside the head
tag
if the component is a ContextProviderComponent
, otherwise style is ignored. In case the style is place in
the head
tag, any reference of :host
and :host-context
will be replaced with
the name of the component.
Components with "none" mode that are not ContextProviderComponent
should be components meant to be styled
by their parents. These should be headless components.
The bellow example shows how style will look like in the head tag with none
mode.
<head>
<style id="submit-button">
submit-button {
display: inline-block;
}
submit-button button {
background: blue;
color: #222;
}
</style>
</head>
Note: We recommend to start any style selector with :host
to avoid any style affecting other elements on the page
since the style will be placed in the head of the document.
data binding
You can refer to data inside the stylesheet by using the [...]
syntax.
This is great for when you want to refer to some theme data or simply want to react to data changes for CSS updates. Yes! It will update on data changes. This means that you may not need to define class to be set or removed on data changes to update the style.
Take the following button component as example. It is referring to the theme
of the app which can be just coming
from some context provider component,
and it is falling back to some other color in case those do not exist.
class MyButton extends WebComponent {
get stylesheet() {
return `
<style>
:host button {
color: [theme?.colors?.light ?? '#fff'];
background-color: [theme?.colors?.secondary ?? '#000'];
}
</style>
`
}
get template() {
return `<button type="button"><slot>click me</slot></button>`;
}
}
Let's say the theme provider component looks something like this:
class ThemeProvider extends ContextProvider {
static initialContext = {
theme: {
colors: {
primary: 'purple',
secondary: '#222',
cta: '#900',
light: '#f2f2f2',
dark: '#111',
},
}
}
}
We can then use the ThemeProvider
component to provide the theme to the MyButton
component.
<theme-provider>
<my-button></my-button>
</theme-provider>
import stylesheet
What the stylesheet
can also return is a stylesheet link. This is useful when you want to import a stylesheet
from another file.
class MyButton extends WebComponent {
get stylesheet() {
return '<link rel="stylesheet" href="./my-button.css">'
}
get template() {
return `<button type="button"><slot>click me</slot></button>`;
}
}
Modern Browsers will automatically import the stylesheet only ONCE even if the component is used multiple times on the page.
extend style
The ability to extend style is super useful when you want to inheret other components to make specific changes.
You can learn more about how to extend a component style by checking the abstract component doc.