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; }

WooCommerce code snippet: convert the Order Notes field into an EU VAT ID field

The scenario: My WooCommerce store has no need for the Order Notes field. In fact, up until now I had it hidden on the checkout page. But what my site does need is an EU VAT ID field. The portion of my business that takes place in Europe is, so far, well below the VAT reporting threshold, but I am increasingly being asked by customers to provide an invoice containing their VAT ID.

Well, my site does already produce PDF invoices. But there was no way for customers to include their VAT ID on the invoice. Until now.

A simple code snippet converts the existing WooCommerce Order Notes field into an EU VAT ID field, including changing it from a <textarea> to an <input type="text"> field. Put this in your theme’s functions.php file, or wherever else is appropriate in your setup:

add_filter('woocommerce_checkout_fields', function($fields) {
    $fields['order']['order_comments'] = array_merge(
        $fields['order']['order_comments'],
        array(
            'class' => array('eu-only'),
            'label' => 'EU VAT ID',
            'type' => 'text',
        )
    );
    return $fields;
}, 10, 1);

That’s it. You can stop right here. But you may notice a line in there that seems unnecessary: 'class' => array('eu-only')

What’s that all about? Well, I’m using that with a bit of jQuery to enhance the functionality: only showing my new EU VAT ID field when the user’s selected Billing Country is an EU country.

Here’s a JavaScript function you can use to dynamically show/hide elements with an .eu-only CSS class, depending on a given passed-in value:

function showHideEUOnly(val) {
    var eu = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'ES', 'SE', 'GB'];
    if (eu.indexOf(val) != -1) {
        jQuery('.eu-only').show();
    }
    else {
        jQuery('.eu-only').hide();
    }
}

I obtained the list of EU VAT-applicable countries here, and I decided to include 'GB' (the United Kingdom) in the list, despite… y’know, uh… Brexit, because I have the vague impression that UK customers may still be impacted by VAT policies. (Being a dumb American, I don’t know much about it. I think maybe the UK has its own VAT now? Anyway, suffice to say, you may want to modify your list of 2-digit country codes in the eu array, as applicable to your situation.)

This function isn’t going to do anything unless it’s called though, so let’s do that. Here’s a bit of jQuery that will call it both on the initial page load and any time the Billing Country field changes:

jQuery(function() {
    if (jQuery('body').hasClass('woocommerce-checkout')) {
        jQuery('select[name="billing_country"]').on('change', function() {
            showHideEUOnly(jQuery(this).val());
        });
        showHideEUOnly(jQuery('select[name="billing_country"]').val());
    }
});

Both of these JavaScript snippets can go in a script.js file in your theme, or wherever else is appropriate in your setup.

That’s the end of the story, but there’s more…

Incidentally, there’s more to my custom setup. I’ve significantly modified the layout of my checkout page. I’ve got WooCommerce configured for billing addresses only, with this setting in Shipping Options:

I then used CSS to hide everything else in the second column (including, up until now, the Order Notes field) and moved the product summary and payment information up into that space. Explaining all of that is outside the scope of this post, but one thing you may find useful is my CSS for hiding the “Additional Information” <h3> heading. This selector is a bit of overkill, but it works:

body.woocommerce-checkout .woocommerce > form.checkout .col2-set > .col-2 .woocommerce-additional-fields > h3:first-child { display: none; }

There’s context in my CSS file to justify all of that, but you should be able to accomplish the same with just this:

.woocommerce-additional-fields > h3 { display: none; }

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.

https://icscalendar.com/on-the-futility-of-naming-colors/

How I learned to stop worrying and embrace being a middle-aged curmudgeon

Reading John Gruber’s thoughts on the potential future existence of an iPhone SE 4, which necessarily included a note on the lack of an ongoing slot in the iPhone lineup for my beloved 13 mini, got me once again thinking more generally about something that’s been on my mind a lot lately, as my 49th birthday approaches in 2023.

That thing I have been thinking is, this is not the future I signed up for. I’m beginning to understand why older people get set in their ways and cranky about change, in ways that probably could not have occurred to me until I had lived enough to experience a lot of things firsthand.

I remember lots of things, both good and bad. Most things, in general, improve and get better over time. But some things get worse — at least, worse by my subjective standards, because there are certain things I don’t want, like a phone that’s too big for me to use with one hand, or fit comfortably in my jeans pocket.

Possibly even more frustrating than things that get worse are things that just stop existing. Things I really liked that are now gone, especially if they are gone for reasons that I do not think are sound. That happens a lot for me with styles of music or genres of video games. I want new synthwave music* that I can get as excited about as when I first heard Tycho and Com Truise in 2012, for instance, or new Castlevania games that are as good as Aria of Sorrow.

True, sometimes those glories do return, and 2021 gave me a double whammy, in the form of Mitch Murder’s Then Again album and the all-time classic Metroid Dread. But more often than not, I just have to move on and give up on dreaming of a decent modern SimCity game, or a computer Scrabble that will ever be even a fraction as “smart” as the GameHouse version I loved back in 2006.

The world is change. I get that. It’s just hard to let go of things that I know are better than what has replaced them, and that makes me cling desperately to the old things I have that I’m still able to enjoy, whether that’s 40-year-old vinyl records on my turntable or 20-year-old GameBoy Advance ROMs in an emulator.

The true nature of the curmudgeon, I think, is not borne of pessimism. The curmudgeon is not purely a crank. It’s optimism, idealism. Belief in a world that could and should be better than the one we’re living in, because you remember something that’s gone. And beyond that, you remember the trajectory those past things suggested we were on. But somehow we never got where we were going.

Now, let me be clear: I’m not one of those MAGA types who longs for a return to their fictional, idealized version of what the 1950s were (at least, for white people). I’m inspired by the progress we are making towards a more equitable society for everyone — even as I see how far we still have to go. I’m longing for a future world that never was, but that I believed we could — and would — have by now.

Ironically, it was the sci-fi dystopias that were so popular in my childhood in the 1980s, that seemed to get many of the worst things about the 21st century right. The techno-fascism of corporations more powerful than governments, spying on our every action as a way to make more money. Aggregations of the intimate details of millions of people’s personal lives have become the most valuable commodity around. I didn’t believe this was the future we would have, but here we are.

I just want a small phone, some good music to listen to on it (ideally through wired headphones and a 1/8″ jack), and maybe a couple of games that are actually mentally stimulating instead of just “idle” ways to give up money or personal information in an endless stream of “microtransactions.”

Is that too much to ask?

* Of course, synthwave itself is an appeal to the future-should-be-better-than-it-is nostalgia of people (like me) who grew up in the 1980s.

On effective altruism, longtermism, crypto bro billionaires, and the boringness of maintenance

This morning I was reading a Vox article entitled How effective altruism let Sam Bankman-Fried happen, and if that title isn’t enough to scare you off, the first few paragraphs probably will be.

Of course, I am a glutton for punishment, especially when people in their third decade think they have solutions to all of the world’s problems. Not to be a crusty curmudgeon, even though as a “Gen X”er it seems to be my destiny. I just feel like you have to have first-hand memories of the 1980s for proper context on what’s happening today. Or maybe not, but you at least need to be even more cynical about the claimed promise of crypto than the author here. You know, cynical enough to not take the money in the first place.

The problem actually is longtermism

One section of the article begins with the subhead “The problem isn’t longtermism.” But I would argue that, in fact, it is the problem. Or rather, it’s one of a cluster of problems that all come out of the heads of people who think they are smarter than everyone else and somehow have The Answers that are beyond the grasp of mere mortals.

It’s the same mentality that lets a person make the choices (often building on past choices by their forbearers, the ones who allowed them to be “born on third base, thinking they’d hit a triple”) that lead to becoming a billionaire in the first place. It’s a status that can only be achieved on the backs of others.

But whether you’re a billionaire striver, an aspiring Mars colonist, just an ordinary “maker,” or a grandiose philosopher of the distant future, the common thread is that it’s more fun, more inspiring, to invent something — to inhabit a fresh, clean, newly-constructed mental space — than to do the messy, mundane, boring work of maintaining what we already have. Of cleaning up other people’s messes. Of learning how someone else’s invention works, because they’ve moved on to the Next Big Thing and now you’re the one charged with keeping the previous Next Big Thing up and running.

I get it. It’s the same for me, in many ways. I’m a “maker.” I work as a consultant, brought in to create new systems, that then get handed off to internal staff to keep running, while I move on to the next client. But I’m not claiming to solve all of the world’s problems; I’m just making something new that people ask me for when their old thing stops working.

OK, the problem actually is billionaires. Period.

It’s dangerous when the means to solve society’s big problems are concentrated in the hands of a small group of billionaires, and we are left to trust their vision for how that money should be used. These are the guys (and it’s pretty much always guys — bros, if you will) who get that thrill out of grandiose thinking, who don’t want to deal with the messy realities all around us. They don’t want to fix problems. They want to invent something, clean and shiny and new. Don’t clean up Earth; move to Mars. Don’t improve public transportation, distract us with ludicrous promises of tunnels and tubes, so we buy your fancy new cars instead. Don’t do the hard, complicated work to improve the actual lives of the billions of humans who are living in poverty today; concoct some imaginary future event that might kill billions of people, and invent a fancy new high-tech gizmo that will prevent that event from happening.

Later on, the article does get into a lot of the problems with big longtermist philosophy and silver spoon crypto bros who think they understand all of the world’s problems better than anyone who actually has any of those problems in their own lives.

But ultimately, for me, it all simply comes back to the idea that billionaires shouldn’t exist. And since they shouldn’t exist, they shouldn’t be making decisions about how to fund solutions to society’s problems.