Giving Super-Powers to your Web Components
Tags: webcomponents, components, javascript, html
Published: 2021-10-11 10:26:43

In this post, we are going to take a look at a new JavaScript library called [styled-web-components](https://github.com/rajasegar/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](https://styled-system.com/). 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:
```html
<my-flex>
<div></div>
<div></div>
</my-flex>
```
And you can style your `my-flex` component with something like this:
```css
: 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.

```css
: 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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yk59sgzju2djlzdjwmz7.jpg)

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: 

```html
<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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dly6gbreeq9l4c20g12q.jpg)

But we have one problem, multiple inheritance is not possible in JavaScript, which means this code won't work:
```js
class Example extends ClassOne, ClassTwo {
    constructor() {
    }
}
```

Then I came across this Stack Overflow post about this issue of extending multiple classes in JavaScript.

{% stackoverflow 29879267 %}

The recommended solution to multiple inheritance is basically class composition by using class factories just like below:

```js
// 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.

```js
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
```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.

```js
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

```html

<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

```js
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`

```html
  <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:
{% codepen https://codepen.io/rajasegar/pen/qBjeWPz %}

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](https://github.com/rajasegar/styled-web-components)