Site Theming Adventures 2025.
Ah, element semantics, styles, the joys of coding for the web.
For about a year this site has run on a WordPress Block Theme. The idea behind WordPress Blocks is roughly, “Let’s make a design system on top of hypertext, stylesheets, and Javascript, so that mere users can edit WordPress sites for themselves.” A noble goal. For my uses and tastes, that system isn’t good enough, and it hasn’t shown signs of going in the direction to become good enough.
Most of the friction comes down to the idea that the implementation should handle some generic case without affordances for complete control. If you want to wrangle some arbitrary part of the system, you’ll need to write a drop-in replacement or find a plug-in. That being the case for someone like me, I’d rather opt-in to it from the start without the extra layer of blocks getting in the way.
By choosing to build a classic theme, I sign up for that deal. Some of the WordPress functions have conflicting designs that make it harder than necessary to customize. I replace them as needed, filter them where I can, and so on. But they let me go write code to patch over deficiencies without wrestling with the Blockiness of things at the same time.
The other problem with Blocks is that they insert themselves into the content markup too much. If down the road I want to leave WordPress behind, I’ll have a lot of old crufty posts with .wp-block-classes inserted everywhere. (That’s already the case on my local test blog, where when I decided to build a new classic theme I saw just how much Blocks had invaded it.)
The whole idea of a CMS is supposed to be that it separates the concern of frontend from the content in backend. But it doesn’t do that with Blocks. So for now I’m trying to move away from them.
Compacting CSS.
I used a tool clean-css the last time I built a classic theme around 2022. It worked well, but I ran into some issues when using more modern CSS features like nesting, so I had to go dig up Lightning CSS. As I formerly used cleancss from the command line, I wanted to do the same with its replacement. That meant digging around to find the executable since Lightning CSS is distributed via NodeJS.
From there it was mostly a drop-in replacement. It has slightly different features, and I don’t think it is as aggressive about minification at the moment. It compacts things, but it doesn’t seem to dig for places to combine styles or things like that (at least not much). Maybe the tooling will improve.
But for now, you can save internet points (the less bandwidth you use, the more internet points!) by e.g., using nested styles.
Here’s an example:
textarea:hover,
textarea:active,
input:hover,
input:active,
button:hover,
button:active {
border-style: solid;
}This, when minified using lightningcss, becomes:
textarea:hover,textarea:active,input:hover,input:active,button:hover,button:active{border-style: solid}That’s 103 characters.
textarea,
input,
button {
&:active,
&:hover {
border-style: solid;
}}
And this second one, when minified using lightningcss, becomes:
textarea,input,button{&:active,&:hover{border-style:solid}}That’s only 59 characters.
It’s not always going to be a savings. It’s very much golfing with how much repeating a rule costs versus a selector. But in the right circumstances, it will make the stylesheet smaller to distribute.
One area you want to be careful of is with hyperlinks if you style :visited links differently than regular ones. I went off on a brief tangent of trying to do something like:
a:any-link {
--link-color: blue;
color: var(--link-color);
&:visited {
--link-color: purple;
}
}I am well aware that there are privacy features involved around :visited links, to prevent people from being able to tell where you’ve surfed before. It makes enough sense to have those. I just think some of them aren’t implemented very well. Like the above. As long as the consumption of that custom property (link-color) happens within the visited link, there shouldn’t be a way for someone to sniff it out. But apparently it’s too risky, so not allowed.
(Another example is in Firefox developer tools, accessibility tab, where you can check color contrasts. But you can’t check color contrast for :visited links’ link colors against their background! The browser lies about what link color it is. To your face. Via its graphical lying interface! I’m not sure who that is protecting, but in all likelihood it’s down to the devtools simply using the same code paths as server-offered Javascript would, thus hitting the same Protectatron 4000 that bounces it away.)
The fancy colors I cannot (yet) see.
There’s outer space, there’s cyberspace, but did you know there’s also color space? You may or may not be reading this on an RGB monitor. But newer monitors apparently have bigger color spaces. To use them, you can supply colors with things like oklch() (see Oklab color space). But if you do, some of those colors are non-existent, out-of-gamut. Think about it like flying into a black hole. (Err, an unspecified-color hole.)
In any case, the current Block theme has some of these fancy new colors, but I can’t see them. They get turned into RGB colors, which I can see. For the new theme, at least until the future when I have whatever hardware or software these new colors require, I’ll stick to RGB colors. The extra good news there is that writing a bunch of oklch() colors takes up a lot of space (I guess for the extra colors and the deadzones that house non-colors), where regular old RGB hexadecimal notation is nice and compact.
Perhaps by the future, oklch() and its high-colorspace friends can get some compact formats?
The woes of light and dark.
The other issue I ran into with colors was the fancy new light-dark() color specifier for CSS. By setting color-scheme: light dark; on some element (usually the root html element), you can automatically have any light-dark() declaration choose the color (light or dark, in that order, as in color: light-dark(springgreen, midnightblue)) that the user prefers.
There are several small problems. One is that I have to pick twice as many colors now, and I am pretty bad at picking colors. Another is that it doesn’t minify very well. You have to repeat those 13 characters light-dark(,) for every color. Throw them in a custom property, sure:
--color: light-dark(#abc,#def);But you still have all that wasted space. If you have a foreground, a background, a link, a visited link, a hover or focus, an active, and a few other accents and things, you start to run into say eight or ten times 13, you’re looking at more than a tenth of a kilobyte! Think of the internet points, when a regular picture might run as much as 1000 times that!
Semantic tags but numbered headings?
One of the nice things about modern hypertext is how there are many more nice elements to properly markup your site with. Things like article and header and footer go a long way. On the other hand, the headings continue to be a drag. These are your h1, h2, h3, h4, h5, and h6.
Can we get an :any-heading? Or just move to a generic heading element? Ah well. Your best bet is, where possible, nesting again. But you can’t do reverse nesting, where you want to select based on parentage. There is :has() for looking in the children, but there isn’t a way to look up toward root because we have normal selector rules for that. That means if we want to specify the parental context of headings, it can’t take advantage of the nest.
We can simplify some older styles of rules where we would have used :where():
:where(h1,h2,h3,h4,h5,h6) + a {
font-variant: small-caps;
}
:where(h1,h2,h3,h4,h5,h6)+a{font-variant:small-caps}That can become:
h1,h2,h3,h4,h5,h6 {
& + a {
font-variant: small-caps;
}
}
h1,h2,h3,h4,h5,h6{&+a{font-variant:small-caps}}The reformulation saves us five whole characters (52 characters for the original versus 47 characters for the nested form).
Now I can hear you dragging your mouse to go find a more productive article that can save you more than five stinkin’ characters. Fair enough. But you won’t get all the internet points, now will you?
I’m still poking at the new theme. The dirtier coding bits are left to the reader’s imagination. But I am glad to be getting back on the classic side of things.