Skip to content

[css-contain] Similar to window.matchMedia(), Container Queries should have a similar method #6205

Open

Description

@clairebaire

CSS Media Queries have a method on the Window interface (window.matchMedia()) giving a script the ability to listen for media query changes. I propose that CSS Container Queries has the same functionality. However, I think this introduces some complexity, and therefore I'd like to present two thoughts for consideration:

Proposal 1

A new method on the HTMLElement interface possibly called matchContainer() or something similar. Reason it wouldn't be on the Window is due to the nature of container queries - not being based upon viewport size, but size of the container. However, this should be influenced by the question still pending, which is what is the default behavior of a container query when there is no contain property on any parent element.

Proposal 2

A new Observer of sorts, similar to ResizeObserver or MutationObserver where it's a bit more clear that it is listening for any kind of container change, and that it relays the container as a member of a ContainerQueryList. This would make it not pollute the HTMLElement interface, and instead be a DOM method. This would solely be used to identify when a container query is activated, and which items are affected by that activation.

Admittedly, the second proposal seems very similar to what ResizeObserver does already, but I feel it misses the connection that CSS Contain Level 3 is attempting to solve. This could be something that might be added to ResizeObserver, but might also be its own API.

Thoughts?

Activity

  1. andruud commented on May 3, 2021

    @andruud
    Member

    but I feel it misses the connection that CSS Contain Level 3 is attempting to solve

    Why isn't the existing ResizeObserver (as-is) good enough to listen for resize events on containers?

  2. css-meeting-bot commented on Oct 6, 2021

    @css-meeting-bot
    Member

    The CSS Working Group just discussed Similar to window.matchMedia(), Container Queries should have a similar method, and agreed to the following:

    • RESOLVED: Add a matchContainer method as proposed in issue. Keep issue open to decide if need new observer or if we can reuse the existing
    The full IRC log of that discussion <dael> Topic: Similar to window.matchMedia(), Container Queries should have a similar method
    <dael> github: https://github.com//issues/6205
    <dael> miriam: Idea is have something similar for container queries. Claire (sp?) has provided a few options. Would be great to resolve on one. Or something else
    <dael> astearns: 3 options: new method. new observer. reuse resize observer
    <dael> fantasai: Possible use cases for both approaches. Could start with defining matchContainer since that's easy
    <dael> astearns: You're thinking we would need matchContainer and either new observer or reuse resize?
    <dael> fantasai: Yeah
    <dael> astearns: Anyone with opinions about adding matchContainer?
    <dael> TabAtkins: This would allow style queries in the matchContainer since they're under container query umbrella?
    <dael> fantasai: Yeah
    <dael> TabAtkins: Nice. Good
    <dael> astearns: Prop: Add a matchContainer method as proposed in issue. Keep issue open to decide if need new observer or if we can reuse the existing
    <dael> astearns: Objections?
    <dael> RESOLVED: Add a matchContainer method as proposed in issue. Keep issue open to decide if need new observer or if we can reuse the existing
    <fremy> FWIW I will not join the second call, I am going to bed, I think most issues I was involved in have been discussed already, or I left a comment with my thoughts. Talk to you all next week!
  3. jogibear9988 commented on Jan 11, 2023

    @jogibear9988

    Is there anything new about this? We created a Designer with CSS parsing support (https://github.com/node-projects/web-component-designer) where we would need the "matchContainer" API to find the mathcing container queries

  4. seedee commented on Jun 6, 2023

    @seedee

    Wouldn't just checking a container element's offsetWidth when the window resizes be functionally the same as container query?

  5. jogibear9988 commented on Jun 7, 2023

    @jogibear9988

    Wouldn't just checking a container element's offsetWidth when the window resizes be functionally the same as container query?

    no? container queries could not only check for a size....

  6. aradalvand commented on Jul 26, 2023

    @aradalvand

    I expected this to have already been implemented, such a shame.

  7. 7oque commented on Aug 18, 2023

    @7oque

    Just wanted to add that I ran into a use case where I wanted to be able to do;

    window.matchContainer('(aspect-ratio < 16 / 9)')

    Would have loved to be able to do this instead of writing some custom ResizeObserver which could potentially lead to performance issues if not managed mindful of re-paints for calculating/retrieving values.

  8. xamir82 commented on Dec 13, 2023

    @xamir82

    No movement on this yet? No offense but do we have to wait another 10 years for something as basic as this again?

  9. robrez commented on Feb 28, 2024

    @robrez

    Some experimenting with learning about container-query changes via JS

    • MutationObserver
      • Not possible, CSS cannot cause mutations to be observed; Thought perhaps pseudo-elements might work.. they don't.
    • IntersectionObserver
    • ResizeObserver
      • Viable by toggling any css property. I'm unsure if the CSS engine will always run prior to the resizeObserver callback
      • Example: https://codepen.io/robrez/pen/yLrLyKE
      • Note that many would just use the ResizeObserver w/o any interaction w/ an underlying container query css policy... but the point of this thread is to have something similar to mediaMatch. The things you can do in CSS might be tricky to otherwise detect in JS
      • Note also that Element.matches seems like a reasonable instictive "let me try this"... but it will not tolerate any container query
  10. 7 remaining items

  11. jogibear9988 commented on Nov 7, 2024

    @jogibear9988

    i think the other similar APIs like matchMedia and matches are on the coresponding object, so this also should be.

    I think it the browser vendors should implement this in the near future. We have container queries but no API to check if a element matches...

  12. alienself commented on Dec 21, 2024

    @alienself

    I was also surprised that there wasn't such api but that being said it can be very easily polyfilled using an invisible iframe in case you need this feature today:

    https://github.com/tomhodgins/element-match-media/

  13. teetotum commented on Feb 24, 2025

    @teetotum

    I created a polyfill for Element.matchContainer()
    It supports size queries and style queries and uses the CSS feature under the hood so it will only work in browsers that already support the @container queries CSS feature. All it really does is only adding the scripting API.

  14. prantlf commented on Jun 12, 2025

    @prantlf

    Wouldn't just checking a container element's offsetWidth when the window resizes be functionally the same as container query?

    no? container queries could not only check for a size....

    ... and it should be possible to optimise container queries better than the resize observers. The change event on the container query is triggered only when it starts or stops matching. No need to throttle the observer event handlers, which can be triggered very often.

  15. jogibear9988 commented on Mar 5, 2026

    @jogibear9988

    We seem to have progress in chrome:
    https://issues.chromium.org/issues/40887402

  16. jamesnw commented on Mar 5, 2026

    @jamesnw

    Will matchContainer() match on children elements of a container, the container itself, or both?

    For example, with anchored container queries, I want to know when the applied fallback changes of the anchored element itself. It would be awkward to have to call matchContainer on a container's child (and not all anchored elements have children, necessarily).

    I can imagine cases where you would want to listen on a container's child as well.

  17. lukewarlow commented on Mar 5, 2026

    @lukewarlow
    Member

    I have use cases where I would want to match on the container itself not just children.

  18. jogibear9988 commented on Mar 5, 2026

    @jogibear9988

    I think you hand over to this function only the container query string (same as you do with match media), so it could only work on the container

  19. jamesnw commented on Mar 6, 2026

    @jamesnw

    @teetotum If I understand it correctly, your polyfill only works on children of containers, and not on the containers themself. Do you have any insight into use cases for matching on children?

    I suspect there are use cases where you don't know what element is creating the container, but want to know when the match changes. We could add something like Element.findClosestContainer('inline-size').matchContainer('min-width: 500px'), but that wouldn't support use cases where the container itself changes.

    I think you hand over to this function only the container query string (same as you do with match media), so it could only work on the container

    If the function accepted a container name somehow, would that allow it to work on children? Or are there other things I'm not thinking about?

    Potentially we could have a second function matchContainerParent() that could be for the children. This function could also emit an event for when the container changes, even if the match doesn't.

  20. teetotum commented on Mar 9, 2026

    @teetotum

    The CSS @container query mechanism (setting aside the proposed workings of the .matchContainer() scripting API for a moment) can match an element when one of its ancestors (the container) meets the query condition. The container must be an ancestor, it can not be the element itself, posing as its own container.
    This is by design; otherwise you would be able to write cyclic query conditions and style rules:

    .thing {
      container-name: the-thing-itself;
      --matches: true;
    }
    
    @container the-thing-itself style(--matches: true) {
      .thing {
        --matches: false;
      }
    }

    My understanding of the proposed .matchContainer() scripting API is, that it would behave exactly as its CSS counterpart. Nay, it must behave exactly so! Anything else would lead to great confusion and probably bugs. You could have some elements that are queried and styled via the CSS feature, and simultaneously have a programmatic query listener via .matchContainer() that observes the exact same query (for example to process the UI state further inside a React component which has some conditional behavior). Those two different ways to listen to the exact same query in the exact same DOM must match in the exact same way so the results are in sync. They must not differ.

    There seems to be some confusion what role the element has in element.matchContainer(...) so let me try to clarify: the element is the descendant, not the container. An eligible container is searched in the ancestor chain of that element; when a container is found that is eligible (its container-type corresponds to the query type, container-name matches if one is required by the query) the condition is checked, and if met, the query reports matches as true. This means element is currently in a container that fulfills the query condition.

    @jamesnw My polyfill does not enforce or assume any descendant and ancestor relationship for the watched element; so for the unlikely case that one day a container query type should be added to the CSS standard that would allow an element to form its own container, then this should just work fine through the polyfill.

    You contributed a testcase to the polyfill recently; so I believe I know your use case for needing to match an anchored query for a custom styled <select> element. You don't need to pick the first <option> to get a reference to a descendant, you can use the dropdown element itself and it doesn't feel hacky anymore. It seems when a select is set to appearance: base-select i.e. custom styled the browser provides a popover that acts as a container, having <div class="dropdown"> (or whatever you put as content) as its child.

          <select>
            <button>
              <div>
                <selectedcontent> </selectedcontent>
              </div>
            </button>
            <div class="dropdown">
              <option value="carrot">Captain Carrot</option>
              <option value="garlick">Magrat Garlick</option>
              <option value="dibbler">Cut-Me-Own-Throat Dibbler</option>
            </div>
          </select>
    const watchingEl = document.querySelector('.dropdown');
    watchingEl.matchContainer(...
  21. Loirooriol commented on Mar 9, 2026

    @Loirooriol
    Contributor

    Yeah, iframe.matchMedia() checks the parent document, not the nested one. So here it's the same.

    Also, consider @container (width > 30em), (height > 40em): width and height can even query different containers. So it's not clear what this means if the context element was supposed to be "the" container, especially if if only contains in one or zero axes.

    For observing the element itself, better use ResizeObserver instead, and if needed for style queries or such, consider new observers in different issues (e.g. ComputedStyleObserver in #8982)

  22. jamesnw commented on Mar 9, 2026

    @jamesnw

    You don't need to pick the first <option> to get a reference to a descendant, you can use the dropdown element itself and it doesn't feel hacky anymore.

    That doesn't seem to work in my testing, but was the first thing I tried. Even then, in that example the container itself is set on the ::picker() pseudoelement, not div.dropdown.

    Also, consider @container (width > 30em), (height > 40em): width and height can even query different containers. So it's not clear what this means if the context element was supposed to be "the" container, especially if if only contains in one or zero axes.

    I'd imagine a matchSelfAsContainer() (bikeshed) function would check if the element itself is an established container matching that. So If it has height and width, but only has container: inline-size, it would throw.

    Then, a matchInsideContainer() function would check if the element is inside a matching container. I think there's probably need for both.

    I suspect that anchored container queries would mostly use matchSelfAsContainer(), as it is more about state than context, and other queries would mostly use matchInsideQuery().

  23. DevSDK commented on Mar 12, 2026

    @DevSDK
    Contributor

    Ah, thanks for clarifying that, everyone.

    According to the css-conditional-5, it describes about figuring out the container from the ancestor.

    For each element, the query container to be queried is selected from among the element’s ancestor query containers that are established as a valid query container for all the container features in the . If the contains unknown or unsupported container features, no query container will be selected for that . The filters the set of query containers considered to just those with a matching query container name.

    I initially thought this api is likely matchSelfAsContainer. Maybe it is good to mention this on the spec.

    FWIW, If we need like matchSelfAsContainer maybe, we can remove the word container from the API and probably this off the topic.

  24. DevSDK commented on Sep 30, 2026

    @DevSDK
    Contributor

    Did we resolve on having an event for this? I don't think so? And I think firing events based off layout needs a lot more thought.

    Originally posted by @emilio in #13551 (comment)

    #13551 adds matchContainer() without the change event, so we still need to decide how authors observe changes, which the resolution left open. Options I see:

    • A change event on ContainerQueryList, matching matchMedia(). The dropped spec text is in 1d44ed7, and loop termination is tracked in [css-conditional-5] Element.matchContainer has no termination rule for change event listeners #14430. Chromium has a prototype behind --enable-blink-features=ElementMatchContainer if anyone wants to try it, and there's a tentative WPT.
    • A new observer, as the resolution mentioned.
    • Reusing an existing observer. @robrez looked into this: MutationObserver doesn't observe CSS changes, and IntersectionObserver and ResizeObserver only work through workarounds like toggling a property on a dummy element. ResizeObserver also misses style() and scroll-state() changes, which can flip without a resize.

    Thoughts?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions