Drop #227 (2023-03-24): Weekend Project Edition

The Curious Case of Web Components

Y’all have likely noticed a “web” subtext this week (readers of the Daily Drop are quite discerning and keen-eyed, after all). We’re going to continue that through the weekend with a challenge to folks to get curious about and create one or more Web Components. Read on to see both “why” and “how”!

Becoming A Composer

black framed panto-style eyeglasses beside black ballpoint pen

Web Components are a set of web platform APIs that allow you to create reusable custom elements with encapsulated functionality. They solve the problem of reusability and maintainability in web development by enabling modular, self-contained components. While they live in the land of HTML, CSS, and JavaScript, they are not — depending on when you entered the web — your parent’s HTML, CSS, and JavaScript.

Web Components were first introduced into web standards in 2011, when a group of engineers at Google proposed a set of APIs to enable the creation of reusable, encapsulated UI components that could be used across different web applications. The proposal included three main technologies:

The first specification for Web Components was published by the W3C in 2013, and included the Custom Elements and Shadow DOM APIs. The HTML Imports specification was not included in this initial release, but was later added in 2014 as a separate specification.

Since then, the Web Components specifications have been updated and refined, with new features and improvements being added over time. You can always find the version of the Web Components specifications (or any spec) on the W3C website.

One compelling reason to dig into Web Components is that they are increasingly being used across the internets:

Another, is that many of you do write some HTML/JS/CSS now, and, I posit you who do not will eventually have to. With greater adoption, being able to at least grok Web Components will make it easier to dig into other’s projects or use third-party examples.

But, 👀 is believing, so let’s do more than talk about Web Components and show you one!

Crafting Components

person making pot

So, Web Components provide:

  • encapsulation: they have isolated styling and functionality which reduces CSS/JS conflicts in a project

  • reusability: these components can be used across projects, promoting consistency and reducing development time

  • interoperability: they’re compatible with many modern JS libraries/frameworks

What does some of that look like? Well, what you will build, here, looks like this:

I’ve been bootstrapping the use of Observable in my team where I work (GreyNoise Intelligence), and set up some of our (non-seekrit) data to be used in a public “lunch & learn” notebook (https://observablehq.com/@greynoise/greynoise-l-and-l-what-is-observable). We’ll use that (since it’s at a CORS-friendly path on my personal host) to make a “GreyNoise Tag” web component that lets us specify just a tag identifier (slug) to our custom tag. That will ultimately populate a “card” with selected info I’ve put in the JSON.

You can see it at https://rud.is/daily-drop/web-components/, where there are three files:

The CSS file is not needed, but it handles light/dark mode, so I don’t blind the dark-mode folks or make folks who prefer light-mode live in darkness.

The index.html is super-basic:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Daily Drop Web Components Addendum</title>
<script type="module">
import { TagCard } from "./tag-card.js"
customElements.define("tag-card", TagCard);
</script>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<h1>GreyNoise Tag Cards</h1>
<ul>
<li><tag-card tag-id='brown-university'></tag-card></li>
<li><tag-card tag-id='apache-http-server-path-traversal-attempt'></tag-card></li>
</ul>
</body>
</html>

You can see we define a custom <tag-card> (Web Components need dashed-names) and we supply the id (tag “slug”, really, but that’s four whole characters when “id” is just two!).

Our component is going to turn that into a lovely (basic) “card”, complete with its own style.

We’re also using in-browser JS modules because we are not monsters.

I tried to keep tag-card.js manageable, but I’m sure Substack will be brutal to it.

Comments explaining what’s going on are inline, but please drop a comment to the post if they’re not helpful.

/**
 * Welcome to tag-card.js
 * 
 * @module TagCard
 */

// load up the data since we don't need to make
// multiple requests (it's small)
// export in case we want to use it elsewhere
export let gnTags = {};

await fetch('https://rud.is/data/tags.json')
  .then(response => response.json())
  .then(data => {
    gnTags = data
  })
  .catch(error => console.error(error))

// Each tag looks like this:

// {
//   "id": "bace3ec5-3932-431b-ae4c-be73a462a195",
//   "label": "BROWN_UNIVERSITY",
//   "slug": "brown-university",
//   "name": "Brown University",
//   "category": "actor",
//   "intention": "benign",
//   "description": "IP addresses with this…"
//   "references": [],
//   "recommend_block": false,
//   "cves": [],
//   "created_at": "2020-04-07",
//   "related_tags": []
// }

// doing things in the shadows(DOM)

// creating a template to fill in. this one is super basic
const template = document.createElement('template');

// we use inconsolata so i'm just using it to show styling
template.innerHTML = `
<style>
@import url('https://fonts.googleapis.com/css2?family=Inconsolata');
:host {
  font-family: Inconsolata, monospace;
}
</style>
<div>
</div>
`

// OUR COMPONENT!
// it's just a class that is now a SUPER HTMLElement.
// We're _so_ much better than that `<div>` dude.
export class TagCard extends HTMLElement {

// standard component constructor
// this gives us our shadow dom
constructor() {
  super();
  this._shadowRoot = this.attachShadow({ 'mode': 'open' });
	
  this._shadowRoot.appendChild(template.content.cloneNode(true));
}

// what's going to get called when we add an element to a page
connectedCallback() {

// get the tag we asked for from the tags db
const ourTag = gnTags.metadata.filter(d => d.slug == this.tagId)[ 0 ]
		
  // find our div
  this.$card = this._shadowRoot.querySelector('div');

  // add an h3 element
  let $tagName = document.createElement('h3');
  $tagName.innerHTML = `${ourTag.name}`;
  this.$card.appendChild($tagName);

  // add a p element
  let $description = document.createElement('p');
  $description.innerHTML = `<b>Description</b>: ${ourTag.description}`
  this.$card.appendChild($description);

}

// this is how we get access to what folks specify
get tagId() {
  return this.getAttribute('tag-id');
}

}

Yes, it is a bit more up-front work, but not much!! We:

  • In a JS file we build a template where our component will “live”

    • this can include styles, as you likely noticed

  • then we make a new HTMLElement that includes:

    • a constructor (for all our extra bits)

    • a “callback” which does all the hard work

    • one method to help access the tag attribute we’ll be using

  • then use it in the HTML

Our HTML is cleaner, and we can do all sorts of fun things with those components.

Please open Developer Tools and navigate the HTML DOM tree to see what like beneath those two tags.

Your Mission

astronaut in white suit in grayscale photography

At a minimum, please read up on the links I provided (above, and below). At the very least, you will be able to understand any code you see, and may even develop an affinity for some modern, popular frameworks that make use of components.

If you’d like a challenge, take my files (please!) and make a better tag card using whatever styling you like.

Pick some other subject to turn into a Web Component. Perhaps your favorite API? That way, you can try incorporating dynamic API calls into the connectedCallback.

Web Component Extra Reading List

FIN

This one is a bit more involved than the previous ones, but I truly think most folks who have to deal with technical bits in their daily jobs will get something out of a peek into how Web Components work. Have a lovely weekend! ☮

#webcomponents #css #html #javascript

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.