Getting Started with Web Components: Building a Color Palette Generator

What are Web Components?

Web Components are a set of APIs that allow you to create custom HTML tags and use them alongside standard tags. They provide many advantages such as portability across projects, encapsulation of state, and access to a shadow DOM. Web Components are also functionally independent from frontend frameworks. You can use your components in a React, Angular, Vue, or vanilla project. For example, let’s say you wanted to create a simple feed of photos with related titles in a React app. Instead of writing some typical JSX like this:

photos.map(photo => {
return (
<div className="photo">
<img src={photo.url} alt={photo.altText}/>
<h1>{photo.title}</h1>
</div>
)
})

You can write a single, more straightforward tag like this:

<photo-list />

This component can then be used across multiple projects while maintaining consistent functionality and style. Using these components will also make your DOM concise and easy to read. In this example, we are going to be creating a color palette generator that is as simple to use in your HTML as <palette-generator />.

Here is how the component looks:

What You Should Know

To follow this article, a basic understanding of the DOM and how to add objects to the DOM through vanilla javascript is required. I try and explain anything else that may be confusing or unknown.

Constructing the Component

We are going to be building this example project in React. To make it easy to get up and running, we will be using create-react-app. create-react-app is a command line tool for quickly generating React apps with all the required configuration. If it is not already installed, install it using the command npm install react-scripts@latest. Then, reload your terminal window and navigate to your development directory. Run:

create-react-app web-components
cd web-components
yarn start

Once these steps have finished running, you should see a bare-bones react app pop up in your browser of choice. Open your project in VSCode (or some lesser IDE) and open up the App.js file. Replace the contents of that file with this:

import React, { Component } from "react";
import "./App.css";
class App extends Component {
render() {
return (
<div className="App">
      </div>
);
}
}
export default App;

This file is the main JS file for your app. The only other thing that will go into this file is your new <palette-generator /> tag.

Hit save, and your website should automatically update to a blank white screen. Now we get started on the fun stuff: building the actual web component. Create a new file in the same directory as your App.js file and name it ColorPaletteGenerator.js.

# 1
class ColorPaletteGenerator extends HTMLElement {
# 2
constructor() {
super();
  }
}
# 3
window.customElements.define("palette-generator", ColorPaletteGenerator);
export default ColorPaletteGenerator;
  1. To start, we will define our new class and make it extend the HTMLElement object. This line will allow the window to render our component just like a regular div, span, or table element.
  2. The constructor of the class is where we will open up our shadow DOM, create and style our elements, and add those elements to our newly created shadow DOM to be displayed to the user.
  3. These last two lines tell the window what to call our new component and what class will be responsible for rendering the component.

Next, we are going to need to install a package to do the color generation.

Navigate back to your project directory and enter yarn add color-scheme. This package will allow us to quickly generate some colors that we can use in our color palette generator. Next, add this line at the top of your ColorPaletteGenerator.js file:

import ColorScheme from "color-scheme";

And copy this function into your ColorPaletteGenerator class:

generateColors() {
const scheme = new ColorScheme();
scheme
.from_hue(Math.random() * 360)
.scheme("contrast")
.variation("soft");
const colors = scheme.colors();
return colors;
}

This function will generate an array of hexadecimal color strings that we can use to style our color palette generator.

From now on, we will be working in the constructor of the ColorPaletteGenerator class to create our component. Begin by adding this line directly after the call to super().

let shadowDOM = this.attachShadow({ mode: "open" });

This will create our shadow DOM and attach it to the instance of our ColorPaletteGenerator object. The mode in which you attach the shadow DOM is not relevent for this tutorial, but take a look at this article if you want to read more about it:

Next, we want to create a container div to hold each of the different colors that we will be generating. Copy the following code in directly after creating the shadow DOM.

let paletteContainer = document.createElement("div");
paletteContainer.classList.add("colorsContainer");

This creates a div and adds a custom class which we can use to style it later. You won’t see any update to your DOM yet, so don’t worry if nothing appears on your page. We need to attach this newly created div to the shadow DOM we created at the beginning before we will see it displayed. We will do this at the end of the constructor.

Next, let’s create a style tag which we can use to style the elements we are adding to the shadow DOM. Because Web Components are made to be compartmentalized, independent objects, we must add styles from inside the component itself rather than from an external CSS file. Add this line next:

let paletteStyle = document.createElement("style");

Finally, before we start creating our color divs, we need to generate some colors. Using the function we wrote previously, it’s as simple as:

const colors = this.generateColors();

Here is what your code should look like so far:

Next, we are going to be creating the divs that house our colors and text:

// You can change this constant to however many colors you'd like
let numberOfColorsToGenerate = 5;
colors.splice(0, numberOfColorsToGenerate).forEach((color, index) => {
// # 1
let swatchDiv = document.createElement("div");
  // # 2
swatchDiv.id = "colorSwatch" + index;
  // # 3
swatchDiv.classList.add("colorSwatch");
  // # 4
swatchDiv.innerText = `#${color}`;
  // # 5
shadowDOMStyle.innerHTML += `
#${swatchDiv.id} {
background-color: #${color};
color: ${contrastingColor(color, true)};
}
`;
  // # 6
paletteContainer.appendChild(swatchDiv);
});

This section of code will establish some colors to generate, loop through each color, and then take the following steps:

  1. Create a div element to house our color swatch and the name of our color.
  2. Set the id of the newly created div to a custom name so we can set the background color and color of text later.
  3. Add a class to style each color the same regarding size, layout, padding, etc.
  4. Set the inner text of the div to the name of the color to display.
  5. Next, we have to add the style to our divs so the background color and text color match our color object.
  6. Finally, we need to add our newly created color div to the container so it can be displayed.

We need one more piece of code for this loop to work, and that is our contrastingColor function we use to decide what color to make our text. This adds contrast to the text and makes sure that it stands our no matter what color it is laid over.

contrastingColor(hex) {
var r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16);
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? "#000000" :   "#FFFFFF";
}

This will make text appear light on a dark background and vice versa. We are so close to being done at this point, just a few more lines!

Our penultimate step is to add some general styling to our palette container as well as our color swatch divs. Copy and paste this code after your forEach over each of the colors.

paletteStyle.innerHTML += `
.colorSwatch {
height: 375px;
width: ${(1 / numberOfColorsToGenerate) * 100}%;
justify-content: center;
align-items: flex-end;
padding-bottom: 30px;
display: flex;
flex-direction: vertical;
font-size: 30px;
font-weight: bold;
}
.colorPaletteContainer {
display: flex;
flex-direction: row;
background-color: red;
width: 100%;
}
`;

This will style our palette container as well as our color swatches inside of the container.

Finally, we need to add the palette container as a child of our shadow DOM to display the contents. To do this, add the following lines after the paletteStyle code:

shadowDOM.appendChild(paletteStyle);
shadowDOM.appendChild(paletteContainer);

This will attach both the palette container and the style of the palette container to our shadow DOM on our HTMLElement object, which will subsequently be rendered to the user.

Save your file, and it should now be ready to use!

Go back to you App.js and add this line to the top of the file:

import "./ColorPaletteGenerator";

This will make the <palette-generator> tag available to use in your html. Finally, in your main render function just inside the App div, add this:

<div className="App">
<palette-generator />
</div>

Make one quick change to your App.css file by adding this line:

palette-generator {
width: 100%;
}

Now, save your file and you should see a beautiful color palette that reloads every time you refresh your page!

The final version of your code should look similar to this:

And that’s it! You now have a working color palette tag that you can use across any of your projects. The one caveat to this is that we are dependent on the package we used to generate our colors. However, we could easily write this ourselves to make our new component completely independent. It can then be copy and pasted from project to project or put in a shared repository to be updated all at once.

Make sure to check out the color generation library we used from npm and play around with the generateColors function to see what kind of cool palettes you can generate!