CWCO logo

v1.7.5

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 the stylesheet property. WebComponent will automatically place it inside the style 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:

  1. Allows style nesting with & prefix;
  2. 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 in the document and any reference of :host and :host-context will be replaced accordingly.

The above example style will look like the following 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>

It is very important to start any style selector with :host to avoid any affecting other elements on the page if you intend to use the none mode.

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.