Render JSX plugin for Esbuild
The last post showed a simple plugin for Esbuild.
- Jacco Meijer
- |
- Mar 20, 2024
Render JSX plugin for Esbuild
Transform Esbuild generated JSX bundles to HTML pages.
Green build
This plugin was enhanced with a few lines of code to support Front Matter, CSS bundles and images. The result was named green-build.
Green lib
This post shows how green-build was used to create a small UI library. The library is documented with MDX documents for each item in the library and available on Github.
The green reference example site is created with the library to show the capabilities. It simply renders all components.
The MDX documents of the library are published here. A simple menu binds the documentation together. The result is quite similar to a component storybook.
Fast evolving CSS
This post is part of a series that originated with the question "can the Contentful-Gatsby-Netlify trio be simplified?".
The original idea to put this trio together goes back to 2018. Six years later, much has changed in the world of CSS!
CSS Specificity
A classic CSS challenge is CSS specificity. The selector with the highest
specificity value wins. Inline styles have a specificity of 1000 which could
only be overridden by the !important
rule. Lots of fun!
This challenge resulted in theories like the Inverted Triangle CSS.
ITCSS
"Specificity is inversely proportional to how many elements they affect."
Similar concepts to manage specificity are in this library. The structure (objects) and utility classes have a lot in common with ITCSS.
CSS Cascade Layers
The @layer
rule added in 2022 greatly improved managing specificity. When
importing CSS like this:
@import "./css/global/reset.css" layer(reset);
@import "./css/global/markdown.css" layer(markdown);
@import "./bundles/css/structures.css" layer(structures);
@import "./bundles/css/components.css" layer(components);
@import "./bundles/css/utilities.css" layer(utilities);
Specificity can be managed like this:
@layer reset, markdown, structures, components, utilities, custom;
CSS on the custom layer will always override CSS on the layers below. This is a
great way to manage the cascade. No need for the !important
rule anymore!
:is and :where
A concise way of writing rules and more control over specificity:
:is(list)
→ specificity is set to the most specific selector in the list:where(list)
→ removes specificity for all selectors in the list
Clamp
Clamp is another useful addition to modern CSS. Amongst many things, it allows for dynamic font and space scales. This library uses scales generated at Utopia.
Design tokens and CSS Custom Properties
Space scales are added to the project by using design tokens.
Design tokens allow for importing things like colors and spaces from Figma directly into CSS. This library uses the design-tokens-cli package to covert Design Tokens to CSS Custom Properties.
More on that at the W3C Design tokens community group. This is the latest version of the spec.
Eslint v9
Not a CSS improvement, but working with JSX, a relevant change in the landscape.
Eslint finally decided on a new eslint config format
called flat config
. The VSCode Eslint plugin has a Experimental: Use Flat Config
flag which makes the plugin work with the new (greatly improved!)
config.
Native Nested CSS
The list of CSS improvements is long. Nested CSS is natively supported by all major browsers. No more need for a CSS preprocessor to use this.
Container queries
Another interesting addition is what is named 'container queries'. When defining:
.parent {
container-type: inline-size;
}
Children of this parent can use:
@container (min-width: 700px) {
.child h3 {
font-size: 2em;
}
}
This gives a lot more control than the existing media queries.
Web components
By experiment, two components in the library have been set up as Custom Elements with isolated shadow DOM. The components were elected because they require Javascript to function.
- navigation-header, uses Javascript to open the mobile menu
- carousel-layout, uses Javascript to scroll the slides
This works well, but faces all the problems of styling Custom Elements.
NOTE: Zach Leatherman, author of 11ty, wrote a great post on the modern issues of styling Custom Elements.
The library produces one CSS bundle which can be reused in the Custom Element. Because the browser caches the bundle, it won't go over the line twice. Still, this solution is not perfect. As outlined by Zach in the post mentioned above.
Declarative Shadow Dom
Since Feb 20, 2024 all major browsers support 'Declarative Shadow Dom'. The declarative part means that 'Shadow Dom' can be enabled without Javascript:
<template shadowrootmode="open">
This brings the advantages of isolating Javascript and styling. Obviously, isolation makes sharing styles harder.
React build
Version 3 of the library did not bundle the JSX components. Version 4 added a React bundle.
The import below imports from the source jsx files.
import { Topic } from '@jaccomeijer/green-lib'
While this import uses the React bundle.
import { Topic } from '@jaccomeijer/green-lib/react'
Using the first import, a bundle could be created for e.g. Preact or another JSX Factory. The second is useful for frameworks like NextJs and Astro.
Conclusion
Setting up the library was straightforward because of the simplicity and speed of Esbuild. Having a MDX document right next to the CSS provides for a quick way to draft up a component. It's like developing a library with a Storybook built-in.
Adding a React bundle to the library made it more universal. The library can now be used in React projects and MDX environments.
Nested CSS, Container queries, CSS Layers, etc., the list of CSS improvement in the last six years is long. This improves the Developer Experience. CSS Layers for example, are clearly visible in the development tools of a modern browser. A major improvement over guessing the specificity of a selector in the old days.
Green build and lib
Below are the links to the Github repositories. They're small and simple. Easy to clone and roll your own!