Quick CSS fix for WordPress Block Editor (Gutenberg) link hover color issue

The WordPress Block Editor (a.k.a. Gutenberg) makes it easy to set the text, background, and link colors on any block. But links can and often do have more than one color. And there’s no option here for setting the hover color. So what do you do in what, I think, may be a common situation, where you’re setting a background color on a block and making the link text white, but your theme’s link hover color is either the background color you’re switching to, or something way too close to it?

I’ve come up with a very tidy bit of CSS code that will make your link hover state match the custom link color — granted, you lose the UX of a unique color on hover state, but you gain necessary legibility and accessibility, which I guarantee is more important.

This may not work in every situation, but it’s so simple that it’s worth investigating as an option. With this code, any time you have a block that sets both a custom background color and a custom link color, it will ensure that the hover/focus state matches the custom link color:

main .has-background-color.has-link-color a:focus,
main .has-background-color.has-link-color a:hover
{ color: inherit; }

Bootstrap 5 Carousel: position captions outside (i.e. below) the images

(If you want to get right to the point, a full code example is provided at the bottom of this post.)

I’m not a Bootstrap expert. During most of its evolution, I’ve mostly ignored it in favor of rolling my own… everything. I finally really embraced Bootstrap when I had a rush project in October 2022 that was way too precisely designed to work with WordPress, especially Gutenberg. (And the client didn’t need editing capabilities.) So I decided to hand-code it, but to use Bootstrap to… uh… bootstrap my HTML/CSS layouts.

I think Bootstrap 5 is excellent. I wish Gutenberg was built on top of it instead of the idiosyncratic house of cards it’s actually built on, but whatever.

As it happens, I’m actually now using Bootstrap 5 with Gutenberg for some custom blocks, specifically a Carousel block. One of the options I want to provide in my block is the ability to show the carousel’s captions and controls outside of the image, but apparently, at least with Bootstrap 5, that’s not an option.

I decided to Google for a quick solution before creating my own and I came across… this. I’m sure it gets the job done, but it seems severely over-engineered, so here I’m presenting my own comparatively simple, CSS-only solution.

Basically there are two things you need to do: 1) move the absolute-positioned caption text below the image, and 2) add padding to the bottom of the container, so the caption has somewhere to go instead of just overlapping the content below it.

Let’s start with the second item first:

.carousel { padding-bottom: 4rem; }

There may be some trial and error here, as you need to make sure you’re accommodating captions of varying length. I will admit this is not fully thought out here, and unlike the rest of what is about to follow, it may be a deal breaker under certain circumstances. But let’s assume your captions are a fairly consistent length, and you can determine how much padding you need.

Getting the caption pushed below the images is easy…

.carousel-caption { top: 100%; }

…except, oops, vertical overflow is hidden. Let’s fix that:

.carousel-inner { overflow: visible; }

Of course, if you have your transition effect set to slide (which is the default), that now spews stuff all over the page in an ugly way. But we can fix that by hiding overflow on the outer carousel element instead:

.carousel { overflow: hidden; }

You might, at this point, wonder why I didn’t just set overflow-y: visible on .carousel-inner which seems perfectly reasonable, and which, of course, I tried. But for reasons I couldn’t be bothered with investigating, that ended up causing .carousel-inner to just show a vertical scrollbar and not display the caption unless you scrolled it. Ugh. No matter, the above takes care of it.

That’s pretty much it, as far as the captions go. But if you’re using the controls (previous/next arrows) or indicators (dots/lines for the number of slides and current selection), you’ll notice there’s some weirdness to their placement, so let’s fix that too. The indicators just get shoved to the bottom of the container, so your bottom padding can accommodate that. But if you want to move them back up onto the image, you just need to offset that extra padding, like this:

.carousel-indicators { bottom: 4rem; }

Make that value the same as the bottom padding you added to .carousel itself.

As for the controls, since you’ve made the overall container taller, they’re now a bit too low rather than being vertically centered on the image. Guess what… setting their bottom value to match the extra bottom padding fixes their placement too!

.carousel-control-next, .carousel-control-prev { bottom: 4rem; }

So, putting it all together as concisely as possible, here’s what we have:

.carousel { overflow: hidden; padding-bottom: 4rem; }
.carousel-caption { top: 100%; }
.carousel-inner { overflow: visible; }
.carousel-control-next, .carousel-control-prev, .carousel-indicators { bottom: 4rem; }

On the Futility of Naming Colors

I posted this over on the ICS Calendar blog, but it’s probably of some interest to the audience of this blog, dealing with the intersection of code and design.

HTML has “named colors” which are… weird. And I have finally given up on using them in the plugin, which was a bit of a silly thing to do anyway, when there are 16 million RGB colors at one’s disposal. The post ends with swatches of both the old and the newly revised color palettes.


This is the kind of thing that I find maddening about Gutenberg

First off, if you are the regular reader of this blog, you are painfully aware of how much I am waffling on rejecting or embracing Gutenberg (a.k.a. the WordPress Block Editor). There are things I genuinely do like about it, but I also have some fundamental disagreements with its approach (like the facts that it writes inline CSS directly into post content, or that its templates don’t support PHP code).

And then there are the weird little quirks that just make using it much more difficult than it needs to be. In general I have learned that this comes down to the incredible finickiness of JSON. Fine. But the fact that one character out of place in the theme.json file can break everything seems a bit ridiculous… or at least, a big step backwards for usability.

Fix your syntax. Sure. I wish JSON allowed trailing commas the way PHP — and even JavaScript! — does, but I can grok the fact that it doesn’t and learn to adapt. What I have a bit more trouble with is the problem that struck yesterday, only because it was so hard to pin down.

I’m working on a new, small block theme as a one-off for a unique project — a website that will feed menu board monitors in a pizza place. Seems like a perfect opportunity to practice and experiment a bit with what Gutenberg can do. (And, after over a decade of trying to find the “right” way to give restaurant staff a reliable but still easy-to-manage web interface for updating menu information, I actually think Gutenberg’s Block Patterns might be the optimal solution.)

So here’s the maddening thing. I was setting up my theme.json file like a good Block Theme developer, defining all of my colors, typography, etc. And then I started loading in some test content. All looked fine on the back end, in the Block Editor itself. But none of the styles were getting applied on the front end!

I googled the problem, of course, and found a WordPress forum thread about this very issue.

Huh. Sure enough, what’s described in the thread was exactly what was happening in my case. I had this line in my code (font names changed to protect the innocent):

"fontFamily": "'Helvetica Neue', 'Comic Sans', 'sans-serif",

Do you see the problem there? (No, it’s not the trailing comma… that actually belongs, in context.) It’s the stray apostrophe before sans-serif. Removing that fixed the problem.

Now, if this were just in straight CSS, I would have seen the problem immediately, because the syntax highlighting in BBEdit would’ve gone all wonky. But since this was inside a text string in JSON, it looked just fine, and I didn’t notice the apostrophe was there.

I get that the parser that converts the contents of theme.json into CSS would get thrown off by this. Honestly, I’m surprised it didn’t cause the whole page to bork with a PHP fatal error… I’m pretty sure earlier versions did throw a fatal error if there was any problem parsing the theme.json file. So this is… an improvement?

I also get that the generated CSS output would not work properly. What I don’t get is that it all looked correct in the Block Editor. Why was the Block Editor on the admin side able to “fix” this issue, but on the front end it didn’t? It doesn’t make sense!

Anyway, trying to unravel that mystery wasted about an hour late yesterday afternoon, when I was finally starting to get productive after having a near existential crisis over Gutenberg for most of the week.

These are hard times to be a WordPress developer. No, strike that. These are hard times to be a generalist web developer who happened to make the fateful decision 8 years ago to go all-in on WordPress.

Making Bootstrap work with the WordPress Block Editor

tl;dr: You need to load Bootstrap’s CSS in the 'wp_enqueue_scripts' hook with a priority lower than 10.

Where do I begin with this one? First, some foundational details: I’m old school. I’ve been a professional web developer/designer since 1996. I’ve embraced new technologies as they come along, but I tend to bristle at things that are either a) not an open standard or b) need to be compiled.

I refused to use Flash. In the end I was proved right, as open web technologies supplanted it. Flash is dead, and the open web is not. I still refuse to use CSS preprocessors for a similar reason. They’re a non-standard workaround to limitations in the current standard. They fragment the landscape instead of pushing the standard forward. And as CSS variables have gained wide browser support, preprocessors are beginning to look as pointless as Flash.

Now the thing I hate is npm — or any similar tech that requires me to run shell scripts and compile anything. I’ve long since embraced server-side scripting, using open source libraries like jQuery, and even using a pre-built CMS (or CMS-ish system) like WordPress. (Yes, for many years I rolled my own CMSes.) The bottom line for me is, if it’s code I can download simply and treat as a “black box,” fine. But if it’s something I need to get my hands dirty in, I shouldn’t need anything to work with it but a text editor.

All of that to say, I am having a bit of a time with where things are going these days with Gutenberg, a.k.a. the WordPress Block Editor. And I haven’t exactly been quiet about it here.

This week I needed to extend the Block Editor by creating a carousel/slideshow block. Yep, I’ve rolled my own with these for many years as well, but this time around I decided I wanted to work with something pre-built, so I settled on the one in Bootstrap. I don’t really need all of Bootstrap (although I suspect over time I will need more of it), but customizing it requires using npm, so I decided to go ahead and include the entire pre-compiled package in my theme.

That’s when the problems began.

Oh, the carousel works great! But I spent a bunch of time yesterday trying to figure out why the custom background color and fonts defined in my theme.json file were being ignored… especially since they’re right there in the inline CSS inside the <head> of every page.

Don’t even get me started on inline CSS, or the way so many people in the industry seem to worship PageSpeed Insights. Once again we’re skating to where the puck is, instead of where it’s going, and to stretch the analogy to the limit, we’re melting all of the ice in front of it too. This is not the way to move web development forward intelligently.


Eventually I worked out what’s happening. WordPress, historically, was designed to allow you to define dependencies, so you could load CSS and JavaScript in a logical manner. Gutenberg/Block Editor throws all of this out the window with this inline CSS. Certain “critical” inline CSS for the Block Editor gets loaded immediately regardless of the dependencies you put into wp_enqueue_style(). The result being, styles defined (indirectly, in a way more convoluted way than vanilla CSS) in my theme.json file were appearing before the Bootstrap CSS file was loaded. And since I’m using the compiled Bootstrap instead of a custom npm build, there’s a bunch of “general” CSS it’s throwing in, including color and font definitions on the <body> tag that override Gutenberg’s earlier inline CSS.

Fortunately, someone else had the same problem and figured out a solution. But since, in 2022, spammers overrun any forum thread that’s left open too long, the thread was already locked and I couldn’t express my appreciation to the poster who shared it. So I’m writing this blog post instead.

The trick is to load any third-party CSS that you might need to override using a separate function called on the 'wp_enqueue_scripts' hook, with a low priority number (less than 10, since that’s the magic default).

Here’s a generalized version of the code I’m using:

function my_enqueue_scripts_early() {

    $ver = '1.0.0'; // Your theme version; could also be a class property, constant, global variable, etc.

    wp_enqueue_style('bootstrap-style', get_theme_file_uri('vendors/bootstrap/bootstrap.min.css'), array(), $ver);
    wp_enqueue_script('bootstrap', get_theme_file_uri('vendors/bootstrap/bootstrap.bundle.min.js'), array('jquery'), $ver);

add_action('wp_enqueue_scripts', 'my_enqueue_scripts_early', 8);