Handling Styles In Web Components
January 2025
Introduction
I have two main struggles with web component styling:
-
Having to adjust a page's stylesheet
to do something like apply
display: block;
to a custom element - Having to add a custom CSS property to a page's stylesheet to set a style inside the component
Both cases require messing with the parent page's CSS. That breaks my mental model of the encapsulation. This page looks at a self-contained approach.
Overview
I'm using two techniques to avoid having to adjust a component's parent page CSS:
-
Use the
:host
selector in the component to set styles on the custom element itself - Pass CSS variable names (or specific values) into the component via attributes
The only thing necessary after that is to set optional attributes inside the custom element itself.
The Component File
Here's a full example (which also powers the examples on this page):
customElements.
We'll dig into the details in a moment. First, here's a look at how it's used:
Basic Usage
The component's filename is component.js
.
It's included via this tag:
Example 1
The component provides a custom element named alans-wc
that outputs the word "Ping" on top of
a background color. The basic use looks like this:
Code
Result
The component defaults the background color
to blue
. The text color is
inherited.
Example 2
Adding a :bg-color
attribute
to the custom element sets the color behind
the text:
Code
Result
Using CSS Variables
Even better than being able to pass explicit colors is the ability to pass CSS variables defined by the parent page's styles.
Example 3
This page includes the following in its CSS:
}
--accent-color-1
is what's used for the
borders of the example code/result blocks.
We can set a component to use the
same variable for its background like this:
Code
Result
The name of the variable is passed into a function that builds the stylesheet for the component's shadowroot. Once there, it grabs the associated value from the parent page's styles like a regular CSS variable call.
Example 4
Component instances are isolated from each other. We can use different variables for each one:
Code
Result
Example 5
The same technique can be used with multiple
styles. Here's an instance that
uses a text-color
in addition to the
bg-color
:
Code
Result
Looking At The Code
The component's connectedCallback()
calls four functions:
getAttributes()
, getColors()
,
generateStyles()
, and addContent()
.
They do the following:
getAttributes()
This function slurps up all the attributes from the custom element.
It works by looping through every
attribute and pulling out the ones that
start with a (:
) colon.
getColors()
The getColors()
function works
by creating a this.colors
variable
and pre-populating it with default colors. The
next step is to loop through the attributes
to see if any colors were set in the instance
and update them if they were.
generateStyles()
generateStyles()
creates
a new stylesheet using the this.color
values and adds it to the shadowRoot.
The styles are applied via the
:host
selector. That what
applies them to the <alans-wc>
custom
element itself.
addContent()
Finally, the addContent()
function
adds the HTML for the component to
the shadowRoot via a template.
Conclusion
Styling web components was one of the major tripping blocks that led to false starts when I started using them. This approach lets me adjust their styles with a mental model that I can easily grok.
Endnotes
- This approach doesn't attempt to update the styles if something on the page adjusts the attribute values. Look into Responding to attribute changes section of Using custom elements for details on how to do that.
- Thanks to folks on the Frontend Horse, Shot Talk Show, and Web Components Community Group discords for providing feedback to help improve this post