A Nice Vanilla App Archicture Using Web Components and CSS Module Scripts

Chris Coyier Chris Coyier on

It’s an established Good Idea™ that building digital interfaces of any kind is best done by building components and then piecing together the interfaces from those components. This can be sliced and diced a lot of ways, but generally: a component is a reasonable independent peice of what that interface needs. When it comes to websites, things like a header, footer, grid, card, button, etc. A design system, as it were. See concepts like Atomic Design.

A nice by-product of the Rise of JavaScript Frameworks is that they solidified this idea. React, Vue, Svelte… you work with them by building components and composing them together. That’s their point.

I like the idea of userland tools like JavaScript frameworks pushing the boundaries, then the web evolving to not require those tools. So can we pull off a component-structured project without any build process or framework? We’re close.

This is an example of how I’d like to structure a website:

Those components (in our simple example, a button, card, and header) are all:

  • Inside a components folder, each with their own named folder (organized!)
  • Have a file for their template and logic
  • Have a separate CSS file

So like this:

This is the kind of logical grouping and isolation that makes sense to me in creating a component architecture. A more complex setup might have components with, say, .graphql files, their own images, tests, etc. The co-location is key to sanity.

Our components are JavaScript here because there is no concept of HTML includes yet, but also that web components are a generally nice way to handle this anyway, and they require JavaScript instantiation. We don’t need any framework to use web components (hence “vanilla app architecture”), but in the demo, I’ll use Lit (just a light helper library).

How do we integrate those component.js and component.css files? That question has long lingered for me. Bundlers can do this job. For instance, webpack just invented their own way of dealing with it. If you type import "./card.css"; in a JavaScript file that is processed by webpack, it’ll just know what you mean and ensure that CSS is loaded on the page somehow. Likewise, Vite just does it’s own thing:

Importing .css files will inject its content to the page via a <style> tag with HMR support.

That’s great and all, but we’re trying to go vanilla here. No bundler/build process. How do we import CSS like that?

Enter CSS Module Scripts

Good news: JavaScript has an answer to that question we just asked, and it’s called CSS Module Scripts.

Bad news: Only Chrome supports it. (WebKit bug; Firefox bug)

Google’s blog post on them (linked above) is one of the few pieces of information available about them, and it contains some incorrect syntax, so be careful there. It should look like this (the with keyword is correct, if you see assert that’s old/wrong):

import sheet from './styles.css' with { type: 'css' };Code language: JavaScript (javascript)

When you do that (in a supporting browser), sheet becomes a “Constructable Stylesheet” and then you can use it to, in our case, apply it to the Shadow Root of a web component.

class MyComponent extends HTMLElement {
  constructor() {
    super(); 
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.adoptedStyleSheets = [sheet];
  }
  
  ...Code language: JavaScript (javascript)

These “import attributes,” as I think they are called, can do other things. It’s much better supported to import JSON this way, like:

import sheet from './data.json' with { type: 'json' };Code language: JavaScript (javascript)

Lit

Using Lit, applying the styleheet (or, “the constructable stylesheet, as imported via CSS module scripts” to do the whole mouthful) is like this:

import {html, LitElement} from 'lit';
import sheet from './button.css' with { type: 'css' };

class My Component extends LitElement {
  static styles = [sheet];

  ...Code language: JavaScript (javascript)

Demo

Need front-end development training?

Frontend Masters logo

Frontend Masters is the best place to grow in your career as a developer. We have courses on all the most important front-end technologies and beyond, from React to CSS, to backend with Node.js and Full Stack.

7-Day Free Trial

Leave a Reply

Your email address will not be published. Required fields are marked *

$871,946

Frontend Masters donates to open source projects through thanks.dev and Open Collective, as well as donates to non-profits like The Last Mile, Annie Canons, and Vets Who Code.