Skip to content

Instantly share code, notes, and snippets.

@tomhodgins
Last active December 27, 2017 23:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomhodgins/cb6eb9cb16b1ba3948cdc74b6b83d5ba to your computer and use it in GitHub Desktop.
Save tomhodgins/cb6eb9cb16b1ba3948cdc74b6b83d5ba to your computer and use it in GitHub Desktop.
Thoughts on a Syntax for Element Queries

Thoughts on a Syntax for Element Queries

It's possible to think of Element Queries as a natural extension to CSS Media Queries. I would summarize the requirements for media queries like this:

  • a media type
  • query condition(s)
  • a stylesheet

In CSS this gets exposed as:

@media screen (min-width: 500px) {
  html { background: lime; }
}

The @media wraps the stylesheet, the screen part is specifying the media type, and (min-width: 500px) specifies the query condition(s).

In HTML the same functionality is exposed through the media="" attribute on <style> and <link> tags:

<link rel="stylesheet" media="(min-width: 500px)" href="data:text/css,html { background: lime; }">

<style media="(min-width: 500px)">
  html { background: lime; }
</style>

Finally, in JavaScript the media querying functionality is available to authors using the window.matchMedia:

if (window.matchMedia('(min-width 500px)').matches) {
  css = 'html { background: lime; }'
}

In order to extend these syntaxes in HTML, CSS, and JavaScript to cover the functionality needed to describe element queries, let's look at the requirements for element queries:

  • a selector (list)
  • query condition(s)
  • event listener(s)
  • a stylesheet

Here we have a couple new ideas: a selector to apply the stylesheet to, and event listeners.

In CSS

Normally JavaScript events are abstracted away from CSS authors - you don't need to worry about window.load or window.resize when writing media queries, you don't need to worry about mouseover or mouseout when writing :hover, and you're not worried about focus or blur when using :focus, etc.

For the most part I believe browser makers will be able to find a way to implement element queries in the most general sense without the need for CSS authors to specify the specific events they should listen to. In addition, enhancements may be made by listening to specific events related to the selector, and/or query conditions. If a query condition for scroll is used, the browser should be able to automatically listen to scroll events on the tags matching the selector used.

However it is likely that performance improvements could be made by allowing authors to limit which events trigger a reevaluation of the query conditions using a whitelist.

Proposed CSS Syntax:

@element html {
  html { background: lime; }
}

Here the @element wraps the stylesheet, the html specifies the selector, and we are lacking query conditions or events. This is a simple element query that would apply to the document as long as an <html> element was found.

@element textarea (min-characters: 50) {
  textarea { background: lime; }
}

This example includes a query condition that counts the number of characters of text inside the value of a <textarea> element.

This brings us naturally to the question: "What happens if there is more than one <textarea> in the document?" It certainly looks like as long as any <textarea> has more than 50 characters, all <textarea> tags in the document will gain a style. Because of this, another concept is required: a scoped selector.

I like the idea of using $this as a placeholder inside selectors in the element query stylesheet, to represent each element tested that passes the query conditions.

@element textarea (min-characters: 50) {
  $this { background: lime; }
}

Now if we have multiple <textarea> present in our document we have a way of targeting only those specific <textarea> elements that match the query conditions. But the use of this placeholder isn't limited to just applying styles to matching elements themselves - it should be able to be used anywhere in selectors in the contained stylesheet:

@element .widget (max-width: 300px) {
  $this h2 { font-size: 10pt; }
}

Lastly, a way to specify limited events should be offered to the user. This has no precedent in CSS so here are some ideas. It's possible that the specific names of events could be aliased to something new, but assuming for a moment that we are using the names of events from JavaScript, we need a way to specify both window-level, and tag-level event listeners. Maybe something like:

@element textarea (max-characters: 5) on blur {
  $this { background: red; }
}

I would hope that by default, based on this query applying to an input element it would naturally listen to events like blur and focus and reevaluate the query conditions when those events occur, but here we limit reevaluation only to the blur event, and it is prevented from reevaluating any other time.

In HTML

How could the same element query functionality be exposed to HTML? Perhaps through the use of additional attributes on <style> and <link> tags:

  • selector for a selector list
  • element for query conditions
  • event for events

These could look like the following:

<link rel="stylesheet" selector="textarea" element="(max-characters: 5)" event="blur" href="data:text/css,$this { background: lime; }">

<style selector="textarea" element="(max-characters: 5)" event="blur">
  $this { background: lime; }
</style>

In JavaScript

And to expose this functionality to JavaScript, perhaps something like window.matchMedia could be useful, maybe window.matchElement. It would work similarly to this:

document.querySelectorAll(<selector>).filter(<query conditions>)

So for an element query of (min-width: 500px) on <div> tags you would need a test like:

document.querySelectorAll('div').filter(el => el.offsetWidth > 500)

This works fine, but the real power of window.matchMedia is that it's able to handle CSS units, rather than just working with pixels. Having the ability to do something like the following example would be better than what we have in JavaScript right now:

window.matchElement('div', '(min-width: 50em)')

References

If you're looking for syntax precedents for these ideas, or plugins that use something similar, check out:

  • @element at-rule similar to EQCSS
  • <link> attributes similar to Slinky
  • <style> attributes similar to reproCSS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment