New CSS to fix Chrome breaking old CSS (image size/aspect ratio)

Last week I had several clients report to me that their websites were suddenly displaying images at the wrong size — too small. I checked their sites (in Safari, my default browser), and everything looked fine.

Then I realized, it was a bug in the new version of Chrome. (At least, it seems like a bug to me, when CSS code I’ve been using consistently for over a decade suddenly doesn’t render properly in just one browser, right after that browser has been updated.)

Here’s the code I had been using:

img {
  height: auto;
  width: auto;
  max-width: 100%;
}

I discovered that by just removing the width: auto; line, the images rendered at the intended size, and this didn’t seem to break anything in other browsers either. Sweet!

Unfortunately today I had a client contact me with what I initially thought was the same issue. But then they said it was affecting Firefox and Edge as well. (Windows user!) Sure enough it’s also a problem in Safari.

But this time, it’s not that the images are rendering too small; it’s that they’re getting stretched and distorted. Then I realized I had already applied my fix for that client’s site, and it was causing this new issue.

Fortunately there’s another simple CSS fix. (I briefly considered the aspect-ratio property, but that’s not flexible enough for what I’m trying to do.) It’s a newer CSS property that I haven’t used much: object-fit.

It’s really quite simple though. Here’s the new code I’m using that seems to resolve the issue completely. But I suppose I’ll get another email from someone next week describing another edge case where it doesn’t work. Stay tuned…

img {
  height: auto;
  max-width: 100%;
  object-fit: contain;
}

Update (11/21/2024): OK, so maybe as with the PNG-to-HEIC-to-JPEG glitch, this was actually more of a WordPress 6.7 issue. By the way, I reported the PNG glitch and it got patched in 6.7.1, as did this issue… assuming it’s the same issue I’m describing here, which kind of sounds like it. In retrospect it does seem kind of odd that Chrome and WordPress would both introduce image-related issues on the same day. Much more likely that WordPress introduced multiple issues — or, well, maybe just revealed browser issues, but still. Gotta blame someone.

An even dumber workaround for how dumb CSS hyphenation is

Look, it’s all well and good that CSS has a hyphens property. The problem is, that property is really dumb. It’s all-or-nothing, with no rhyme or reason to whether a word absolutely needs to be hyphenated. It will literally hyphenate any and every multi-syllable word at the end of a line.

You really almost never want that.

In my particular case, I’m looking at a very specific situation. Specific, but I am guessing probably the most common situation where a web developer wants a browser to hyphenate words: words in large headings that are too long to fit on a line. What I mean here is individual words that are by themselves too long to fit on a line, typically in a mobile browser, in headings with large or extra-wide fonts.

There are proposals to improve this, but there is currently nothing with broad browser support. So I invented my own.

This is a crafty little combination of CSS and JavaScript. (OK, technically it’s jQuery, but you could reasonably adapt this to vanilla JavaScript if that’s your thing. Since I’m deeply immersed in the jQuery world of WordPress, I just went with jQuery because it’s simpler for me that way.)

The first thing you need is a special CSS class for hyphenation. Since I only want it to apply on mobile devices, I gave it a logical name, and defined it in my CSS media query for the mobile breakpoint:

@media screen and (max-width: 782px) {
    .hyphenate-on-mobile {
        -webkit-hyphens: auto;
        hyphens: auto;
    }
}

OK, with that set up, then we just need a little jQuery function to determine where it’s going to be applied. I want it to get added automatically to any h1, h2, h3 or h4 tag. (I’m skipping h5 and h6 because they’re small enough text that we shouldn’t need it.) I also made the somewhat arbitrary decision to set the minimum word length at 15 letters. This is something you may need to adjust based on your font size. Here’s the jQuery:

jQuery('h1, h2, h3, h4').each(function() {
    var words = jQuery(this).text().split(' ');
    var i = 0;
    var hyphenate = false;
    while (i < words.length) {
        if (words[i].length >= 15) { hyphenate = true; break; }
        i++;
    }
    if (hyphenate) {
        jQuery(this).addClass('hyphenate-on-mobile');
    }
});

And then you just want to make sure that jQuery gets fired off when the page loads. It works!

Stupid jQuery and CSS tricks: Move a hidden element to the end of the parent

I just came up with a solution so simple, yet so stupid, that I had to share it.

The scenario is this: my client has some third-party JavaScript to inject a form into a page. I’ve been writing some of my own CSS to style the forms, including some crafty use of :nth-of-type(2n) and :nth-of-type(2n-1) to apply styles conditionally to adjacent fields in a two-up layout. (These are just sequential <div> tags, and I have to work with what I’ve got.) Specifically, I’m adding some right margin on the “left” element, and no right margin on the “right” element.

I noticed an instance where my margins were flipped for two fields, and when inspecting the code, I discovered why: the client has set up their form with a hidden field tucked in there before the “left” element. That’s throwing off the value for 2n in my CSS. I was going to contact them and ask them to update their form to put the hidden element at the end, but I realized this is a problem that is likely to recur, so I should just write in a workaround. (Yeah, yeah. But it’s the lesser of two evils.)

Fortunately, the site is already using jQuery, so a fix was super simple. I’m leaving the CSS class names here as they are in the actual site, but you may need to change them to suit your particular form.

jQuery('.form_container .form_page .form_question.hidden').each(function() {
    jQuery(this).appendTo(jQuery(this).parent());
});

Since the hidden field has the .hidden CSS class, it’s really easy to use the jQuery .appendTo() method to just take all of the hidden fields and shove them to the end of the container element. I used the parent and ancestor CSS classes in my jQuery selector just to be sure I’m isolating this action to these particular forms. Then of course I have to tell the page when to execute this functionality, which is ideally on the load event:

jQuery(window).on('load', function() { /* Code goes here */ });

It works!

A simple way to add responsive (breakpoint-specific) blocks in Gutenberg

The new WordPress Block Editor (a.k.a. Gutenberg) has improved immensely over the years, to the point where I am now willingly (although occasionally grudgingly) using it as my main development platform for client sites.

One area where it really falls down though is in smartly handling responsive breakpoints. There is some effort to make it responsive-friendly (though it’s definitely not “mobile first”), but there are really weird holes, like the fact that there’s no built-in way to apply consistent left and right margins on text content at smaller screen sizes; once you’re below the defined content width, if you don’t manually configure your theme to add padding/margins to headings, paragraphs, etc., and they don’t have a defined background color, the text will bump right up against the edges of the screen.

That’s not the problem I’m here to solve at the moment, however. Instead, I want to focus on a handy little tool I’ve been using in my WordPress development for over a decade: specialized CSS classes to designate blocks of content as only appearing on phones, or not appearing on phones.

By “phone” here I just mean the smallest standard breakpoint. I’m using the WordPress convention (at least it used to be the WordPress convention; I don’t feel like I can trust anything anymore) of setting that at the slightly odd (although even) value of 782 pixels.

What can you do with this? Once you have CSS classes that designate a block as only appearing on phones or not appearing on phones, then you can easily tailor your output to certain screens. Obviously (at least, I hope it’s obvious) this is not the ideal solution; it means you’re delivering HTML content — potentially redundant HTML content — that will not be seen by some users. But nothing in web development is perfect, and this can be a convenient way to get out of a pinch when, for example, you have a very complex desktop layout element that simply cannot be adapted in a usable way on phones. You can just hide that element on the phone breakpoint and, if desired, present the same content in a more simplified way to only phones. (Again… only if you can’t achieve a usable phone experience on the same HTML block via media query-specific CSS.)

OK, let’s get down to it. The first thing you need to do is register a couple of block styles. I am only making these styles applicable to the Group block, because that seems like a logical way to rein it in. Any block can easily be wrapped in a group, so if you need this feature, just do it.

register_block_style('core/group', array(
  'name' => 'no-phone',
  'label' => 'No phone'
));

register_block_style('core/group', array(
  'name' => 'phone-only',
  'label' => 'Phone only'
));

That should go into your functions.php file or wherever in your theme you are defining block characteristics. Ideally it should be in a function that is executed on the init action. Next, you need to make sure you’ve got a custom block style .css file enqueued for the Group block. You can place this wherever is appropriate within your theme; I happen to have a nested assets/css/blocks folder where I put mine. Here’s that bit of code, also for your function that runs on init.

wp_enqueue_block_style('core/group', array(
  'handle' => 'my-theme-core-group',
  'path' => get_theme_file_path('assets/css/blocks/core-group.css'),
  'src' => get_theme_file_uri('assets/css/blocks/core-group.css'),
));

And then in the referenced core-group.css file, you’ll need this:

@media screen and (max-width: 782px) {	
  .wp-block-group.is-style-no-phone {
    display: none !important;
  }
}

@media screen and (min-width: 783px) {
  .wp-block-group.is-style-phone-only {
    display: none !important;
  }
}

Now in the Block Editor, when you switch to the Styles tab when focused on a Group block, you’ll see your new style options:

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


Update (April 4, 2023): Yeah, don’t do this. You can specify these colors in theme.json. I’m not sure if this is a recent addition, the documentation was previously lacking, or I just didn’t find it, but anyway… do this instead.