Back To Top

Solved With :has(): Vertical Spacing in Long-Form Text

Solved With :has(): Vertical Spacing in Long-Form Text

It’s surprisingly tricky to get this right. And it’s one reason why things like the Tailwind Typography plugin and Stack Overflow’s Prose exist — although these handle much more than just vertical spacing.

Surely it should just be as simple as saying that each element — p, h2, ul, etc. — has some amount of top and/or bottom margin… right? Sadly, this isn’t the case. Consider this desired behavior:

You need to look no further than right here at CSS-Tricks to see where this could come in handy. Here are a couple of screenshots of spacing I pulled from another article.

Then we add CSS to select all typographic elements in that wrapper and add vertical margins. Noting, of course, the special behavior mentioned above to do with stacked headings and the first/last element.

The traditional solution sounds reasonable… what’s the problem? I think there are a few…

Having to add a wrapper class like .rich-text in all the right places means baking in a specific structure to your HTML code. That’s sometimes necessary, but it feels like it shouldn’t have to be in this particular case. It can also be easy to forget to do this everywhere you need to, especially if you need to use it for a mix of CMS and hard-coded content.

The HTML structure gets even more rigid when you want to be able to trim the top and bottom margin off the first and last elements, respectively, because they need to be immediate children of the wrapper element, e.g., .rich-text > *:first-child. That > is important — after all, we don’t want to accidentally select the first list item in each ul or ol with this selector.

In the pre-:has() world, we haven’t had a way to select an element based on what follows it. Therefore, the traditional approach to spacing typographic elements involves using a mix of both margin-top and margin-bottom:

Collapsing margins are yet one more thing to be aware of. It might be confusing for junior devs who aren’t up to speed with that CSS quirk. The spacing will totally change (i.e. stop collapsing) if you were to change the wrapper to a flex layout with flex-direction: column for instance, which is something that wouldn’t happen if you set your vertical margins in a single direction.

I more-or-less know how collapsing margins work, and I know that they’re there by design. I also know they’ve made my life easier on occasion. But they’ve also made it harder other times. I just think they’re kinda weird, and I’d generally rather avoid relying on them.

And here is my attempt at solving these issues with :has().

To recap the improvements this aims to make:

So there you have it, a bleeding-edge solution to a very boring problem! This newer approach is still not what I’d call “simple” CSS — as I said at the beginning, it’s a more complex topic than it might seem at first. But aside from having a few slightly complex selectors, I think the new approach makes more sense overall, and the less rigid HTML structure seems very appealing.

If you end up using this, or something like it, I’d love to know how it works out for you. And if you can think of ways to improve it, I’d love to hear those too!


If you need help creating a digital marketing strategy for your business, don’t hesitate to contact one of Digidude’s consultants.

Post a Comment

We use cookies to give you the best online experience. By agreeing you accept the use of cookies in accordance with our cookie policy.