First, an exercise. Can we represent all of css with plain data?
let redText = { color: 'red' };
let blueText = { color: 'blue' };It's immediately obvious what this code 'does'. There's no third party dependency to import, no helper functions or fancy syntax, and runs in any javascript environment. We've switched to camelCase (as opposed to css hyphen-case), but that seems super natural in javascript, and preferred.
Let's make another.
let boldText = { fontWeight: 'bold' };Again, super apparent. Now, Let's combine the two.
let boldRedText = { ...redText, ...boldText };Nothing special, it's "just javascript". you can inspect it and see what it contains, no surprises.
console.log(boldRedText);
// { color: 'red', fontWeight: 'bold' }An alternate representation of combining the two could be with an array -
let boldRedText = [redText, boldText];
console.log(boldRedText);
// [{ color: 'red' }, { fontWeight: 'bold' }]This representation has the advantage of preserving the pieces and order that compose the style, which is nice for debugging, while also being more efficient for the computer to handle. (add footnote)
Let's extend this object language further by adding pseudo selectors -
let redGreenText = {
color: 'red',
':hover': {
color: 'green',
},
};It should hopefully be clear what this object represents - a text style text that's red by default, and green when hovered. Like before, this composes well.
let composed = [redGreenText, boldText];
/*
[{
color: 'red',
':hover': {
color: 'green'
}
}, {
fontWeight: 'bold'
}]
this would be equivalent to -
{
color: 'red',
fontWeight: 'bold',
':hover': {
color: 'green'
}
}
*/Say we wanted bold text only on hover, how would we represent that?
let composed = [redGreenText, { ':hover': boldText }];
/*
this would be equivalent to -
{
color: 'red',
':hover': {
color: 'green',
fontWeight: 'bold'
}
}
*/Nice!
We can nest more than just pseudo classes. Sass/Less folks will be familiar with contextual selectors -
let translucentRed = {
backgroundColor: 'rgba(255, 0, 0, 0.8)',
'.ie6 &': {
backgroundColor: 'red',
},
};This would mean "background color is a translucent red, unless it has a parent with class 'ie6', in which case it's plain red."
Similarly, we can add support for @media queries and @supports blocks. A contrived example showing them all in one object -
let page = {
color: 'red',
':hover': {
color: 'blue',
},
'@media screen': {
color: 'blue',
'@supports (mid-width)': {
color: 'yellow',
},
},
};You're free to nest arbitrarily and deeply; with whatever combination of selectors or media queries or whatnot.
Finally, sometimes, you want to output multiple values/fallbacks for a property. It only feels natural to define this with arrays.
let style = {
color: ['red', 'rgba(255, 0, 0, 0.8)'],
};With this code, the background color will be a translucent red in browsers that support rgba(), and plain red in older browsers.
With these few rules, we now can write css styles that target the entire css spec, no exceptions. There's no vendor lockin, they can be used across environments, serialized into json (or any other format) and transmitted over the wire, type checked(footnote - how?). Further, you can leverage decades of data management knowledge and 'architect' your styles in whatever manner you prefer. Need theming? Use functions that accept theme values and return objects. Creating too many objects of the same shape and value? Hoist them and/or use a cache. Etc etc.
mixins variables themes
You're now free to implement any classic css architectures like itcss, smacss, oocss, bem, but without any of the constraints of a statically compiled language like sass/less.
But you're not bound by the inadequacies of sass/less/css.
So far, so good. Now how do we use these styles? Keen observers would have also noticed the lack of any classnames to add to elements.
Let's assume we have a magical function, css(), that takes any of these objects and gives us a classname to add to our html element.
let html = `<div class='${css({color:'red'})}`> this is red! </div>`;
// <div class='css-1sdfpa`> this is red! </div>... that's it. there's nothing new to 'learn', and it cooperates really well with the rest of your tooling/workflow/ecosystem. Brilliant!
In the next post, we'll dive into what css() does behind the scenes.