Two line balanced inline block

Page published on

There was a question asked on Mastodon by Pascal Laliberté:

Hey #CSS folks…

Anybody know how to pull off a box like this:

screenshot of a mockup showing three boxes side by side, occupying two lines per box, with text balanced to occupy the two lines but not grow in width.

As things are this is impossible to achieve reliably with current CSS: we have no tools to force a box to shrink so that it attempts to size itself to always result into two lines of text. So let us go through the closest matches!

The restrictions

“Feels like inline-block” was mentioned in the quote above. The issue with it is that when inline block has text long enough to wrap to multiple lines it will take all the available space. We can’t force it to take the least space available when wrapping.

The closest rule that we get by technical functionality is the old proprietary -webkit-line-clamp which has been implemented in all browsers, but it doesn’t do what we want: while you can use it to tell how many lines of text you want to see, it achieves this by truncating the extra away and adding three ellipses if it does so.

Then we have min-content value. The problem with it is that it wraps to absolute minimum width:

I’m a Hello World box

You could overcome this issue by selectively placing non-breaking spaces:

I'm a Hello World box

I’m a Hello World box

… but that would either mean extra manual work for the people writing content, or writing a piece of server code that would determine where to keep the regular space.

So, are we done? We have a solution, albeit not a very CSS one.

Duplicate it!

Of course, me being the “if it is possible to be done in CSS I will make it happen” sort of guy, I did manage to find a solution to this problem. And of course it is a hacky solution since we are talking about something CSS doesn’t have.

The idea is to have the text in DOM twice: one is for visual representation, but another is added in half the size and that would determine the minimum width of the box when using fit-content. The half-size text would have white-space: nowrap.

Here is the minimal implementation of that idea, keeping the text visible so you can see what is happening:

<span style="border: thin solid; display: inline-block; padding: 4px; width: min-content;">
	<span style="display: block; font-size: 50%; white-space: nowrap;">I'm a Hello World box</span>
	<span>I'm a Hello World box</span>
</span>

I’m a Hello World boxI’m a Hello World box

Very much a success! There is a bit of extra space to the right but that usually isn’t an issue as long as we have roughly the right width for the box. However the above trick doesn’t work as intended for all the cases:

Web Components are the bestWeb Components are the best

There are multiple ways to try fix this issue:

  1. Write a regular expression that replaces space with &nbsp; when there is a combination of short + long word at beginning and end of the text.

    Web Components are the bestWeb Components are the best
  2. Adjust the small text to take more space.

    Web Components are the bestWeb Components are the best
  3. Use soft hyphens.

    Web Components are the bestWeb Compo­nents are the best

The first fix is almost good enough for almost every case since the problem does happen mostly when there is not enough text.

The second fix helps with the issue, but has the downsize of adding even more empty space to the right side of the box.

The third fix can also help with the issue by breaking the text to smaller parts, however the added hyphen does take a bit more space so you should account for that (by also using the second fix).

Combine these fixes and you are likely to get very close to never seeing three lines of text!

Making it good for production

We now have the technical wrapping very close to that we want, but we are not really good for production. The main issues we currently face are related to the duplicate text:

  1. It effects our layout.
  2. It is accessible.
  3. It can be read by a screen reader too.

There are also other cases we have not yet accounted for:

  1. Limiting to a maximum width of 100%.
  2. Preventing horizontal scrollbars in limited space.

To demonstrate the issues here is a simulation of what happens in limited space:

This is some lorem ipsum without it being lorem ipsum so that you can see how this text wraps.

Web Components are the best

Web Components are the best

The desire to go for two lines of text has become too extreme here: we would rather want the text to wrap to more than two lines than to cause a horizontal scrollbar to appear.

Hiding the smaller text

We can effectively hide the 50% text with visibility: hidden, and remove the effect to layout with height: 0:

Web Components are the bestWeb Components are the best

This also removes the text from screen reader, but just to be sure we can also make use of inert attribute on HTML side to be perfectly sure the element will not be part of any other use than to be the measure of minimum width.

Limiting the width and preventing scrollbars

The problem with white-space: nowrap is that the text still takes all the space it needs even if we don’t see it, and this will be a source of (sometimes mystical feeling) horizontal scrollbars. To counter it we can use contain: content on the parent element.

Finally, to allow the parent element to be smaller than min-content we can use max-width: 100%. The following has all the new changes applied:

This is some lorem ipsum without it being lorem ipsum so that you can see how this text wraps.

4rem

Web Components are the bestWeb Components are the best

3rem

Web Components are the bestWeb Components are the best

2rem

Web Components are the bestWeb Components are the best

Hurrah, no horizontal scrollbar! We could still polish the visual end result with the fresh text-wrap: balance.

Here is a more complete sample to play around with:

Use cases

There is one place where this kind of behavior could be desirable: buttons!

This link pretends to be a buttonThis link pretends to be a button

Usually with buttons you have to resort to a mixture of specifically designed widths and agreements with localization folks for certain length of text so that you can get a desired visual end result.

With this solution you can tell your designer that hey, we can actually now do this with some dirty HTML and CSS trickery. You have to be careful for the designer not to fall in love with you so make sure to ruin their day with something else to maintain safe boundaries.


Return to the blog