Solved by StyleObserver: Element.matchContainer()

Martin Winkler published a package that polyfills Element.matchContainer to get notified in script when a Container Query matches/unmatches. Under the hood, it uses a StyleObserver.


This content originally appeared on Bram.us and was authored by Bramus!

Martin Winkler published a package that polyfills Element.matchContainer to get notified in script when a Container Query matches/unmatches. Under the hood, it uses a StyleObserver.

~

Window.matchMedia()

With Window.matchMedia() you can observe whether a Media Query matches the given document.

const mql = window.matchMedia("(max-width: 600px)");
console.log(mql.matches); // true when the document is ≤ 600px

Furthermore you can use it to observe changes, by listening to the change event on the returned MediaQueryList object.

const mql = window.matchMedia("(max-width: 600px)");
mql.addEventListener("change", (e) => {
  console.log(e.matches);
});

~

Element.matchContainer()

One of the open requests at the CSS Working Group is to have something similar to Window.matchMedia() but then for Container Queries. One of the suggested solutions is to have something like Element.matchContainer().

Today, Martin Winkler published a package that polyfills exactly that: match-container:

Element.matchContainer() is to @container what Window.matchMedia() is to the @media query CSS feature.

It allows you to do things like this:

const cql = $someElement.matchContainer("(width < 400px)");
cql.addEventListener("change", (e) => {
  console.log(e.matches);
});

Or this:

const cql = $someElement.matchContainer("(style(--my-property: some-value))");
cql.addEventListener("change", (e) => {
  console.log(e.matches);
});

Sweet!

~

StyleObserver under the hood

Peeking under the hood, I see Martin uses my technique from @bramus/style-observer: set up a transition on some custom properties and listen for their transitionrun event. These custom properties automatically get registered and applied on the registered Container Query.

When Element.matchContainer() is called the polyfill inserts a corresponding @container query into the CSSOM. The observed element is tethered to an attribute selector inside this query block by a corresponding data-* attribute, ensuring the target element is the only element matched by this selector. When the @container query matches a prepared CSS custom property is set on the observed element. A style observer (heavily inspired by Bramus’ StyleObserver) finally triggers the callback in the script code.

For example, if you observe "(width < 400px)", this piece of CSS automatically gets injected into the page.

@property --container-query-observer-bae45330cd3d4e0e96b60d26b57009b5-1740410397140-0 {
  syntax: "<custom-ident>";
  inherits: false;
  initial-value: --false;
}

@container (width < 400px) {
  [data-container-query-observer-bae45330cd3d4e0e96b60d26b57009b5-1740410397140-0] {
    --container-query-observer-bae45330cd3d4e0e96b60d26b57009b5-1740410397140-0: --true;
  }
}

The attribute you see there is also auto-generated and gets attached to the observed element.

Note that match-container only registers its internally used Custom Properties. When observing a <style-query> you must still register the property you are observing yourself. See the section on Custom Properties from @bramus/style-observer for details.

~

Demos

Here are two demos that use match-container. The first one uses a size query and the second one a style query. Whenever the container query matches/unmatches, the log gets prepended with a new entry.

See the Pen
Element.matchContainer() demo
by Bramus (@bramus)
on CodePen.

See the Pen
Element.matchContainer() demo – Style Query
by Bramus (@bramus)
on CodePen.

‼️ Don’t forget that container queries query a parent Element, so you need to attach .matchContainer on a child of the container (!).

Also don’t forget to apply container-type: inline-size when doing .matchContainer(<size-query>) or to register the custom property that you are observing when doing Element.matchContainer(<style-query>).

~

In Closing

It’s very nice to see that Martin got to leverage the technique from @bramus/style-observer to solve this longstanding request. It confirms to me once again that we need a native StyleObserver built into the Web Platform.

Get match-container here:

match-container on NPM →
match-container Source Code (GitHub) →

~


This content originally appeared on Bram.us and was authored by Bramus!


Print Share Comment Cite Upload Translate Updates
APA

Bramus! | Sciencx (2025-02-24T15:48:50+00:00) Solved by StyleObserver: Element.matchContainer(). Retrieved from https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/

MLA
" » Solved by StyleObserver: Element.matchContainer()." Bramus! | Sciencx - Monday February 24, 2025, https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/
HARVARD
Bramus! | Sciencx Monday February 24, 2025 » Solved by StyleObserver: Element.matchContainer()., viewed ,<https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/>
VANCOUVER
Bramus! | Sciencx - » Solved by StyleObserver: Element.matchContainer(). [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/
CHICAGO
" » Solved by StyleObserver: Element.matchContainer()." Bramus! | Sciencx - Accessed . https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/
IEEE
" » Solved by StyleObserver: Element.matchContainer()." Bramus! | Sciencx [Online]. Available: https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/. [Accessed: ]
rf:citation
» Solved by StyleObserver: Element.matchContainer() | Bramus! | Sciencx | https://www.scien.cx/2025/02/24/solved-by-styleobserver-element-matchcontainer/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.