DEV Community

loading...
Cover image for Web Components in Style

Web Components in Style

Brantley Harris
Trying desperately to change the world for the better through *technology*... I know, it's probably a bad idea. he/him
・5 min read

Web Components are really easy to style. You can share styles between them at nearly zero cost, and they can still be styled from outside. They are easy to set up and progressively enhance. There are a throng of frameworks that use Web Components, and if you got into one, you'd know all this naturally. But it's really hard to understand coming from React, Vue, Angular, etc. So let's talk about it.

Baseline

Web Components are probably the most misunderstood technology right now on the web. It's no one's fault really, the tech is in an awkward place. The industry is caught in a React fever dream, and if not React, then surely Vue, and if not Vue then I guess Angular? All of these frameworks work in their own eco-system. And it's really hard to see what's going on outside of them.

Web Components are about bringing everyone out of the closed-systems to share with each other. It's really easy to make your own walled-garden in tech, and in fact, once you've created it, you have incentives to keep it going. It's a whole other magnitude more difficult to do the work to create standards that everyone can agree on and that build interoperability.

So let's go into what Web Components literally are. I'm obliged to go deep because of the misconceptions out there. There are three main browser features collectively called "Web Components":

Custom Elements

Being able to make your own elements, your own buttons, your own inputs whatever. The implementation is stupidly simple. You just create a new class extending HTMLElement, and then you register the name.

class MyButton extends HTMLElement {
   connectedCallback() {
       super.connectedCallback();
       console.debug("I just appeared on the page!");
   }
}

customElements.register('my-button', MyButton);
Enter fullscreen mode Exit fullscreen mode

Et voila you can use <my-button> anywhere in your HTML. Cool. BTW, you can use it in React and Vue also. It just works. Yes, we have to run JavaScript, but it's easy for this to be progressively enhanced because the browser treats it like a DIV until the javascript is loaded. You can still style it from the outside.

This implementation of components is about the most straightforward, simple thing you could think of. It's wicked fast because it's using the browser's native object system already, and it's completely full-featured. Also, when you inspect it in the browser, you see it, not some weird <div class="rg84823">, not some huge pile of indecipherable elements. Just <my-button>.

HTML Templates

HTML Templates is a way to define fragments of HTML in the DOM without them having to actually run on the page. This lets you do declarative things without having to use JavaScript.

Shadow DOM

The Shadow DOM is a way of encapsulating and hiding chunks of HTML. Even though <my-button> looks like a single element, it can have lots of sub-elements in its Shadow DOM. Just like the <select> element has its own inaccessible sub-elements like the dropdown arrow, your Custom Element can have its own little world.

Yeah, it's kind of a mind-fudge. Because you're very used to the DOM tree as being, well a tree. Shadow DOM turns it into a hyper-tree. The nodes can have their own sub-trees off the main tree. That has a cost, mentally. And, honestly, any good developer will naturally want to turn off their brain here because the complexity doesn't seem to have an immediate payoff. But rather than giving up on the concept of Web Components entirely, let's first remember that we don't have to use Shadow DOM to use Custom Elements, and secondly, maybe we can keep trekking on over the conceptual hill, and see what's on the other side.

One of the first things you'll find is that it lets you control how CSS affects the inner-workings of your component. This allows you to shield it from the mess above. Specifically, components become a boundary of the cascade, or in other words, the CSS for your component becomes scoped to it.

There are multiple ways to pierce in and control the style within a component. The easiest, when you are writing the component is to have shared styles. This is a set of CSS styles that components all rely on, a style library. The browser caches it, but gives it to each component. In LitElement you'd do something like:

const buttonStyles = css`
  .icon { width: 32px; vertical-align: middle }
`;

class MyButton extends LitElement {
  static styles = [buttonStyles, myStyles];
}

class YourButton extends LitElement {
  static styles = [buttonStyles, yourStyles];
}
Enter fullscreen mode Exit fullscreen mode

MyButton and YourButton will both use buttonStyles, and whatever else they want, without being changed by the CSS coming from above them.

Another is CSS variables, which if you aren't using, you really should. Components get the CSS variables from above them in the tree. It's common to define and publish with your component variables they support like --my-button-color. It becomes simple to change it from above.

We've also got CSS Parts, which lets the component author define pieces to alter.

And finally, since it's a simple JavaScript class, you can extend whatever element you want and hand it new styles.

With these systems, you'll find CSS to be much, much easier to manage. You are naturally pushed towards styling components separately, vs layout, utility, page-elements, etc. You'll find you aren't nesting nearly as much. And generally, you'll find it much easier to organize your styles. I don't even use sass/scss anymore, because I just don't need it.

Frameworks

Let's talk about the really big misconception. People seem to think Web Components are a competitor to frameworks like React, Vue, Angular, etc. NO! Web Components build a path for these frameworks to provide components for you. Ideally, as an end developer, you would never have to worry about Custom Elements or ShadowDOM except to understand some of the details of styling and encapsulation.

A lot of frameworks are doing this, but not the popular big three, who each have a vested interest in keeping themselves closed off. Check out the many ways to make a webcomponent, all of these frameworks have different strategies and systems, but all of them can be used anywhere, not only in their own garden.

Yes, you can use Web Components in a zero-dependency way, and that's a great option for things like date pickers or very specific one-offs. But it's not the main way you're supposed to consume or even create Web Components. It's just one of many options in an ever-expanding, open system.

Please ask me anything, I'll do my best to help: @deadwisdom

Discussion (9)

Collapse
addn2x profile image
David Trattnig

This is probably the best write-up on Web Components I've read so far. Thank you! It confirms my choice to use Web Components as an framework-agnostic approach to code an Open Source library as a toolset for custom web apps.

Do you have any opinions on using Svelte for coding Web Components?

Collapse
deadwisdom profile image
Brantley Harris Author

I think Svelte is really interesting because it has a compilation step. That is a double-edged sword because it means you are abstracted from the final component. The LitElement I make is directly inherited from an HTMLElement, so I have a much stronger feeling of being close to the metal, so to speak. BUT the compilation step means no dependencies, or at least very few. So your end product is maybe the lightest-weight component possible. Also, compilation lets Svelte do cool things that something like LitElement readily couldn't. Trade-offs.

Overall I think just use what appeals to you. Highly recommend trying a few different ones so you don't get yourself stuck in a mindset.

Collapse
billraymond profile image
Bill Raymond

This is an interesting article. I rarely use JavaScript, but this seems like one of the better reasons to use it. I have a whole bunch of questions, but if you don't mind, I would like to ask you a few high-level questions:

  1. Do you have a working example (CodePen?) of how one might incorporate the component into HTML?
  2. One of the things I really like about SASS/SCSS is mixins, especially for creating responsive sites (update the font, remove elements, etc.). Would this now be something you have to handle with JS logic?
  3. Do you have thoughts on best practices for organizing these components? For example, there could be many components on the page for menus, buttons, etc. Is there a best practice for naming the files and how you might store them in folders? I'm specifically thinking about some of the loose standards proposed for CSS, like this: elad.medium.com/css-architecture-f...
  4. How do you propose sharing these components easily? I am wondering, do you put them in Gists or package them in some way?

Thanks again!

Collapse
deadwisdom profile image
Brantley Harris Author • Edited
  1. Right, the awesome part about Web Components is you use it like any other element! So just like you'd use <button>My Button</button>, you can use <my-button>My Button</my-button>. You're really making your component part of the system, rather than sitting on top of it.

  2. Mixins are definitely where SASS is untouchable right now, and if you use them a lot, web components won't change this. That said, CSS variables help, and you can build the css with javascript, so you can do all sorts of things because you can make JS functions to return CSS. Mixins + web components is great because you can apply them at the component level specifically.

  3. Part of why you need so much organization for CSS is because you have to be thinking cross-functionally always. I highly recommend CubeCSS for an example. Web Components help you focus on the components individually, so you don't have to be worried too much about organization past what you'd naturally do with a file-system. For me, I find an organic process works best. I start with a folder just called /components, and then when I have too many components around a specific feature, e.g. message-stream, message-post, message-item, etc. I create a folder called /components/messages.
    \
    A more difficult process is figuring out what should be a component and what shouldn't be, or where one component begins and one ends. That's a lot harder and really just takes experience. But that's true no matter what you build with whatever tool you use.

  4. You can package them! The consensus seems to be monorepos with an npm package for each one. Check out material web components for an example: github.com/material-components/mat...

Collapse
billraymond profile image
Bill Raymond

Thanks, Brantley! I’ll play with this next week!

Collapse
addn2x profile image
David Trattnig • Edited

Some thoughts from my side:

on 3.) I'm using Svelte to write my components. There every component has it's own file, holding JS, HTML and CSS.
on 4.) In Svelte when the component bundle is build, everything is merged in a single JS file. On the target page you only need to include this file and instantiate individual components using their tag e.g.

Collapse
dannyengelman profile image
Danny Engelman

super.connectedCallback(); is a remnant from your Lit-Element (or other base Class) script. Not required when you extend straight from HTMLElement

Collapse
deadwisdom profile image
Brantley Harris Author

Ah yes, thanks.

Collapse
nuxodin profile image
Tobias Buschor

Thanks for this article.
Here is a registry where you can check and add the namespace for your framework.
github.com/nuxodin/web-namespace-r...