LightningCSS - How to Transform a DashedIdent value?
Sometimes I have very stupid ideas. People usually laugh at first, but I've learned that there's truth in every stupid idea. So, my latest dumb idea was to turn CSS into CSS! Sounds great, I know. If you're working with Bootstrap 5 and want to use CSS variables to change the color of some buttons, for example, you'll find that Bootstrap can only handle this in some parts of its components. They are working on fully supporting this with Bootstrap 6, but they also rely on SCSS in their components, and if they were to change it now, those would be breaking changes. The sad thing is that there is currently no date for when Bootstrap 6 will be released.
We've tried things like overriding SCSS functions to have control over how the CSS is generated, but we get from one error to the next, and you don't know how many errors there will be because you have to fix one at a time and compile each time during development.
Generally, our goal is to have some sort of theming over the administration. Basically change some colors and maybe some font and font sizes and a logo and you are ready to go. As said before, Bootstrap 5 is not consistent with the root CSS variables, they copy values while compiling and setting new variables inside components they somehow depend on the root variables but are disconnected after compiling.
So my stupid idea was that we change the compiled CSS file again to allow easy theming via the administration without having to recompile things. But how? Yeah, let's try using LightningCSS for that. I kind of know that they have a very detailed AST and you can use some visitors to transform things. But I've never done that and had no idea if that would work.
Example how to transform a custom DashedIdent value
import { transform } from 'lightningcss'; let res = transform({ filename: 'test.css', minify: true, code: Buffer.from(` .foo { width: 12px; } .btn-primary { --bs-btn-color: #fff; --bs-btn-bg: #0042a0; } `), visitor: { Length(length) { return { unit: length.unit, value: length.value * 2 } }, Declaration(decl) { if (decl.property === 'custom' && decl.value.name === '--bs-btn-color') { decl.value.value = [ { type: 'color', value: { type: 'rgb', r: 0, g: 0, b: 0, alpha: 1 } } ] } if (decl.property === 'custom' && decl.value.name === '--bs-btn-bg') { decl.value.value = [{ type: 'var', value: { name: { ident: "--bs-body-color", }, } }] } return decl } } }); console.log('result:', res.code.toString());
In that code we convert the color value of the DashedIdent --bs-btn-color from #fff to #000 via an rgb value. And for --bs-btn-bg we set a CSS variable (--bs-body-color) instead of a color value (it was #0042a0).
Learnings?
I am pretty sure there is even a more beauty way to search for a custom DashedIdent but as an example I guess this is maybe already helpful. So what would be the next steps? First we would need to create some mapping, then a function that is using the mapping and replacing the values with CSS variables. To understand the AST from LightningsCSS the most helpful tool was this little Lightning CSS AST Viewer page.
Additional Resources
- How to combine SASS color functions and CSS Variables
- Polyfill / Addon for overriding colors through CSS variables #39901
- Consider dynamic theming support #37983