Open bfgeek opened 2 years ago
Why not height: max-content
? See https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes
Although the
auto
size of text input controls such as HTML’s<input type=text>
and<textarea>
elements is typically a fixed size, the contents of such elements can be used to determine a content-based intrinsic size, as for non-replaced block containers. Themin-content
andmax-content
keywords of the sizing properties thus represent content-based sizes for form controls which render their value as text contained within their box, allowing such controls to size to fit their visible contents similarly to regular non-replaced elements.
Prior discussion in https://github.com/w3c/csswg-drafts/issues/2141#issuecomment-365692012.
I didn't realized we have previously discussed this - that's great!
I have a few concerns about overloading the by using the min-content / max-content keywords however.
It creates a slightly weird discontinuity between height: auto
and height: min-content
. E.g. the auto
keyword effectively maps onto either stretch
of fit-content
within layout (depending on the context). There shouldn't be any difference between a height:auto
(where auto
maps to fit-content
) and height:min-content
to height:fit-content
.
This is difficult to feature detect. E.g. it'll be difficult for web developers to test if this feature/mode is supported, and min-content/max-content/fit-content is widely supported.
Easy to get wrong when various algorithms request min-content/max-content sizing of different elements.
The definition here will also need to be tweaked. Here you actually don't want it to be strictly the height of the content, rather you want to count the number of lines, then multiply that by the lh
unit. The reason for this is that you'll get very subtle "glitches" when typing, e.g. type in a character that uses a font-fallback and has difference ascent/descent metrics, and the textarea will appear glitchy when typing.
+1 to solving this, as well as sizing <input>
horizontally, which is insanely difficult to get right today (especially given the spinner arrows, date & time icons etc which are different per UA).
The CSS Working Group just discussed Sizing <textarea> by contents
.
I missed this discussion, but here are my two cents about what was said:
height: max-content
. Placeholders are essentially shadow DOM descendants and thus should automatically be taken into account when calculating intrinsic size. 1lh
, we already have round(up, max-content, 1lh)
, no? We just need to allow intrinsic size keywords in there, which IIRC was the plan all along.round(up, max-content + 1, 1lh)
would suffice for use cases? Or a box-sizing
keyword to add scrollbars to height rather than subtract from it?Chrome and Safari currently implement -webkit-line-clamp
to control the number of lines of text, how will the new lh unit interact with the standard line-clamp
property in the future?
- Or a
box-sizing
keyword to add scrollbars to height rather than subtract from it?
Having actually box-sizing
option for scrollbars can be useful for a lot of other cases!
Working well on Chrome v109 (Canary) with lh
.
https://user-images.githubusercontent.com/9271155/198690200-144c1202-b528-4132-b324-bf70cad621c8.mp4
JS:
static supportsCSSLineHeightUnit = CSS.supports('height', '1lh');
function resize() {
if (!TextArea.supportsCSSLineHeightUnit) {
const { lineHeight } = window.getComputedStyle(textarea);
this._lineHeight = lineHeight; // Calls style.setProperty('--line-height', newValue);
}
/* ... */
CSS:
:host {
--line-height: var(--mdw-typescale__body-large__line-height);
}
@supports({width: 1lh}) {
:host {
--line-height: 1lh;
}
}
Very happy to remove the window.getComputedStyle
calls.
One option that was brought up at the F2F was using the min-intrinsic-sizing
property which messes around with what intrinsic sizing algorithm to use. See notes:
https://github.com/w3c/csswg-drafts/issues/7552#issuecomment-1203201900
A simple proposal which uses this would be:
textarea { min-intrinsic-sizing: from-textarea; }
input { min-intrinsic-sizing: from-input; }
/* or for a global setting */
* { min-intrinsic-sizing: from-textarea from-input; }
(Note see: https://github.com/w3c/csswg-drafts/issues/8620 that min-intrinsic-sizing
needs to be per-axis, above example might be better using min-intrinsic-block-sizing
, and min-intrinsic-inline-sizing
respectively).
Combined with the lh
unit this allows for control over what most folks desire, e.g.
textarea { min-height: 2lh; max-height: 4lh; line-height: 1.2em; min-intrinsic-sizing: from-textarea; }
The CSS Working Group just discussed [css-ui] ? Allow <textarea> to be sized by contents.
.
OK, seems like we have three ways forward:
min-content
/max-content
/etc. to use the input size, as the WG resolved in https://github.com/w3c/csswg-drafts/issues/2141#issuecomment-365692012. However, @bfgeek suspects this is likely to cause compat pain.max-input
keyword to width/height that triggers this behavior on form controls, and maps to max-content
behavior on all other elements. @bfgeek is uncomfortable with how this can mix with the existing intrinsic sizing that form controls use (like width: fit-content; max-width: max-input;
); auto min size will still look at the intrinsic form stuff.form-sizing: normal | contents
) that switches input elements to use content-based sizing in all aspects. @fantasai is concerned this is less discoverable than a width/height keyword.I would much prefer re-using existing syntax than expanding the language even more, and I share @fantasai's concerns about a new property. Also, I think it should be an antipattern to add syntax that only applies to specific types of elements (e.g. this is why we re-used ::marker
for <summary>
rather than introduce a new pseudo-element).
I think we should explore the compat implications before we rule it out. I could ping the httparchive folks if that would be useful, but we need to formulate a query first. E.g. what about this: % of pages where there is at least one <textarea>
or <input>
element where at least one of width
, height
, min-width
, max-width
, min-height
, max-height
is either min-content
or max-content
.
One thing that makes this a little tricky: when the form element is empty, you almost never want full-blown input sizing, because that would make it 0! You usually want a minimum width/height of 1 character / 1 line. This can be done with min-*
of course, but it's somewhat annoying that every use of width: min-content
for form elements would also require this boilerplate. I suppose max(1lh, max-content)
is more palatable, but we still can't use identifiers in calc functions.
Also, the concept of input sizing needs to take placeholders into account. Generally, when there is no input, you want to use the placeholder for sizing (when one exists), rather than the empty string.
We've observed that in the wild the auto-min sizing behaviour triggers a lot as acting as a floor. Developers often regularly under-size flexboxes (lots of different cases, but one example is when the flexbox is a scrollable area, and sometimes authros will explicitly set a flexbox to 0px
in order to trigger this behaviour!). Flexboxes are also used everywhere now - sometimes as the "default" layout (due to the main layout that react-native supports - see https://reactnative.dev/docs/flexbox).
For option (1) switching the min-content
/max-content
/etc to be different will result in significant compat pain. The primary concern is hanging the auto-min size in flexboxes. E.g. <div style="display: flex; width: 0;"><input></div>
. E.g. this is triggering new behaviour even if you aren't using one of the min-content
/max-content
/etc keywords.
For option (2) the concern is similar. You might specify something like:
<div style="display: flex; width: 0;"><input style="width: max-input"></div>
The min-content size would still be the old behaviour, leading to a weird sizing above when you had content exceeding the "legacy" min-content size, the element would size to the "legacy" min-content size.
I think we should explore the compat implications before we rule it out. I could ping the httparchive folks if that would be useful, but we need to formulate a query first. E.g. what about this: % of pages where there is at least one
<textarea>
or<input>
element where at least one ofwidth
,height
,min-width
,max-width
,min-height
,max-height
is eithermin-content
ormax-content
.
@LeaVerou - This isn't the primary compat concern. The primary concern the is auto-min size behaviour.
One additional thing to note here - <input>
/ <textarea>
are super common in enterprise usecases vs. the public web. Compat analysis has a "blind" spot with regard to this, as these sites aren't available on the public web, and we aren't able to get accurate UseCounter data.
We've previously had bugs with <input>
/etc where they were only caught in our stable channel releases due by enterprise usecases.
Also, the concept of input sizing needs to take placeholders into account. Generally, when there is no input, you want to use the placeholder for sizing (when one exists), rather than the empty string.
Yeah that's fine, the placeholder is content. It should Just Work.
One thing that makes this a little tricky: when the form element is empty, you almost never want full-blown input sizing, because that would make it 0! You usually want a minimum width/height of 1 character / 1 line. This can be done with min-* of course, but it's somewhat annoying that every use of width: min-content for form elements would also require this boilerplate. I suppose min(1lh, max-content) is more palatable, but we still can't use identifiers in calc functions.
Note: you want max()
there. The fact that you use max()
to implement min-*
behavior, and vice versa, will always be confusing.
Wouldn't you usually be using height
here, so min-height: 1lh;
is still available? That is, textarea { form-sizing: auto; min-height: 1lh; }
would do what you want, right? Are there cases where you need to use height
for some other value, but still want to restrict it to a minimum size expressed both in terms of content and an explicit length? (I could maybe see this, with a height: 100%
perhaps? I think you could shuffle them around then, tho, with a height: auto; min-height: max(1lh, 100%);
.)
If we do have such cases, is it actually form-specific, or is it a more general issue with wanting to apply multiple min/max constraints to an arbitrary element? If the latter, we should handle it in a generic way, such as by making min/max take a comma-separated list of constraints.
Note that for an editable areas, rendering engines will preseve 1lh of height even if its empty to allow content to be added (there is an implicit minimum of 1lh).
@bfgeek If the main problem is with min-width/height: auto
, we can define auto
there to resolve to the current width/height calculation. In other words, define the “content size suggestion” for form controls to be the current default size. You'll often want to shut off the automatic minimum to allow it to shrink further, but as @LeaVerou points out, authors need to specify an explicit minimum to get reasonable behavior there anyway. I think this interpretation would give us a pretty reasonable and useful behavior tbh.
Disregard my last (deleted) comment. Was concerned about box-sizing: border-box
, but seems like I can move to content-box
safely and still use something like form-sizing: auto
.
Wouldn't you usually be using
height
here, somin-height: 1lh;
is still available? That is,textarea { form-sizing: auto; min-height: 1lh; }
would do what you want, right? Are there cases where you need to useheight
for some other value, but still want to restrict it to a minimum size expressed both in terms of content and an explicit length? (I could maybe see this, with aheight: 100%
perhaps? I think you could shuffle them around then, tho, with aheight: auto; min-height: max(1lh, 100%);
.)If we do have such cases, is it actually form-specific, or is it a more general issue with wanting to apply multiple min/max constraints to an arbitrary element? If the latter, we should handle it in a generic way, such as by making min/max take a comma-separated list of constraints.
Yes, I'd expect min-height
to be available, this is not about applying multiple constraints. None of this is particularly hard, it's just that authors need to remember to do it, and the UX without it is pretty terrible. Boilerplate in general is an API smell (though sometimes unavoidable).
Of course, if we define it as a new value, I suppose it could have the reasonable minimum baked in. Are there any use cases where you don't want that minimum?
I have to revert back to border-box
. That will probably put in the question the usefulness of form-sizing: auto
. The problem is the textarea
has vertical padding. That means it's not as simple a min-height: 1lh
in all use cases.
Edit: Sorry, keyboard was stuck and commented early.
We would need a value that can be used with calc
to play nice. We want to set a max-height of 100%, which is border-box
based, so we can't use content-box
.
To reiterate @LeaVerou 's point from before:
I suppose max(1lh, max-content) is more palatable, but we still can't use identifiers in calc functions.
I was able to get it work content-box
and -webkit-fill-available
, but that leaves Firefox out.
As @bfgeek pointed out in https://github.com/w3c/csswg-drafts/issues/7542#issuecomment-1535543361, it turns out that engines interoperably apply an implicit minimum content-box block-size of 1lh
when an element is editable. I opened #8800 to get us to document that. So this actually moots @LeaVerou's concern; a textarea that is switched to "normal block" behavior will not collapse to zero height by default; it'll collapse to 1lh height and still be usable.
@bfgeek If the main problem is with min-width/height: auto, we can define auto there to resolve to the current width/height calculation. In other words, define the “content size suggestion” for form controls to be the current default size. You'll often want to shut off the automatic minimum to allow it to shrink further, but as @LeaVerou points out, authors need to specify an explicit minimum to get reasonable behavior there anyway. I think this interpretation would give us a pretty reasonable and useful behavior tbh.
There are several interactions which will cause developer confusion here, this I think is the main "breaks existing content one", but I'd still place a non-trivial risk of the "min-content"/etc being used on <input>
s and friends.
There are many different ways to trigger the current default/legacy magical behaviour - which I suspect will cause developer confusion.
A use-case which I want to ensure we get right is "I want all <input>
s in the column to be the size of the largest input".
(Lots of <input>
s in "table-like"[1] things is a super common case in enterprise software unsuprisingly).
A reasonable solution to this would be:
<!DOCTYPE html>
<style>
input { min-width: max-input; width: 100%; box-sizing: border-box; }
</style>
<table border=1>
<tr><td><input style="min-width: 30px; /* for demonstration pretend max-input = 30px */"></td></tr>
<tr><td><input style="min-width: 40px; /* for demonstration pretend max-input = 40px */"></td></tr>
</table>
Here you'd expect the column to be 40px wide, and all the <input>
s to match, but it'll be the current "default"/"legacy" size of "magical number", as while calculating the intrinsic sizes the 100%
becomes auto
(and I think we've established we can't change the default auto
behaviour by default).
[1] May not be an actual table, but typically one.
The more I've played around with examples here, the more I'm convinced that the "just act like a normal element, stop having magical semi-replaced behavior" is absolutely the way to go for this, and flipping that with a single form-sizing
switch is the way to trigger this. The default form control semi-replaced behavior is a funky combination of legacy behavior and somewhat reasonable defaults for when you're not thinking about it and just want something functional, but the more magical we try to be with this the worse it's gonna be.
I have two notes regarding the different discussed options.
Firstly, if we choose to go with keywords for min-intrinsic-sizing
, then we should not introduce keywords specific to the type of input. So no from-textarea
or from-input
. As it was implicitly pointed out earlier, this mechanism should also work for elements with contenteditable
attribute. So I think, from-content
as suggested by @astearns or something similar would be better.
Secondly, if we choose to go with a new property, then form-sizing
probably isn't the right name. The property refers to any elements allowing manipulation of their contents, not forms. For that, my suggestion would rather be input-sizing
, text-input-sizing
(though with contenteditable
, contents can be more than just text) or content-sizing
.
Sebastian
The CSS Working Group just discussed [css-ui] ? Allow <textarea> to be sized by contents.
, and agreed to the following:
RESOLVED: Add a new property (provisionally "form-sizing: normal | auto") that turns off the "half-replaced" sizing of form controls and makes them content-based like normal elements
If I'm understanding this correctly, then this should be enough:
textarea {
overflow-y: auto;
box-sizing: content-box;
padding-block: 16px;
block-size: min-content;
min-block-size: 1lh;
max-block-size: calc(100% - 32px);
/* https://github.com/w3c/csswg-drafts/issues/7542 */
form-sizing: normal; /* Default is auto */
}
textarea[minrows="2"] { min-block-size: 2lh; }
textarea[minrows="3"] { min-block-size: 3lh; }
textarea[minrows="4"] { min-block-size: 4lh; }
textarea[maxrows="2"] { max-block-size: 2lh; }
textarea[maxrows="3"] { max-block-size: 3lh; }
textarea[maxrows="4"] { max-block-size: 4lh; }
JS side, I assume a CSS.supports('form-sizing', 'normal')
would suffice to skip custom sizing steps.
I believe normal
here refers to normal editable elements and auto
would be the "old" way?
nitpicking here but normal
/ auto
isn't super obvious for someone who doesn't know the history of how form controls are sized.
@tabatkins suggested legacy | auto
but I still think this isn't great since it refers to history (which someone new to web dev probably doesn't know). Something that would describe the behavior more among the lines of fixed | auto
or such (auto
probably still isn't great here) might be better.
(fixed
probably isn't 100% accurate either, but describes the behavior better I think)
<div style="form-sizing: fixed">
won't have a fixed preferred size. It's confusing. So auto
keyword looks reasonable to me.
How about auto | none
? It's same as appearance
property.
It's unclear what form-sizing: none
means to me.
form-sizing: fixed
for the existing default and something like flexible
or grow
for the new one would be preferable to names like auto, normal or none to me. Can't think of a pair of value names that really win it for me though.
I don't think it's surprising for <div style="form-sizing: fixed">
to not have a fixed size, since it's not a form control, so form-sizing
doesn't apply. This kind of thing happens all the time, e.g. width
has no effect on inline boxes.
I hope this proposal is extended to input
text fields too. I found this pattern useful in those "dynamic notebooks" that have editable values interspersed in regular text.
If not available in a first release, it would be great to at least use wording that's not strictly related to textarea
@fantasai earlier in this thread proposed contents
as the opt-in value and I think that (or content
) makes a lot more sense than what has been proposed since then. fixed
seems reasonable for the status quo value, but I could live with a number of the alternatives mentioned thus far as well.
Adgenda+ to bikeshed.
I hope this proposal is extended to
input
text fields too.
Agreed. I'm arriving here looking for improved UX for input
word wrapping. That's admittedly a separate issue, but if that was implemented natively, this issue would absolutely become relevant, and should (would need to?) be applied to both input
and textarea
.
What do you mean input
word wrapping? Inputs are single line.
Yeah, there's definitely a case to be made for <input>
autosizing its width to fit its contents, but I doubt we'd see inline-like fragmented wrapping. For that, you probably need contentEditable="plaintext-only"
.
What do you mean
input
word wrapping? Inputs are single line.
Exactly right, inputs are single line. That's the behavior I want to maintain, without resorting to hijacked keyboard events on textarea
.
Visually wrapping a single line is not the same as true multi-line text, no? Maybe there's a technical implementation reason why that's not correct, but seems like a true statement to me.
I'm thinking more in mobile/small screen context, where autosizing width isn't as viable, as it's often already been maximized, but vertical expansion space is available (and vertical scrolling assumed) more often than not.
People with long names, street addresses, countries, product titles, subject lines...
I suppose a native way to prevent linebreaks in textarea
would equally help. But imo overflow on any input, in any direction, makes for poor UX. Again, particularly with mobile, you can often only fit a handfull of words in width before losing context. No native visual indicator of hidden text, and methods of scrolling/moving cursor within are inconsistent across devices, if supported at all (let alone known to the user).
There can be real consequences if a user inadvertently submits incorrect information that they literally can't see. Seems like autosizing to content should have been a default, and choosing to constrict size/overflow should always be a deviation decision made by designers and devs. Tho I guess that ship has long since sailed.
Final gripe was the function/icon of the keyboard return key differing between input
(go/next/search) and textarea
(return). BUT I just found out about enterkeyhint="next"
, which is at least something.
I would like to know how to interact with resize: both? Many existing JS components will still adapt to the height of the textarea
when the user drags it to change the height, and then adds the content again.
I would like to know how to interact with resize: both? Many existing JS components will still adapt to the height of the textarea when the user drags it to change the height, and then adds the content again.
This would have to be done in script still. resize: both
works by setting an explicit width/height on the inline style of an element (changing this wouldn't be web-compatible).
A component could intercept this style, and set a min-width / min-height on it instead if that is what was desired for example.
(FWIW in components I've seen they are somewhat split on this design decision, some prefer to keep the resize stable from what the user has explicitly resized to, others let it continue to grow).
Ian
Would the textarea height be animatable?
I just saw that the form-sizing
property will be added to Chrome Canary soon. Where is the message where CSSWG resolved to use that new property rather than the existing max-content
value with an existing property?
I see quite a bit of discussion around that, but I can't find any sort of resolution.
Here is the resolution https://github.com/w3c/csswg-drafts/issues/7542#issuecomment-1542505774
@lukewarlow Thanks!
This would be an excellent addition!
I think a common scenario for this is chat boxes, as in the Slack screenshot below. Notably, these tend to grow upward – presumably this is best done by placing the dynamically-sized textarea at the bottom of a flexbox. As you write and test the initial browser implementation, it might be worth implementing the chat box scenario to check that we're not running into edge cases that are unexpectedly difficult to implement.
Some interesting edges cases off the top of my head:
If you come back to a conversation with an unsent draft in the chat box that exceeds the maximum height, WhatsApp's behavior (on iOS) is to pre-scroll the chat box to the bottom:
Programmatic changes to the textarea contents, such as a /slash command expanding into a template, might cause the textarea to resize and possibly reach its maximum height. Here again, there might be interesting interactions between the resizing, the upwards growth, and the textarea scroll.
Since this is on the agenda for today, and many folks can't make it due to timezone conflicts, could someone please summarize the options considered so that people can weigh in async?
Even after skimming the thread, it was unclear to me what's the current status on:
form-sizing
property? A max-content
value on another property? If so, which one? <input>
width autosizing, which is also a common request (though not equally common)? I really hope we don't introduce two entirely separate bits of syntax for these very similar things.What are we bikeshedding? Is it the naming of the form-sizing property? A max-content value on another property? If so, which one?
I believe max-content
was discussed first. Then it was decided on the form-sizing
property. Some claimed the naming is confusing as long as the values (auto
/normal
). Then max-content
was asked about after, but it that was resolved much earlier to use a new property.
(As for bikeshedding, even I'm confused about the naming looking back. auto
implies recognition of how auto
used to work, which isn't clear by name only. normal
is also non specific. Neither describe what is actually happening, just external references.)
Is this intended to eventually also control width autosizing, which is also a common request (though not equally common)? I really hope we don't introduce two entirely separate bits of syntax for these very similar things.
Discussion came about to how nice it would be to apply this to <input>
as well. I can understand since authors/designers have to pick which control they want (<input>
vs <textarea>
) before presenting to users. Then discussion come about the complexities of making <input>
multiline. Form submission and keyboard <Enter>
are points of topic.
(I'd add the type=search
and others change the way mobile input methods are also affected and what happens there ("Search button replaces Enter"). Also, on desktop Shift+Enter
commonly adds a new line in some UIs, but would actually submit and open in a new window on <input>
.)
But if a developer is willing to handle all those UX cases, what would the CSS look like to support making a <input>
multi-line and sized by contents. Would it match? Would there be more properties? etc
See: https://github.com/whatwg/html/issues/6807 for more context about the problem we'd like to solve. The TL;DR is that we'd like to allow the
<textarea>
element to be sized to its contents a little easier. I.e.We explored different solutions in the whatwg thread. Folks seem to prefer a CSS solution to this problem - hence this issue!
One way to achieve the "minrows"/"maxrows" in that issue is to use the "lh" unit. (Fortunately the "lh" unit works exactly the same way textareas are implemented today - in that they use the first available font to calculate the line-height).
E.g.
The only thing we are missing is the ability to use the "actual"[1] intrinsic size of the textarea. There are a few different potential solutions here.
Today the intrinsic-size of a textarea will (roughly) read the "rows" attribute, and multiply this by the "lh" unit. We need a switch to instead read the "rows" attribute to be based on the actual number of lines. One potential solution is:
textarea-rows: auto | <number>
If "auto" was set the user-agent would read the number of lines, then multiply this by "lh" to get the intrinsic block-size for the text-area.
We could then map the rows attribute to a presentation style hint to this property.
cc/ @tabatkins @lilles