Media Queries Are Not The Answer: Element Query Polyfill

Advertisement

Responsive Web design has transformed how websites are designed and built. It has inspired us to think beyond device classifications and to use media queries to adapt a layout to the browser’s viewport size. This, however, deviates from the hierarchical structure of CSS and characterizes elements relative to the viewport, instead of to their container.

Extensive use of media queries might be the answer for today, but it is not a viable long-term solution. Media queries do not allow for reusable modules that adapt based on their containers’ size.

What Is Responsive Web Design?

Responsive Web design is not limited to a set of technologies; rather, it is a different approach to designing and building websites. I, like many, took Ethan’s words1 about responsive Web design too literally and overlooked the essence of what was being said:

“Fluid grids, flexible images, and media queries are the three technical ingredients for responsive web design, but it also requires a different way of thinking.”

We have accomplished great things while embracing the stated “technical ingredients” for responsive Web design, but we have much room for growth when it comes to a “different way of thinking.” Thinking differently should affect not only how we design and build our websites, but also how we design and build the tools and technologies that our websites are founded on.

Modular Design

When I learned about how media queries could be used in responsive Web design, I was excited by the possibilities. However, it was not long before I learned of the limitations. Media queries are great for adapting layouts to various screen sizes, but terrible for creating modular designs2. Modular CSS is already hard enough, and media queries provide very little to no help. Truly modular layouts need to respond to the sizes of containers, not just to the viewport’s size. Media queries, however, are based on the viewport, rather than an element’s container. There is some hope for standard CSS on the horizon, in the form of a W3C working draft3, by allowing the cascade inheritance to be broken and resetting an element to its defaults. But what about media queries?

The @media Hack

Web developers are masters at taking something created for one purpose and using it to accomplish other things. The Web’s history is littered with examples of this, and media queries are no exception. Kudos to Ian Storm Taylor for writing down his thoughts in the article “Media Queries Are a Hack4.” Hacks are necessary on the Web to provide desired functionality until proper support is achieved, as well as to provide support to older browsers. The W3C states5, “By using media queries, presentations can be tailored to a specific range of output devices without changing the content itself.” The key word here is “can,” but just because you can do something, doesn’t mean you should… But do we have any other choice?

The Element Query

Introducing the element query. An element query is similar to a media query in that, if a condition is met, some CSS will be applied. Element query conditions (such as min-width, max-width, min-height and max-height) are based on elements, instead of the browser. Unfortunately, CSS doesn’t yet support element queries, but that shouldn’t stop us from dreaming, hacking and pushing for new standards.

Conceptual Example

Consider the following example, in which the navigation menu should become visible when it reaches a minimum width of 500 pixels (representing one of many potential syntaxes):

nav (min-width: 500px) {
    display: block;
}

Compare this to a media query in which the navigation menu’s visibility depends on the viewport’s width and needs to account for the padding and other declarations of parent elements:

@media all and (min-width: 520px) {
    nav {
        display: block;
    }
}

Now imagine having to build a modular component that needs to be placed in containers of various sizes on a single page. One current approach is to provide different theme classes (like .module--large) to trigger CSS in media queries. This, however, adds a lot of complications and requires a module to know how its parent will react to various viewport widths.

Issues: Invalid and Looping Conditions

There are several cases in which the CSS of an element query would invalidate the element query itself or create recursion. Hopefully, the browser would be able to detect these conditions and respond appropriately.

Consider the following examples.

Once an element reaches 500 pixels wide, it’s resized to 200 pixels, at which point the rule would no longer apply:

.element (min-width: 500px) {
    width: 200px;
}

Once an element’s width reaches 31.250 ems, its font size would be decreased, which changes the definition of the em unit:

.element (min-width: 31.250em) {
    font-size: 0.75em;
}

Once a container’s width reaches 450 pixels, the size of its child changes to 400 pixels, which would shrink the size of the container:

.container { float: left; }
.child { width: 500px; }
.container (min-width: 450px) > .child {
    width: 400px;
}

There are many other such examples, but you get the point: Element queries are not as simple as we had hoped.

Element Query Polyfill

Element queries seem pretty awesome, but they also have some real issues. To help sort these out, I’ve written a proof-of-concept polyfill. The polyfill has enabled me to understand how a browser might react to various conditions. As I got further along, I realized that the polyfill could hold real value in the Web community’s debate on element queries, and that some developers could even start using element queries today.

The elementQuery polyfill script is available on GitHub6 for you to use, fork and contribute to.

Selector Syntax

The syntax used in the previous examples caused limitations, so I updated the polyfill to support an attribute selector syntax. The ~= attribute selector7 checks whether the value is contained in a space-delimited list (supported in modern browsers above Internet Explorer 6).

The following examples show CSS rules using the syntax required for the elementQuery polyfill.

This rule queries itself for a single condition:

header[min-width~="500px"] {
    background-color: #eee;
}

This rule queries itself for multiple conditions:

header[min-width~="500px"][max-width~="800px"] {
    background-color: #eee;
}

This rule queries a parent for a condition:

header[min-width~="31.250em"] nav {
    clear: both;
}

How It Works

Unfortunately, the elementQuery polyfill requires JavaScript and the Sizzle8 selector engine (which is embedded in jQuery). When the document object model (DOM) is ready, elementQuery scans the document.styleSheets collection for any CSS rules that use elementQuery. When it finds a match, it extracts the following information:

  • Selector
    Such as header, ul > li.class
  • Query type
    min-width, max-width, min-height, max-height
  • Query value
    Such as 500px, 31.250em

elementQuery then uses this information to add or remove attributes from elements that match the given selector and query condition.

Expanded Support

Most browsers, but not Internet Explorer, don’t provide access to the contents of cross-domain style sheets, which causes issues when CSS files are served from a content delivery network. Additionally, parsing style sheets takes time (not much, though). So, I created two branches for elementQuery: master9 and prod10. The master branch includes the code for extracting the necessary elementQuery information (selector, query type, query value), and it also provides a selectors() function to export the information. The prod branch requires the information to be declared in JavaScript, which avoids the cross-domain file issue and the time required to parse the style sheets.

Here is an example of how to export elementQuery information using the master branch:

console.log(JSON.stringify(elementQuery.selectors()));

And here is an example of how to import elementQuery information using the prod branch:

elementQuery({"header":{"min-width":["500px","31.250em"],"max-width":["800px"]}});

Working Examples

I’ve put together a few working examples on CodePen (using the master branch) that you can experiment with or fork. I would love to see what other examples people create (which will probably be much cooler than mine). Just be sure to tag them with #elementquery11 so that others can benefit.

Be Creative

I didn’t write this just to get you to jump on the element query bandwagon, but rather to encourage people to think about how we can solve the problems that are limiting our medium. Let’s keep the discussion going and make the Web a better place. So, go wild and make cool stuff!

Further Reading

(Source of image on front page: Looking Beyond Common Media Query Breakpoints19)

(al)

Footnotes

  1. 1 http://alistapart.com/article/responsive-web-design
  2. 2 http://daverupert.com/2013/04/responsive-deliverables/
  3. 3 http://www.w3.org/TR/css3-cascade/#all
  4. 4 http://ianstormtaylor.com/media-queries-are-a-hack/
  5. 5 http://www.w3.org/TR/css3-mediaqueries/
  6. 6 https://github.com/tysonmatanich
  7. 7 http://css-tricks.com/attribute-selectors/#rel-space
  8. 8 http://sizzlejs.com/
  9. 9 https://github.com/tysonmatanich/elementQuery
  10. 10 https://github.com/tysonmatanich/elementQuery/tree/prod
  11. 11 http://codepen.io/tag/elementquery
  12. 12 http://codepen.io/tysonmatanich/pen/johpn
  13. 13 http://codepen.io/tysonmatanich/pen/wramd
  14. 14 http://codepen.io/tysonmatanich/pen/jIBpJ
  15. 15 https://github.com/tysonmatanich/elementQuery
  16. 16 http://www.matanich.com/2013/06/24/em-values-javascript/
  17. 17 http://filamentgroup.com/lab/element_query_workarounds/
  18. 18 http://www.xanthir.com/b4PR0
  19. 19 http://www.smashingmagazine.com/2012/10/24/beyond-common-media-query-breakpoints/

↑ Back to topShare on Twitter

Tyson Matanich is the lead developer on the Microsoft.com home page. His writings reflect his own opinions and don’t necessarily represent Microsoft’s. He focusses on frontend Web development but will do what’s necessary to get the job done. You can follow him on Twitter or read his blog.

Advertising

Note: Our rating-system has caused errors, so it's disabled at the moment. It will be back the moment the problem has been resolved. We're very sorry. Happy Holidays!

  1. 1

    It seems the repo has gone missing (404).

  2. 3

    Responsive design is dying for a solution to this. Yours it a wonderful step in the right direction.

    There are still a few hurdles that we need to solve. For instance, if classNames are dynamically added, which otherwise would have triggered these element-specific styles on DOM load or resize, then there’s no listener that can trigger a refresh.

    Additionally, if we set the “width” or other attribute of an element dynamically, then these element-specific media queries won’t trigger a rerender.

    I love that we can begin considering flexible designs at the element-level, but a fully-developed solution must accomodate these use-cases and others like it.

    Waiting with bated breath :)

  3. 4

    Explain: what does “access to the contents of cross-domain style sheets” mean?

  4. 6

    Another css element query alternative that supports more events than windows’s ‘resize’ event.
    https://github.com/marcj/css-element-queries

  5. 7

    I wrote something similar 3 Months ago but your syntax is definitely better – https://github.com/wemakeweb/element-queries

  6. 9

    This method is nice but it cost perfromance, especially on mobile devices. Manipulating DOMElements is resource consuming.

    • 10

      It would be a good idea to write mobile first css and, once you’re done, use something like enquirejs to load the script once the screen width is close to your smallest min-width element-query.

  7. 11

    Excellent element queries will be incredibly beneficial for the plugin market. Responsive plugins aren’t enough. I find users often put plugins in the strangest of places….

    • 12

      Jon Hobbs-Smith

      June 25, 2013 4:53 am

      Indeed, if you don’t even know what layout your component is going to end up in… This could be very useful in tandem with Web Components.

  8. 13

    Jon Hobbs-Smith

    June 25, 2013 4:48 am

    Responsive Design is crying out for this. I really hope browser manufacturers and the W3C can band their heads together and come up with a solution. The simple thing would be to not allow any recursion, or not allow inner elements to affect the size of the container, like iframes do.

  9. 14

    Vitaly Friedman

    June 25, 2013 5:15 am

    You can also follow a fantastic discussion about the so-called “element media queries” in a recent Scott Jehl’s article on the Filament’s group blog: Working around a lack of element queries.

  10. 15

    jQuery required? I really wish that this solution was native. As I would like to use this technique on navigation elements.

    • 16

      It heavily relies on Sizzle to quickly query for elements matching the CSS selectors. Doing a standalone script would require me to rewrite most of Sizzle. Hopefully browsers will eventually start supporting element queries so we wouldn’t have to rely on JavaScript.

      • 17

        Nobody forces you to use Sizzle. There are a few faster selector engines around – I’d just take one of those, add the core script of headJS or anything else that does help us to some nice on-DOM-ready call, add the rest of the actual polyfill on top – and taadaa! Bill’s your uncle.

        cu, w0lf.

        ps: Yep, I actually did that already a few times in other cases. Native JS is just so much more fun .. ;)

        pps: Dear Smashing Magazine: Yeah, I’m STILL no spam bot. Instead you folks obviously STILL haven’t got your “anti-spam” issues fixed.

  11. 19

    I have been reading about CSS FLEXIBLE BOX OR “FLEXBOX” It will be interesting if that concept picks up more traction and see if it plays out.

  12. 20

    Thanks, Tyson for sharing your tips & knowledge about the element query as well as showing us some working examples and pointing out some of its issues.

  13. 21

    Adrian Zumbrunnen

    June 25, 2013 6:53 am

    You can actually use a wrapper which i functions as a module.

    for instance:

    if you use box-sizing border-box im combination with the surrounding element (‘module’) which dictates the width, you get a pretty modular result.

    you start sketching your modules first, then the contents within will adapt just smoothly.

  14. 22

    Also, if you want cleaner rules you can use a CSS preprocessor to nest your selectors:

    header[min-width~="31.250em"] {
    	h1 {
    		font-size: 1.75em;
    	}
    	nav {
    		clear: both;
    	}
    }
    • 23

      Alexandru Lighezan

      November 6, 2013 4:55 pm

      I was wondering wether inside the css element query you could add a class. That class could have properties defined somewhere else in the css. Your js script could then add that class to the element based on its size. Should be much faster than reading all the rules and applying them to the element.

  15. 24

    Daniel Montgomery

    June 25, 2013 8:21 am

    This is a great overview of the topic and good examples.

    I feel the main bottleneck will be surrounding the recursion examples provided. To me, this speaks more for the current model where elements can be sized from containers or contents. I don’t know how the browser decides what to do when there are conflicts, but building from the inside out and the outside in is going to present tons of challenges.

    It feels to me like websites used to build for wrappers fitting to their contents where now almost all content is meant to fit inside their wrapper.

    Could a new element type or procedure be used to force contents to fit inside rather than bloat the wrapper? It’s also an issue of using a structural and style system (CSS) on top of a system (HTML) that is supposed to be abstracted from the structure. They are supposed to be independent, but that’s not realistic at this point.

    If we can’t even get a flexible box model supported I don’t know how long before something useful like this will exist. Here’s hoping soon!

  16. 25

    If we’re pushing for standards I’d ask we go a step further and examine how we’re defining these “break points” and re think the ambiguous terminology that is min and max width… yes we’re used to it, but as much as @media is a hack so is this.

    I would propose that we use less-than, greater-than, equal-to, between, range … etc.

  17. 26

    The more we use Media Queries, the more problems we start to see arrising. This kind of solution would definatly help us to solve a few of those problems. Maybe a CSS4 implimentation (if they can get off their ass).

  18. 28

    This is a great solution to one of the RWD problems. However, it is still OSFA in nature.

    By gratuitously pursuing OSFA RWD, we are placing limits that never allow us to take full advantage of the medium. The smartphone, the tablet, the desktop/laptop all need to be seen as unique mediums with unique advantages, benefits and limitations.

    Developers of phone, tablet and desktop apps don’t simply rescale their apps for each device. Why do we do it with web pages?

    RWD should be constrained to the medium. e.g. on the smartphone medium, delivering normal graphics on an iPhone 3GS, and retina graphics on an iPhone 5. Or on the desktop/laptop medium, two column body layout on a desktop greater than 1680, and one column on less than that.

    But when we try transforming a smartphone, 320px wide layout, into a 1920 desktop, we make sacrifices on both mediums.

    If we must pursue OSFA RWD, then this proposal of element queries is better than media queries.

  19. 29

    So if the CSS spec. and browsers someday support the “element query” would this then eliminate the need to use something like ‘sizzle’?

  20. 30

    Wouldn’t a solution to the looping problem be fixed by using container queries rather than element queries? You’d still largely have the same functionality.

    I imagine it would work something like this:

    “`
    @parent (min-width: 500px) {
    .foo {
    width: 200px;
    }
    }
    “`

    In fact, now that I look at this I think it may be more useful. Allowing you to reuse a `@parent` query for multiple modules.

    Issue:

    looping may still occur if the parent elements width depends on it’s content. Although that is an issue with the element query too, so this is at least closer. In fact it could probably solved by only applying to elements that are positioned in a way that doesn’t depend on content.

  21. 31

    Just awesome.

    We’ve been looking for something like this to help us out with quick prototyping. Sure enough, there is a performance hit and I would never use it on a production site, but when it comes to prototyping (where you need _a lot_ more flexibility) this is a true godsend. The concept of element queries has been floating around the web for a while now, good to see it pick up some steam. RWD needs it.

  22. 32

    Thanks for the article. It’s really great to see other developers experimenting with technology that is already in place. Keep up the hard work!

  23. 33

    I hated the idea of element queries when I first saw them, they felt redundant and forced but over the last couple of weeks I’ve really come to believe that they are the long term solution. THank you for putting this polyfill out there, I look forward to playing with i.t

  24. 34

    Great concept and technique. I like anything that’s simpler and more streamlined. If you’re calling media queries a hack though – with which I don’t totally disagree – then this technique also seems like a another hack to me. At least for the time being, considering the current state of affairs on the web with this. Be interesting to see this developed more. I’ll keep an eye out. Thanks.

  25. 35

    Lucifero Von Nachtosphere

    July 1, 2013 7:09 am

    Great article Tyson Matanich, my compliments.
    Maybe (maybe) it’s more for the “classic” templating approach, but nonetheless this is kinda the «holy grail» of templates, really!
    I had some fun with this: http://codepen.io/nobilelucifero/pen/hgGLf and think I’ll use this technique ASAP.

    You made my day, Sir

  26. 36

    Marcellino Bommezijn

    July 2, 2013 11:48 am

    Nice approach indeed. The sample links on Codepen respond very slow (at least for me – FF and Chrome) on browser re-size and doesn’t immediate show the visual result, it stalls. This might be due to the use of JS which needs to evaluate the page first and do the magic.
    And these are small samples, so i wonder what happens when it concerns a large collection of rules that need to be evaluated.
    Would it be possible to mix the use of media queries and polyfill? This way you can take advantage of both.

    • 37

      It’s only a Polyfill, so, yeah, it’s slow. But as soon as this is implemented it will probably be as fast as media queries.

  27. 38

    Jonathan Weavers

    July 9, 2013 12:49 am

    Hey Tyson-

    Your last post [Media Queries Are Not The Answer: Element Query Polyfill] was freaking awesome. I have gone ahead and added your stuff to my Feedly account. Please keep me updated if you post anywhere else.

    Keep rocking –

    Jon

  28. 39

    Some of what you’re talking about is being achieved with the LESS CSS plugin. It provides a very well thought out method for treating CSS programmatically.

    Check it out at: http://lesscss.org/

  29. 40

    I wrote a framework [SickleS] which inspired by your [elementQuery].
    Check it out : http://singggum3b.github.io/SickleS/

  30. 41

    Can you help me how to link the scripts in the html page?

  31. 42

    i don’t like the script solution, but this is how media queries should work and i believe this will be the future

    i hope someone will start soon to write an rfc regarding this

  32. 43

    Loving this, but I feel like I was just getting my fancy SASS-based, old-IE accommodating media-queries set up right.. The web moves so fast :-)

  33. 44

    When will we designers get back to designing in terms of proportions?

    As long as this so called “responsive” technology ignores that visual communications and visual aesthetics are strongly based upon the interrelationship between the height and the width of a visual element — and not based on either width-only or height-only formulas — then all of these technical refinements seem doomed to aesthetic miscommunications.

    I’m all for a “different way of thinking”, but only if this different way actually strengthens our visual communications with one another, and this can’t be achieved as long as proportions are ignored.

    For examples of what I mean, go to:
    http://www.thesevenfoldstudio.com/blog/

  34. 45

    “Once an element reaches 500 pixels wide, it’s resized to 200 pixels, at which point the rule would no longer apply:”

    “.element (min-width: 500px) {
    width: 200px;
    }”

    This seems silly. Isn’t the obvious solution that the width is referring to the parent (available width)? That seems more analogous to how media queries work, where it’s measuring the viewport. Why would I be measuring the properties of the element upon which I’m setting properties? That seems like a recursion nightmare.

    If you want modularity, measure the properties of the available container for your element i.e. the parent element.

    • 46

      I think this might be the more sensible solution. A previous commenter, James Kyle, suggested something similar, a @parent query that works like a @media query. The advantage of this is that it’d work equally well as @media and indeed could be a replacement for all size-based media queries. However, I’d still love to see it as a pseudo-class.

    • 47

      Querying the width of the parent element ignores the possibility that your element might be one of many other elements contained within the same parent. If my element is sitting in a container which lays children out in a grid, the width of that parent will not give me useful information about the space available inside the child element, which is the “available space” that I care about.

      Clearly, the “set width to 500px when the width is 200px” is an edge case that would never actually occur – it’s just an example of where robustness needs to be built into any implentation of the “element query” concept.

      Knowing how much internal space is available to the element’s children is they key to being able to write “drop in and just work” components.

      Same response to Paul below.

      • 48

        The parent query concept works for me – I imagine that if I have a piece of UI that needs to remain modular then it will always have its own dedicated container which I could query widths/heights etc. against.

  35. 49

    Hi there,

    I really want to use this for a website I’m designing. However, I am fairly new with professional web development(still in college) and I’m not sure what I need to do in the html to get this working. The elementyQuery file is in my website’s directory but do I have to link to them in the head of my html page? What do I have to put in the head of html page, if anything? For that matter, besides having the file in my directory, what setup overall do I have to apply?

  36. 50

    I totally agree with this! CSS should totally work like this. Maybe CSS4

  37. 51

    If you are getting this error “SecurityError: The operation is insecure.”, this comment explains how to fix it.

    This error is happening because you are loading at least one css file from a different domain. For example, if your page is at www[.]mysite.com, you might be loading a style sheet from www[.]othersite.com — elementQuery cannot read stylesheets from other domains on some browsers. That doesn’t mean there needs to be an error though. You can add a line of code that will fix it.

    This can be fixed by adding a line to the code.

    1. Download the full source version from github (elementQuery.js, not elementQuery.min.js)

    2. One line 151 (in the current version), you should see the line below.

    if (styleSheet[cssRules] && styleSheet[cssRules].length > 0) {

    3. Add the following line of code right above the line in step 2 (so this new line will become 151 and the line in step 2 will become line 152). Add this line exactly:

    try { styleSheet[cssRules].length; } catch(err) { return; }

    Save elementQuery.js and load it instead of elementQuery.min.js — the security error and it’s side-effects should disappear.

  38. 52

    I think this whole discussion (and I know I’m jumping in late) misses the point entirely. Updating CSS rules for all of this additional complexity is the entirely wrong approach to take. Because at the heart of it, what we’re looking for is a full Turing-complete programming language that focuses on informational display, and what we’re doing is tweaking three separate technologies (HTML, JavaScript, CSS) [Well, four if you include the various back-end middleware the takes raw data and processes it into stuff we can begin to display], none of which do exactly what we want, and none of which are all that great, and that don’t always play well together without a lot of redundancy and stop gaps. I mean, I hate to say this, but honestly Flash was a better alternative to what we currently have. At least it was self-contained and not limited to the whims of the browser (once it was installed and loaded).

    So while it’s great that we can do more with the tools we have, I think we need to spend less time refining those tools (and making a bloated mess of things as we go along), and more time thinking about a legitimate alternative to the web as we know it. It’s a much easier problem to solve – the only difficulty is making it backwards compatible. There’s no reason why a modern webpage should be cobbled together out of three frameworks, two preprocessors, three compatibility plugins, and god knows how many JS components.

  39. 53

    Alejandro Stendelis

    October 9, 2014 6:48 pm

    Hi,

    Sorry for my bad english.

    This script is great.

    I have a situation, in Chrome run OK, but in Firefox not.

    See the demo. You see an error in my code?

    http://www.accuweatherglobal.com/AWG-Widget/test/widget/v.1.0.0/test.html

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top