We want to end up with this as the result, but we want to:
- use a variable while writing the stylesheet
- output a width and height property with the variable's value in PX units
div {
width: 50px;
height: 50px;
}
In this example, we are working 100% outside of the language we're preprocessing. All of our custom extensions here are written in JavaScript, and it's not aware of the content of the CSS it's working with - it only sees it as strings. It assembles and outputs CSS but at no point was parsing CSS needed. This is future-proof.
let size = 50
let width = `width: ${size}px;`
let height = `height: ${size}px;`
let selector = 'div'
outputRule(selector, [width, height])
This is the route taken by preprocessors like Sass and Less and Stylus and PostCSS - it tries to extend the language from inside the language, but it does it in ways that are either invalid syntax in the original language, or valid syntax but doing it in ways that aren't future-proof (by camping on platform namespaces). One big flaw to this approach is that you have to parse the language, but you're constantly trying to reconcile two languages together and as they grow they may grow more incompatible with each other as either adds new features. This is not future-proof, you constantly have to worry about updating your parser and ultimately your tool gets less useful over time as the common ground between the two languages shrinks.
$size: 50;
div {
width: ($size * 1px);
}
The third way you can approach preprocessing a language is by including custom information inside the language you want to preprocess that is valid in that language, and doesn't camp on the platform namespace for future features. This is future-proof.
div {
--size: 50;
width: calc(var(--size) * 1px);
height: calc(var(--size) * 1px);
}
div {
--size: 50;
width: var(--size);
height: var(--size);
}
/*
somewhere in JS we have:
value => value + 'px'
*/
Bonus round, just so you don't think I'm immune to making the same mistake - element queries in all three types. Guess which one we have to worry about parsing ;)
element(
'div',
{minWidth: 500},
`[--self] { background: lime }`
)
@element div and (min-width: 500px) {
:self {
background: lime;
}
}
@supports (--element("div", {"minWidth": 500})) {
[--self] {
background: lime;
}
}
In these examples - the JS one we never have to worry about because it's valid JS. We can process and output the CSS we need from JS easily. The custom syntax we have to write a custom parser for, and to do that we have to now adopt the neverending task of making sure changes to CSS syntax don't break the assumptions we built into our own custom syntax at the time we designed it. The last example is valid CSS, but is guaranteed never to collide with any native CSS feature it will gain in the future. We also don't have to worry about parsing this since it's valid CSS.