Jess Mitchell

Element of Unknown Height with Text of Varying Length

May 30, 2019

A CSS Solution to Show Text When All Your Dimensions and Values are Unknown

I spend a lot of time in React these days and every once in a while I have to style something that makes me think, “There must be a way to do this in pure CSS. If only I knew how…” I try to avoid JS solutions in styling as much as possible because CSS usually has an elegant solution and is much more performant in these cases anyway.

I recently had the task given to me to style a component that has:

  1. Text but the length will vary or might not even exist.
  2. An unknown height at the time of initial rendering. (The height was based on a variable not related to the text length.)
  3. Multiple instances of this component so the solution had to be dynamic.
  4. Not only could the height and width vary, the height was resizable using interact.js.

Ultimately, the goal was to only ever show the amount of text there was room for, including on resize. A line of text should never be cut in half on the vertical axis due to the parent element being too short.

Here is what the component looked like before finding a solution:

Text getting resized and lines getting cut off
Back when I hated this component, a.k.a. pre-solution.

How could I know if there was room for the next line of text if I the height was changing regularly? Sure, you can use a ref to get the height but I didn’t want to calculate it on change to pass to the styling. I’d have to debounce it, etc. Too much work.

There had to be a way of doing this in CSS!

And of course there was because I’m writing a blog about it.

I ended up finding a solution deep in the comments of a Stack Overflow thread that was mostly unrelated to my issue and definitely not written recently. All the makings of a great solution, right?

The solution was to use: column-width.

column-width sets the width of a column in an element and pushes whatever text doesn’t fit in the first column to the second (or third or nth) columns. The number of columns will be equal to the number of columns needed to show the text.

That meant that the text that didn’t fit the the text element would be pushed to a new column. The width of the parent element was set so, with an overflow: hidden; added, I could push the text that didn’t fit to additional columns and just not display them.

The solution looked something like this:

Text getting resized and lines do not get cut off
New favourite component, a.k.a post-solution

The parent container has the width and height values set using CSS. The child component, which contains the text, has the column-width set to the parent width minus padding and overflow: hidden.

So whenever the height resizes, the text that doesn’t fit gets pushed to the next column.

No calculations needed! 🎉

One thing to note is that the first column needs to be at least one line long, so if I tried to hide all the text, the first line got cut off vertically. The solution here was to conditionally show the text only if the height of the component (minus padding) was more than the line height of the child component.

If you’re not totally sure what’s happening in the example above, here it is again with the overflow: visible;

Text getting resized and text getting pushed shows on side
column-width to the rescue

Such a satisfying CSS win. You probably won’t ever have exactly the same requirements as I did but if you find yourself cringing at text getting cut off, consider column-width. Hopefully it can help you avoid killing an entire afternoon. 🤷‍♀

P.S. If you’re curious about interact.js, it’s a great tool for dragging/dropping/resizing/etc. and it’s super customizable. It can be used in React projects as well as vanilla JS. Definitely recommend.

Jeff Goldblum laughing

And forever grateful for Jeffsum (Jeff Goldblum lorem ipsum) for making me think of Jeff Goldblum’s laugh whenever I’m making little demos.

Happy coding! 🌿

May 30, 2019.


Jess

Hi, I'm Jess. I write about software development, health, and how the two can relate. 🌿 Follow me on Twitter