Avatar of Mojtaba Seyedi
Mojtaba Seyedi on (Updated on )

The :defined pseudo-class selector is part of CSS Selectors Level 4 specification allowing you to target custom elements created with the Web Components API and defined in JavaScript. Also, this selector matches any standard element built into the browser.

/* Select a specific custom element */
my-element:defined {
  visibility: visible;

/* Select any defined elenent, custonm or not */
:defined {
  visibility: visible;

/* Select an undefined custom element */
my-element:not(:defined) {
  visibility: hidden;


The CSS :defined pseudo-class has the following syntax:

<element-selector>:defined {}

The <element-selector> can be absent or it can be any valid CSS selector that targets a custom element. Leaving it absent targets any custom element, while declaring a selector selects that specific custom element.

/* Selects any defined element */
:defined {}

/* Selects a custom <spicy-sections> element */
spicy-sections:defined {}

Yes, there is indeed a custom element out there called <spicy-sections> that someone has put out.

What’s a defined element?

Defined elements are related to web components, a web feature that allows us to create our own custom elements — like <pizza-pie> — that bundle all the things needed for that element to “work” such as its HTML, CSS, and JavaScript. We often will simply refer to “web components” when we’re talking about custom elements, so you’ll hear the two terms used interchangeably.

So, with that, a defined element is a custom element that has been created using the Web Components API and registered using the customElements.define() method in JavaScript.

But that custom element is only “defined” once its JavaScript has loaded. Until then, the element is in an undefined state. That’s different from “built-in” elements, which is a fancy term for any standard web element, such as <h1>, <p>, <main>, <div>, <span>, etc.

It’s important to note that built-in elements, unlike custom elements, are always considered to be defined. That’s counter to how custom elements start undefined and only become defined once properly registered. This means that the CSS :defined pseudo-class can be used to selectively apply styles to custom elements that have been defined and are present in the HTML.

For example, the h1:defined will always match a standard, built-in <h1> element. If our <pizza-pie> element isn’t yet in the HTML because it’s waiting for JavaScript or is injected into the HTML at a later time by design, then it is not selected — at least until it finally does load.

What’s all this good for, you might ask? This way, we can style custom elements based on their defined status. Let’s turn to an example to see how that works.


To demonstrate how to use the :defined CSS selector, let’s take the example of a two-up web component. This component compares two DOM elements, such as two images. However, suppose a user loads the page with a slow internet connection. In that case, the custom element’s JavaScript and CSS may take a bit to load and result in the element looking broken on the page until things load in, such as the two images stacked one on top of the other rather than side-by-side.

No one wants that sort of janky experience and that’s what :defined is designed to fix. Using it allows us to hide the custom element until it’s fully loaded, preventing jank that may occur while the custom element’s JavaScript is in the process of loading.

two-up:not(:defined) {
  visibility: hidden

two-up:defined {
  visibility: visible

In the above code, we use the :defined selector to set the visibility of our custom element to visible once it’s fully defined. Notice how we’re able to set the element’s visibility to hidden by combining the selector with the :not() function. By doing this, we’re saying, “If the <two-up> component is not defined, then please hide that guy from view.”

And when we do that, even users on slower internet connections can enjoy a smooth, jank-free experience as the page loads. This strategy could also provide you with performance benefits, as cumulative layout shift (or CLS) is a metric used in Core Web Vitals that influences search rankings. The less shifting the page does as it loads, the better CLS.

Check out the following video to see the result. Also, notice how the :defined selector gets applied to the two-up web component during its defined and undefined states.

Browser support

Support is awesome across the board.

More information