To indicate a selector should parse as an element query,
prefix the media query rule with the media type element
.
We can use custom media types, because the spec acknowledges that media types
are likely to change over time
and affords such flexibility. (However, it should be noted that many media
types are planned to be consolidated.)
// app/styles/modules/np/component.less
.np-Component {
background-color: red;
@media element and (min-width: 20em) {
background-color: blue;
&-part {
border: 1em solid;
}
.child {
font-size: 2em;
}
}
}
If wanting to assign a name to the media query in order to expose it for
scripting, prefix the name with two hyphens (--
) and place it before the
element
media type. This syntax roughly aligns with the
Media Queries Level 4 draft spec
for custom media queries
and extension names.
.np-Component {
@media --medium element and (min-width: 40em) {
background-color: green;
}
}
The following shows an example gulp task that would process the source CSS and preconfigured a JavaScript file for the client.
// gulpfile.js
var gulp = require('gulp');
var less = require('gulp-less');
var elq = require('gulp-element-query');
var stylePath = './app/styles';
var styleSrc = stylePath + '/main.less';
var elqConfig = {
jsDest: './app/bundle-elq.js'
};
gulp.task('elq', function() {
return gulp.src(styleSrc)
.pipe(less())
.pipe(elq(elqConfig))
.pipe(gulp.dest(stylePath));
});
Run the task in the command line.
$ gulp elq
Upon executing the build process, the following should occur:
Nested media queries extract into the body of the CSS once LESS compiles.
.np-Component {
background-color: red;
}
@media element and (min-width: 20em) {
.np-Component {
background-color: blue;
}
.np-Component-part {
border: 1em solid;
}
.np-Component .child {
font-size: 2em;
}
}
@media --medium element and (min-width: 40em) {
.np-Component {
background-color: green;
}
}
Using PostCSS, the source CSS is evaluated
for element media queries, with the first selector in each query block
(e.g. .np-Component
) assumed as the target. LESS prioritizes parent rules
in the source order, so this assumption shouldn't be problematic when
nesting rules
or utilizing the parent selector.
The element media queries are replaced with a modifier class name based on the
target selector (e.g. .np-Component
) and either the supplied name
(e.g. --medium
) or a random, obscured, globally unique name (e.g. --bu3Pu
).
All generated names are evaluated against the source CSS to guarantee uniqueness.
Task: Evaluate unique id projects and recommend one:
- https://github.com/ivanakimov/hashids.node.js
- https://github.com/dylang/shortid
- https://github.com/broofa/node-uuid
- https://github.com/dilvie/cuid
- https://github.com/substack/node-hat
.np-Component {
background-color: red;
}
.np-Component--bu3Pu {
background-color: blue;
}
.np-Component--bu3Pu .np-Component-part {
border: 1em solid;
}
.np-Component--bu3Pu .child {
font-size: 2em;
}
.np-Component--medium {
background-color: green;
}
A JSON object is created with the corresponding config and compiled into the indicated JavaScript bundle.
{
".np-Component": {
"--bu3Pu" : {
"className": "np-Component--bu3Pu",
"name": "--bu3Pu",
"query": "(min-width: 20em)"
},
"--medium": {
"className": "np-Component--medium",
"name": "--medium",
"query": "(min-width: 40em)"
}
}
}
Include the compiled JavaScript at the end of the <body>
.
<script src="app/bundle-elq.js"></script>
The JavaScript will automatically execute and actively watch the DOM for matching selectors.
<div class="np-Component"></div>
The script finds a match in this DOM, so it
appends the <object>
element,
so the media rules can be evaluated.
<div class="np-Component">
<object type="text/html" data="about:blank"></object>
</div>
The modifier class names conditionally toggle as each rule is evaluated.
<div class="np-Component np-Component--bu3Pu">
<object type="text/html" data="about:blank"></object>
</div>
// Mounts or overrides element query.
// Shortcut for `window.elementQuery().mount(config);
var newComponents = window.elementQuery(config);
var components = window.elementQuery(); // array of all elementQuery objects.
var component = window.elementQuery('.np-Component');
var elements = component.elements; // array of DOM objects
var mediaList = component.media; // array of MediaQueryList objects
var media = component.matchMedia('--medium');
media.addListener(function(data) {
data.className; // 'np-Component--bu3Pu'
data.element; // DOM object
data.matches; // true
data.query; // '(min-width: 20em)'
data.selector; // '.np-Component'
});
media.removeListener();
Parse a media query into a JavaScript object and compare. https://github.com/ericf/css-mediaquery
Syze provides an interesting API to set media queries in JavaScript.