In this post, we are going to take a look at a new JavaScript library called styled-web-components which enhances your plain and simple web components by giving them super-powers via additional custom style properties.
Styled-web-components is light weight with no external dependencies. It has got convenient and shorthand property names like m,p,mx,px
and so on. It doesn't require a build or compilation step. You can directly use them in your browser since it supports ES module format.
Inspiration
The real inspiration for this library came from styled-system. Styled System is a collection of utility functions that add style props to your React components and allows you to control styles based on a global theme object with typographic scales, colors, and layout properties.
If we give provision to add style props to our web components automatically, then they become flexible enough to build any complex UI layouts in the browser. That's what styled-web-components is all about.
Let's see this with an example. Let's assume you want to create a Flexbox layout with web components and you are planning to build a separate layout or container component something like my-flex
. And you use it to compose your flex items to get a flex layout something like this:
<my-flex>
<div></div>
<div></div>
</my-flex>
And you can style your my-flex
component with something like this:
: host {
display: flex;
}
Let's say you want to make some changes to your flex layout like adding some background-color or border-radius to your flex container. One way of doing that is to add the respective CSS properties to your my-flex
component style declarations.
: host {
display: flex;
background-color: yellow;
border-radius: 20px;
}
This is a serious mistake and an anti-pattern. Because you are basically violating the Re-usability and Single-Responsibility Principle (SRP) of the my-flex
component. The job of my-flex
component is only to provide a flexbox container for it's children, nothing more, nothing less. It has only one thing to do and it has to do it well.
And also this approach might not scale well if we want to have more styling for our layout components and what about if we want to build a more generic layout component, something like my-box
with a block
display value and can be used for a lot of use-cases with a lot of style customization just like how the typical example of a React styled-system component looks like:
<Box
fontSize={4}
fontWeight='bold'
p={3}
mb={[ 4, 5 ]}
color='white'
bg='primary'>
Hello
</Box>
Composition is the key
So how does this library achieve this. How can you add more custom style properties to your web components? Since all our web components aka Custom Elements are just plain JavaScript classes extending from the common base class HTMLElement
, if we can make our components extends from multiple classes with different style props, then all the style props of the parent classes will become part of our component.
But we have one problem, multiple inheritance is not possible in JavaScript, which means this code won't work:
class Example extends ClassOne, ClassTwo {
constructor() {
}
}
Then I came across this Stack Overflow post about this issue of extending multiple classes in JavaScript.
I've done most of my research on this on BabelJS and on MDN (which has no information at all), but please feel free to tell me if I have not been careful enough in looking around for more information about the ES6 Spec.
I'm wondering whether or not ES6 supports…
The recommended solution to multiple inheritance is basically class composition by using class factories just like below:
// base class
class A {
foo() {
}
}
// B mixin, will need a wrapper over it to be used
const B = (B) => class extends B {
foo() {
if (super.foo) super.foo(); // mixins don't know who is super, guard against not having the method
}
};
// C mixin, will need a wrapper over it to be used
const C = (C) => class extends C {
foo() {
if (super.foo) super.foo(); // mixins don't know who is super, guard against not having the method
}
};
// D class, extends A, B and C, preserving composition and super method
class D extends C(B(A)) {
foo() {
super.foo();
}
}
Installation
You can install the library via npm using the below command.
npm install @rajasegar/styled-web-components
via CDN:
You can also directly embed the library from a CDN via the script tag.
<script src="https://unpkg.com/@rajasegar/styled-web-components@2.0.2/dist/styled-web-components.min.js"></script>
Usage
Create your own Custom element with composing multiple style props like Spacing, Color and Typography styles.
import { SpaceProps, ColorProps, TypographyProps } from 'styled-web-components'
class SWBox extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
const template = document.createElement('template')
// This is very important, your template should have a style tag with :host selector
template.innerHTML = `<style>
:host { display: block; }
<style>
<slot></slot>`
this.shadowRoot.appendChild(template.content.cloneNode(true))
}
}
customElements.define('sw-box', TypographyProps(ColorProps(SpaceProps(SWBox))))
Use your newly defined custom element in your HTML
<sw-box py="2em" color="red" bg="yellow" font-family="sans-serif">
<h1>Hello world</h1>
</sw-box>
Flex box custom component
Let's take a look at an example of creating our Flexbox layout component.
import { FlexboxProps } from 'styled-web-components'
class SWFlex extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
const template = document.createElement('template')
template.innerHTML = `<style>
:host { display: flex; }
</style>
<slot></slot>`
this.shadowRoot.appendChild(template.content.cloneNode(true))
}
}
customElements.define('sw-flex', FlexboxProps(SWFlex))
Usage
And this is how we can make use of our newly created sw-flex
component
<sw-flex justify-content="center" flex-direction="row-reverse">
<sw-box m="1em" width="500px" py="2em" color="red" bg="yellow" font-family="sans-serif" text-align="center">
<h3>Section 1</h3>
</sw-box>
<sw-box m="1em" width="500px" py="2em" color="red" bg="yellow" font-family="sans-serif" text-align="center">
<h3>Section 2</h3>
</sw-box>
</sw-flex>
Grid custom component
This is an example for a Grid layout component
import { GridProps } from 'styled-web-components'
class SWGrid extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
const template = document.createElement('template')
template.innerHTML = `<style>
:host { display: grid; }
</style>
<slot></slot>`
this.shadowRoot.appendChild(template.content.cloneNode(true))
}
}
customElements.define('sw-grid', GridProps(SWGrid))
Usage
We can use the sw-grid
component by passing the style properties like grid-template-columns
and grid-gap
<h2>Grid demo</h2>
<sw-box m="2em">
<sw-grid grid-template-columns='100px 100px 100px' grid-gap="10px">
<sw-box bg="#444" color="#fff" border-radius="5px" p="20px" font-size="150%">A</sw-box>
<sw-box bg="#444" color="#fff" border-radius="5px" p="20px" font-size="150%">B</sw-box>
<sw-box bg="#444" color="#fff" border-radius="5px" p="20px" font-size="150%">C</sw-box>
<sw-box bg="#444" color="#fff" border-radius="5px" p="20px" font-size="150%">D</sw-box>
<sw-box bg="#444" color="#fff" border-radius="5px" p="20px" font-size="150%">E</sw-box>
<sw-box bg="#444" color="#fff" border-radius="5px" p="20px" font-size="150%">F</sw-box>
</sw-grid>
</sw-box>
Let's see our components in action in this Codepen demo:
That's all about the styled-web-components. Give it a try, and let us know for any issues, feedback or improvements in the comments.
The source code is in Github
Top comments (0)