Skip to content

[css-fonts-5] Feature for making text always fit the width of its parent #2528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
tobireif opened this issue Apr 11, 2018 · 72 comments
Open

Comments

@tobireif
Copy link

This thread shows that it's a widely required feature:
https://twitter.com/sindresorhus/status/979363460411609091

Example of a workaround: Open https://tobireif.com/ and (eg using dev tools responsive mode) resize the page down to 250px width while watching the text "Welcome".

@litherum
Copy link
Contributor

This requires performing layout in a loop, which we generally have avoided. Requiring a round-trip through JS is valuable because authors are more likely to realize it has a large perf cost

@litherum litherum added the css-fonts-4 Current Work label Apr 11, 2018
@tobireif
Copy link
Author

In the JS at https://tobireif.com/ I perform two passes - that's plenty for a good-enough result, and it doesn't impact perf in any noticeable way (the text-fitting is only done once in addition to the first main run). That could be a great option for browser implementers as well, and it shows that supporting such a CSS feature is very feasible.

@tobireif tobireif changed the title Feature for making text always fit the width of its parent [css-fonts-4] Feature for making text always fit the width of its parent Apr 11, 2018
@timothyis
Copy link

If this were a feature, I think it'd be best if it was a CSS function. (similar to calc or minmax)

Something like font-size: fit(8px, 48px); where 8px is the minimum font-size and 48px is the maximum font-size.

I think using a function, other than being useful for minimum and maximum sizes, relays the gravity of using the feature since surely it'll have some performance issues in extreme cases.

I'd love to see this in CSS!

@tobireif
Copy link
Author

Great suggestions!

A lower limit and an upper limit both make sense.

Instead of font-size: fit(8px, 48px) it might be better to name it eg font-size: fit-width(8px, 48px).

@SergeyMalkin
Copy link

Changing font-stretch, especially using variable fonts, is another way to fit text into parent. Or compression during justification . So if there is a css property that instructs layout engine to fit, it should allow different methods and so likely be separate from font-size.

And this kind of functionality may not only be on line operation. It may be useful for more advanced functionality, like optimal paragraph layout, line clamping, or simple ellipsis.

@tobireif
Copy link
Author

Changing font-stretch, especially using variable fonts, is another way to fit text into parent. Or compression during justification . So if there is a css property that instructs layout engine to fit, it should allow different methods and so likely be separate from font-size.

True! (also eg letter-spacing)

And this kind of functionality may not only be on line operation. It may be useful for more advanced functionality, like optimal paragraph layout, line clamping, or simple ellipsis.

Let's start simple 😀 If we're asking for too much we might not get anything. The basic simple use case of fitting one line of text (eg a heading) into its responsive parent is so common that a solution for that would cover a lot (and more could get added/specd later).

@tobireif
Copy link
Author

tobireif commented Apr 12, 2018

Yes it's feasible to implement the functionality using JS, and yes there are workarounds, and I think there even is a lib, but it sure would be very handy to be able to simply write one single line of CSS instead.

My implementation in the source of https://tobireif.com/ is more than 50 lines of JS - if people could instead write a single line of CSS then that would save a lot of typing.

By the way @litherum : If the implementation is smart enough, perhaps one pass would be sufficient → no loop / double-pass.

Perhaps the syntax could look like this:

fit-width: font-size(20px, 100px);
fit-width: letter-spacing(-0.1em, 1.5em);
fit-width: any-text-width-affecting-property(min, max);

The sizing/fitting should honour the (potential) padding of the container.

@litherum
Copy link
Contributor

Using a small number of passes is unlikely to work in the general case, because if we get it wrong, the text will overflow its container and wrap, which would be catastrophic. Any generalized implementation would have to iterate until the algorithm converges. Such an algorithm would be a great way to make a browser hang.

@tobireif
Copy link
Author

tobireif commented Apr 14, 2018

If you do want to provide this widely requested feature - perhaps you could try it out 😀 If your algorithm is smart regarding calculating the estimated target value, it will not need many passes, and it might need only one or two passes. For all and any cases.

Such an algorithm would be a great way to make a browser hang.

When you try it out, and limit the maximum number of passes to 2 == no browser hang at all, and if your algo can estimate the correct value pretty well, then there's a good chance that the result will be sufficiently good. You'd have to try it out though.

If you do not want to provide that feature no matter what, and thus do not want to create a quick "beta" implementation for seeing what's feasible, then there's not much reason to continue the discussion. In that case please close the ticket.

I did create a quick implementation using JS and found that it works sufficiently well using only two passes. The code is at https://tobireif.com/ -> source -> "var topLevelHeadings". It's just a quick (but good enough for that case) implementation - I'm 100% sure that you could come up with a much better (and generally applicable) algo 😀

Here's another JS implementation:
http://fittextjs.com/
https://github.com/davatron5000/FitText.js

None of the above implementations causes any noticeable performance issue. And: The latter is a general lib.

@tobireif
Copy link
Author

As for your own site, the type inside your headers is simple enough that you'd have performance gains in just using vw inside a breakpoint, reducing 50 lines of runtime js to possible 4 lines of css.

I'd prefer CSS that's based on the parent element width, not on the viewport width. (Because generally the element width might change without the viewport changing.)

The feature is a (very popular) wish - the specification of that feature (including all relevant details) would be up to the CSS WG.

@tobireif
Copy link
Author

(Oh, and if that feature would be only feasible to spec/implement for a defined set of simple types of cases, I'm sure that simple feature would be widely appreciated as well 😀 The syntax still could be fit-width: any-text-width-affecting-property(min-value, max-value), I think.)

@tobireif
Copy link
Author

If and when there will be an ew unit ("element width", as in EQCSS), and if and when there will be clamp() , then the functionality in this feature wish ticket here could be expressed sufficiently succinct, eg:

font-size: clamp(30ew, 20px, 80px);

@jonjohnjohnson
Copy link

Where are you getting 30 in the 30ew? Are you matching that to the length of the string in the element? Or is 1ew just 1% of the elements width, meaning a 100px wide element would set its font-size to 30px?

@tomhodgins
Copy link

Here's the definition of ew, eh, emin, and emax from jsincss-element-units:

switch(unit) {

  case 'ew':
    return tag.offsetWidth / 100 * number + 'px'

  case 'eh':
    return tag.offsetHeight / 100 * number + 'px'

  case 'emin':
    return Math.min(tag.offsetWidth, tag.offsetHeight) / 100 * number + 'px'

  case 'emax':
    return Math.max(tag.offsetWidth, tag.offsetHeight) / 100 * number + 'px'

}

I was thinking of isolating just these tests into their own package (and maybe the element query tests from jsincss-element-query) so other plugin builders could more easily re-use the same tests in their plugins.

@tobireif
Copy link
Author

tobireif commented Apr 14, 2018

Yeah, it'd not be the real deal where the implementation figures out the value required for making the text fit its container. It'd just be a pragmatic way to get the feature with just one line of CSS.

(And yes, 30ew means 30% of the element width. The exact number is just an example, it could be eg 45.5ew .)

@tobireif
Copy link
Author

(I was replying to @jonjohnjohnson , just so there's no misunderstanding @tomhodgins 😀)

@tobireif
Copy link
Author

Ideally we could state in CSS "always fit this word/line of text inside its parent (by auto-adjusting the property "foo" eg font-size or letter-spacing), no matter what font is used".

@Crissov
Copy link
Contributor

Crissov commented Feb 6, 2021

We now do have clamp() to specify lower and upper bounds. Alas, we can only reference character width (ch, ic) or height (em, cap) and viewport dimensions (vw, vh etc.), not line or box width, as units. (Units for container dimensions have been proposed in #5888.) So you could only approximate the result for an assumed number of characters per line.

For the desired capability we would need new keywords or functions indeed.

@tobireif
Copy link
Author

tobireif commented Feb 8, 2021

For the desired capability we would need new keywords or functions indeed.

Yep 😀

@faceless2
Copy link

No one has mentioned the SVG textLength property, which already does this. The functionality is also part of AH formatter: https://www.antenna.co.jp/AHF/help/en/ahf-ext.html#axf.overflow-condense

Their algorithm applies to a block, not a line - I expect that the text is progressively adjusted and layout retried until it fit. It's certainly going to be multi-pass and expensive - you can take a guess at a start value easily enough, but word-breaks at the end of the line necessarily make the algorithm iterative to find the best value. Doing it once for print layout is one thing, but it would be horrendous if you were resizing a window with this on.

We've been asked for similar functionality a few times over the years, but I believe only ever for "fit to line" rather than "fit to block". I think it's more of an issue in print, at least until they start selling paper with a horizontal scrollbar.

If you restricted it to just scaling either font size or font-stretch, and you restrict it to just scaling text to fit a single line, then it's theoretically a single pass - it's just a multiplier applied to the property. But it gets rapidly more complex when you've got only part of the line scaling this way, or you have multiple items on the line doing this with different layout properties - for instance, imagine a float and two spans with different initial font-sizes on the line, all trying to scale themselves to fill the line. It's all stuff that would need defining.

@tobireif
Copy link
Author

@faceless2 wrote:

If you restricted it to just scaling either font size or font-stretch, and you restrict it to just
scaling text to fit a single line [inside a box]

That would be sensible (with font size as default).

@Crissov
Copy link
Contributor

Crissov commented Feb 10, 2021

I’ve seen cases where this has been applied to each word, for some definition of word. Nonetheless, I guess it would be fine to do this by fitting the whole textual box content on a single line.

(An l or line element would have been better than br in HTML.)

@jimmy-guo
Copy link

jimmy-guo commented Sep 20, 2022

Hi all, I'd like to revive the conversation and provide another perspective on the utility of supporting a feature like this in CSS.

There are many designs that leverage careful placement and styling of text. A lot of time is spent by designers and engineers to implement these designs, but often only just in English. As soon as the text gets translated to another language, especially if the translation is much longer or shorter, applying the same CSS to the text that worked for English often causes issues such as text overflowing, truncating, breaking mid-word, widows, etc. As a result, this feature would make it easier to localize text while preserving design intent.

This requires performing layout in a loop, which we generally have avoided. Requiring a round-trip through JS is valuable because authors are more likely to realize it has a large perf cost

There are several JS libraries that attempt to implement this resizing. However, one limitation of a JS implementation is that it causes layout shifts for server-side rendered (SSR) pages. Since the server does not reliably know the dimensions of the client's device, the text needs to wait for the page to be hydrated before resizing. If supported in CSS, text would be able to render at just the right size even on initial render of SSR'ed pages.

In addition, while performance is certainly a consideration, other expensive CSS features such as animating height also exist and the performance implications are well known. Given the benefits of a "FitText" feature, it would be nice to be able to support this and allow developers weigh the performance cost against the benefits for their use case.

@patrickhlauke
Copy link
Member

patrickhlauke commented Apr 1, 2025

Noting that WCAG is referenced by legislation ... so not just a naive case of "let's tweak WCAG then"

Also: a user wants to make text bigger. That's their prerogative. Coming along (as an author) saying "actually, I'd argue that no, that's not what you want" is ... maybe not the right approach.

It can also be argued that with full-page zoom the minimum size of the text will be 200% larger than before resizing.

You don't know what browser window/viewport size the user started at, so ... you don't know when the fit-to-width kicked in.

@kizu
Copy link
Member

kizu commented Apr 1, 2025

Noting that WCAG is referenced by legislation ... so not just a naive case of "let's tweak WCAG then"

Years pass, legislation changes. We're trying to design the language for the future. If the current specified criterion is absurd, it needs to be changed. Once it changes, and legislation catches up, the feature will stop violating it. Yes, those who are required by law to follow WCAG could not use the feature now, but could in N years from now on.

Also: a user wants to make text bigger. That's their prerogative. Coming along (as an author) saying "actually, I'd argue that no, that's not what you want" is ... maybe not the right approach.

As someone who uses various methods of zooming text and content of pages every single day, I never want to “make text bigger”. I want to access content by increasing the size of small texts, usually paragraphs.

This feature will not change anything in how minimum font-size works on pages, it will only allow authors to increase the size of certain elements to be conditionally larger than they would be without it. At a smaller viewport, a header fully fitting into available space from the get-go, will be rendered at 20px size, and could be without issue increased in size to 40px via browser zoom. On larger viewports, the same header will be rendered at an already larger size.

@patrickhlauke
Copy link
Member

As someone who uses various methods of zooming text and content of pages every single day, I never want to “make text bigger”. I want to access content by increasing the size of small texts, usually paragraphs.

While not trying to invalidate your perspective, note that that's what it is ... your take. Low vision users may have a different need than you. I'd suggest consulting with these groups (such as the https://www.w3.org/WAI/about/groups/task-forces/low-vision-a11y-tf/ once it restarts) rather than just assuming that of course all users will be the same / have the same preference.

@querkmachine
Copy link

As someone who uses various methods of zooming text and content of pages every single day, I never want to “make text bigger”. I want to access content by increasing the size of small texts, usually paragraphs.

This feature will not change anything in how minimum font-size works on pages, it will only allow authors to increase the size of certain elements to be conditionally larger than they would be without it. At a smaller viewport, a header fully fitting into available space from the get-go, will be rendered at 20px size, and could be without issue increased in size to 40px via browser zoom. On larger viewports, the same header will be rendered at an already larger size.

In addition to the previous comment, that feels very presumptive about how authors might use a feature like this. There's no guarantees that this will only be used on headers, or only with text that is already above a minimum size, or only within containers that are already large and will grow in size, and we shouldn't assume they will.

@frankelavsky
Copy link

As someone who uses various methods of zooming text and content of pages every single day, I never want to “make text bigger”. I want to access content by increasing the size of small texts, usually paragraphs.

I just finished a research project on personalization and accessibility (under review) and the most common action taken by people who are considered low vision is to customize text size. Inability to change text size is a pretty significant barrier for many people. Meta-analysis research from 2020 estimates that around 600 million people worldwide would be considered on the spectrum of "low vision" to blind, which was roughly 8% of the world population in 2020. These numbers are expected to grow, due to compounding socio-economic factors, by 2050. This isn't just a small slice of people.

@ChasBelov
Copy link

Is there a way for the site visitor to specify they want to increase the height of the container rather than shrinking the font?

@tobireif

This comment has been minimized.

@tobireif

This comment has been minimized.

@kizu
Copy link
Member

kizu commented Apr 2, 2025

I'll need to do more research on this topic before I could respond further, but it feels necessary to link up two related issues with discussions from WCAG/WCAG3 here:

On the first reading, the discussions there mostly agree that SC 1.4.4 is not ideal, both in ensuring the small text is readable (where you could want to bump it up more than 200%), and that for larger texts the percentage (if used at all) should be smaller, as otherwise headers could fail SC 1.4.10.

There were no resolution about anything though, and I will keep an eye on the Low Vision task force, and will bring these issues, and how they apply to this feature to them once it is restarted. Thank you, @patrickhlauke, for pointing to this group.


@tobireif Yes, GH is the place to discuss that, although maybe we should open a separate issue about the possible accessibility implications of this feature :)

@tobireif
Copy link
Author

tobireif commented Apr 2, 2025

@kizu

Yes, GH is the place to discuss that, although maybe we should
open a separate issue about the possible accessibility implications
of this feature :)

That would be nice of you.

@tobireif
Copy link
Author

tobireif commented Apr 2, 2025

There are now over 50 comments below my feature wish #2528 (comment).

That's awesome, but it's getting a bit much 😁

Here's the new place to discuss related topics: #12038

@yatil
Copy link

yatil commented Apr 2, 2025

Making text dependent on the viewport has always been problematic for accessibility, this is well documented. This is why we usually advise not to use clamp or vh/vw units in font sizes. It just overrides user preferences unnecessarily.

The only way this could pass muster is to require browsers to include an opt-out to the behavior. Or to ignore it when users zoom in, which would defeat the purpose of the proposal.

I second Patrick’s pointer at LVTF and general W3C horizontal review. It might be good to generally outline when you get specific accessibility input to ensure that proposals are feasible to implement so that all users can use them.

@svgeesus svgeesus changed the title [css-fonts-4] Feature for making text always fit the width of its parent [css-fonts-5] Feature for making text always fit the width of its parent Apr 7, 2025
@svgeesus
Copy link
Contributor

svgeesus commented Apr 7, 2025

Re-tagging (as resolution was Start work on this in Fonts 5)

@astearns
Copy link
Member

astearns commented Apr 7, 2025

Am I correct that the accessibility problem here only arises if the container width is tied to viewport width in some way? If the container width is a simple length like 500px then you could use zoom to enlarge fit-to-width text.

I’m not sure I’m ready to agree that an opt-out or disabling-on-zoom is the only way to make this work. Given that quite a lot of current font-size usage has some vw component added in, I wonder if there is something (in the specs, in zoom behavior, in authoring advice, or something else I have not thought of) we can do to make the general problem better that would also help this fit-to-width case.

@kizu
Copy link
Member

kizu commented Apr 7, 2025

The way to solve the fit-to-width issue with font-size I am currently thinking about: make the “max font-size” limit to be 200% of the starting original font-size.

With all major browsers that I tested, they provide around 500% full-page zoom (not counting pinch-to-zoom and similar cases). That means that if we limit the max font-size that our fit-to-width algorithm could reach to 200%, the built-in zoom allows a user to zoom the original text to around 250% in the worst scenario.

This will be very similar to the case described in the “Understanding SC 1.4.4” with the size of the layout (see second note there).

This will also solve another issue that I encountered with fit-to-width much more often than an inability to zoom in — an ability to zoom out. An unbounded fit-to-width on bigger screens can lead to text that is too big, it becomes unreadable.

I am planning to research the existing cases for fit-to-width, and see if 200% max font-size will be enough for them. I anticipate that it should — and not having this big range of values will also mean that authors will have to define a better fallback size for browsers that won't support fit-to-width.

@patrickhlauke
Copy link
Member

@astearns speaking of "quite a lot of current font-size usage has some vw component", note https://www.w3.org/WAI/WCAG21/Techniques/failures/F94

@astearns
Copy link
Member

astearns commented Apr 8, 2025

@patrickhlauke yes, it’s absolutely a failure (for accessibility and for design generally) to use viewport units as the only component in sizing text. Current usage as I understand it has vw as a small extra component (like font-size: calc(1em + .2vw)) that allows for some fluidity but still lets you zoom in to resize the text. I know that having an upper clamping bound has its own issues, but do you see problems with using a viewport unit component as a small tweak to font-size?

@rgpublic
Copy link

rgpublic commented Apr 8, 2025

Um, couldn't this all perhaps be a bit easier? I always thought of this feature like this: You have for example the usual stage-filling banner at the top of the page with a large headline. You are using e.g. a CMS and the text is variable (perhaps even translated to different languages). You want to make that headline as large as possible. Up until now, I would be quite conservative and use like 24px, because I don't know how long that headline might be for all pages. In most cases though, the headline could be larger. So, the font-size: 24px is basically a lower bound.

In the future, I could say: font-size: 24px-or-more. That would make the headline as large as possible before it needs to wrap around. In ideal world, I could even specify a number of lines or a height - not only fit to a single line.

Now, the first important thing here is, that the headline only gets larger. I don't see an accessibility problem there. Next, the calculation should IMHO be done as if the zoom level was at 100%. If the result of this calculation in a specific case is e.g. that 48px would be the maximum until the headline wraps around (or consumes more than the given number of lines), then the factor would be 2.0. Now if I zoom to 200%, the font-size should be 96px - even if more lines are created. This way, the zoom level would still take full effect and we wouldn't have that problem with accessibility. It's the same as if I specified 48px just for this page right away. Hope it makes sense. Just my 0.02€...

@aardrian
Copy link

aardrian commented Apr 8, 2025

@astearns

I know that having an upper clamping bound has its own issues, but do you see problems with using a viewport unit component as a small tweak to font-size?

I'm not Pat, but jumping in here because I have been begging authors not to use viewport units, min(), max(), or clamp() for type since 2019. My experience is that using viewport units as strictly additive (as your example) is not an issue.

It's also my experience that approach doesn't satisfy what many authors want by folding in viewport units. And so that's not how I see authors use viewport units in the wild (or developer blogs and sites).

So I am only jumping in to this thread to ask that we don't dismiss the risk of viewport units by framing some of their uses as "small tweaks" without better defining what that means.

@astearns
Copy link
Member

astearns commented Apr 8, 2025

Thanks @aardrian. To be clear I’m not looking for an excuse to dismiss the problem. I’m looking for the boundaries of the problem so that if and when we have a proposal on how to address it, we’ll know how to evaluate whether the proposal is an actual solution.

@aardrian
Copy link

aardrian commented Apr 8, 2025

Yeah, sorry @astearns, I did not think you looking for an excuse and it was not my intent to convey that. Mostly I wanted to make sure I could point to my own comment when someone said, "but the vw is only making a small tweak!"

Anyway, I think one boundary is when a viewport unit is used for anything other than additive values.

@astearns
Copy link
Member

For those not watching Blink intents:

Intent to Prototype: CSS fit-width text
https://github.com/explainers-by-googlers/css-fit-text/blob/main/README.md

I’ve asked them in their intent to prototype to acknowledge and work through the accessibility concerns raised here.

@svgeesus
Copy link
Contributor

Thanks, Alan. So far there is only a single mention in passing:

Accessibility If an end-user tries to enlarge font size, UAs should not fit enlarged lines to the container width. Is minimum-font setting enough?

@patrickhlauke
Copy link
Member

For those not watching Blink intents:

Intent to Prototype: CSS fit-width text https://github.com/explainers-by-googlers/css-fit-text/blob/main/README.md

I’ve asked them in their intent to prototype to acknowledge and work through the accessibility concerns raised here.

and this is where i see a dangerous slippery slope sometimes. things get prototyped, some developers already start using it...next thing you know it's a fait-accompli

@patrickhlauke
Copy link
Member

not sure how "minimum font setting" (assuming this means "having a minimum font size defined in settings") would help here, unless i'm missing something

@kizu
Copy link
Member

kizu commented Apr 24, 2025

@patrickhlauke Note that “prototype” currently means only in Canary under a feature flag (and, I think, recently, under a CLI feature flag), so it should not go anywhere near developers for now. It is only “intents to ship” that are dangerous and should not happen fast, but prototypes allow us to test our assumptions about the behavior, and iterate on them, including pursuing potential fixes to the issues we know of (and to identify new issues).

Re: what could help — could you give your opinion on #2528 (comment)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Tuesday Morning
Development

No branches or pull requests