css-in-js-precompiler
css-in-js-precompiler copied to clipboard
Discussion: the limits and tradeoffs for best-in-class precompilation
Kent, I'm trying to figure out a path forward for best-in-class precompilation in combination with dynamic props-based dynamic styles at render time. I.e. how to achieve a best-of-all worlds here.
Regarding this tweet:
https://twitter.com/kentcdodds/status/903767274749865984
why exactly would the following be a problem to precompile:
const myStyles = {}
const MyDiv = glamorous.div(myStyles)
myStyles.color = 'red'
<MyDiv />
Is the idea that the reference to myStyles is used only at render time when <MyDiv /> is rendered?
If that's the case, couldn't that be solved by treating it immutably and making a clone of myStyles so that nothing more could be added to it?
That would enable precompilation to do whatever it wants to myStyles before passing it to myStyles.
Basically the sort of setups I imagine are javascript-based merging of objects to take the place of LESS/SASS/Stylus style mixins, inheritance etc. That would look like this:
import { fooStyle, barStyle } from '../styles/global'
import { fontSize } from '../styles/variables'
let sizeIncrease = 2
const myStyles = {
...fooStyle,
...barStyle,
padding: 10,
fontSize: fontSize + sizeIncrease
}
const MyDiv = glamorous.div(myStyles, ({ primary }) => ({
backgroundColor: primary ? '#CC3A4B' : 'rgba(255, 255, 255, 0.5)',
}))
When it comes to building the babel plugin, perhaps the key thing in above is simply that sizeIncrease is not specified within the object literal. It's that sort of stuff we'd want to enable. On the extreme side, you could loop through an array like this:
const sizes = [2, 5, 8]
const allStyles = sizes.map(size => ({
...fooStyle,
...barStyle,
padding: 10,
fontSize: fontSize + size
})
So the goal would be to support all such things in a precompile phase. It would be up to the developer to not access things on window, and that limitation would be acceptable. That's the sort of direction I'm seeing here.
What other sort of challenges do you see here? It seems discovering simply what parts of the code are intended to be pre-compiled are the primary aspect. You'd basically have to discover what arguments are passed to glamorous.div and back-track to figure out what portions of the code need to be pre-compiled (without executing things that possibly can't successfully evaluate in this environment).
To paint the picture of what sort of problems it seems we need to solve, perhaps looking at something more feasable would help. An idea would be something similar to renderStatic as you use for SSR:
let { myStyles } = createStyles(() => {
const myStyles = {
...fooStyle,
...barStyle,
padding: 10,
fontSize: fontSize + sizeIncrease
}
const another = { color: 'purple' }
return { myStyles, another }
})
That's less than ideal, but at least it gets us there, as that's easy to target with a babel-plugin. Obviously the babel plugin would know to only preval what's within the function passed to createStyles. I think if we can take that as inspiration, we may be able to find a way to do it without the createStyles wrapper, while allowing for maximum flexibility without making too many tradeoffs.
I know the use case isn't important to you, but I think it is to a lot of people. You have a lot of experience with this, so any additional information you can think of to help this cause would be very valuable.
It's also more important now that you can render to a stream on the server. For example, now at the beginning of a request you can get all the stylesheets you will need just by knowing what chunks a given route depends on, and then append those stylesheets at the top of the page before even starting the streaming of what's rendered. Of course you need code-split stylesheets to do that, but let's just assume you do.
In that case I'm not sure what happens with prop-based dynamic style functions, which you'll still want, as after all the whole purpose is to have both static precompiled styles + dynamic styles. But I assume you have something cooking for the streaming use-case without precompilation.
In conclusion, I think what you've done with glamorous has paved the way for the ALL JS styling future that will serve us best. And if we can come up with some world-class solutions for preserving static stylesheets when the styles are in fact static while of course gaining the full power of JS, we'll have completed the picture here. The static stylesheets is especially important when it comes to code-splitting. If you have a setup that can generate multiple stylesheets for your dynamic imports, ultimately what you're gonna end up with is better cacheability, as the stylesheets will cache independently of both each other and the JS. And obviously that's just one of several benefits. There's a lot of incremental perf benefits by using static stylesheets where possible that in sum make a difference.
Hi @faceyspacey, I'm afraid I didn't take the time to read your whole comment. Mostly because I'm limited on my time right now, but also because I'm not really too interested in working on this project any further. Feel free to fork it and work on it however you like. Good luck.