Auto-sizing text to fit container


#1

I would be interested in having a CSS feature that would allow font to be sized relative to it’s container size.

Reasoning

There is quite a lot of interest in this:

While vw and vh can provide a certain workaround, relying on viewport size would not be sufficient for a relatively complex page.

Another thing is precision. Basic Kinetic Typography could be done with standard CSS animations if the text can be positioned precisely, taking exact vertical space (from top pixel of the text to bottom pixel).

Specification

This is a very rough description of an idea – I am pretty sure there are quite a lot of problems with it. But here is the basic thing:

font-size: fit-width | fit-height | fit-height-precise;

fit-height-precise would be ascent-to-descent (if I understand font terminology correctly) – basically from top pixel to bottom pixel.

Problems

  • How does this work with multiline text?
    Not quite sure at the moment – any feedback?
    One option is an additional constraint that would describe preferred line length.

  • How does this work with text-overflow?
    Overflow would have to be ignored if this is set.

  • How does this work when calculating the container size?
    Setting this property makes container ignore its text content when calculating it’s size.

  • How does it work when there are other elements in the container?
    Not quite sure, but we could ignore the property in that case.

  • Shouldn’t we support percentage sizes relative to the container instead of 100% fit?
    I would say it’s not worth it – it’s always possible to wrap in another element or use padding instead.


#2

Having written the current text-sizing logic for big.js, I can tell you that handling this for word-wrapped content is a computationally-complex packing problem, solveable by only brute force (although the search space could probably be reduced significantly with better text metric APIs). As such, I believe the multi-line case should remain a problem solvable by only JS (on the other hand, I think Flexbox has a similar sizing mode regarding wrapping content, so maybe this isn’t as intractable as I think).


#3

This is a thing I’ve wanted for ages, and it doesn’t make sense why there is no plain CSS solution up to date.

The last time I thought about it (it was today) I came up with that it should be a new value for text-overflow property, like text-overflow: scale, alongside with two new properties min-font-size and max-font-size, both of which would allow us to control things for the most use-cases .

Issues that should be solved:

  1. Whenever there is a multiline text and only one line overflows, should the font-size of the whole text be scaled, or only the one for the line that overflows? The best would be to have a way to do both, and the easiest would be to scale everything.
  2. Should only font-size be scaled, or both font-size and line-height? What about font-weight and other possible properties of the font? I’d say that for the start we should be able to allow both line-height scaling and no line-height scaling, possibly by different values?

Most of the use-cases are already solved in JS in a number of ways (for example, https://github.com/rikschennink/fitty), and performance or complexity shouldn’t be valid reasons not to implement it in CSS. That’s a thing that developers need really often and it just should be in CSS, one way or another. Performance shouldn’t be worse than that of the JS tools already provide (and it should be on developers to use it properly), and any complexity issues should be fixed by having compromises that would allow the most use cases to be achievable (and for the more complex ones not to be, at least at first).


#4

We already do this for (background) images, using contain, so I imagine something similar could certainly be done with font-size. It definitely has use cases ─ I don’t think that’s even in question.


#5

You can do it with SVG, but you might not like it: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/lengthAdjust


#6

Sure, but what if the text can be any number of values? The vast majority of people won’t want to edit SVGs on the fly.


#7

I am liking this solution so far


#8

This would be an incredibly useful feature, though I would also include minimum and maximum size parameters. It would be particularly great to have it for user-generated text that has to fit within a small fixed size container where scrolling is unacceptable. Normally you’d use ellipsis and present the full text some other way, but it would be neat to be able to just shrink the font slightly to fit.

The computational complexity doesn’t seem bad at all since you can use binary search to find the optimal fit, as long as the precision is within reason. If only integers are allowed, then finding the best fit between 6px and 70px (an obviously ridiculous scenario) would only take a maximum of 6 iterations. Adding subpixel precision down to 0.25px would only add another 2 iterations. Additionally, any iteration can be cut short if the total width of all remaining characters is greater than the remaining horizontal space (treating all rows as a single one), which quickly eliminates any sizes that would be far too large. You can also use previous knowledge to optimize the search, for example if 27px was optimal for a previous text and you add another word to that text, then you know that the new solution is less than or equal to 27px. If the added word is a small fraction of the whole text you also know that the font change will be small, so you can linear search down from 27px. There are many other tricks that can be used to even further speed up the algorithm, but clearly it’s nothing too expensive, especially considering that it would likely only be used for shorter text content (making each iteration very quick).

The main issue I have with JS solutions is the initial page load. The first layout won’t happen until the first stylesheet or inline CSS has been parsed, but it won’t wait for your JS file to download and execute (unless you don’t defer, which has its own downsides). So if you style things by JS on page load you may see a flash of incorrectly styled content. The only solution is to inline the JS, not really something you want to do unless it’s extremely lightweight. It’s also inefficient since with JS you don’t have access to certain information about font and layout behavior (which can differ by browser), so answering a question like “would this text overflow at 11px?” can be very difficult. This can make it either slow to compute, inaccurate or inflexible.