w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.3k stars 636 forks source link

[css-ui] ? Allow <textarea> to be sized by contents. #7542

Open bfgeek opened 1 year ago

bfgeek commented 1 year ago

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.

textarea-1

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.

textarea {
  min-height: 2lh;
  max-height: 5lh;
}

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

Loirooriol commented 1 year 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. The min-content and max-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.

bfgeek commented 1 year ago

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.

  1. 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.

  2. 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.

  3. 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.

LeaVerou commented 1 year ago

+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).

css-meeting-bot commented 1 year ago

The CSS Working Group just discussed Sizing <textarea> by contents.

The full IRC log of that discussion <fantasai> Topic: Sizing <textarea> by contents
<fantasai> github: https://github.com/w3c/csswg-drafts/issues/7542
<fantasai> iank_: form control elements all have some default intrinsic size, inline axis and block axis
<fantasai> iank_: the way they calculate this is sometimes extra fun
<fantasai> iank_: sometimes just a static number, e.g. radio/checkboxes
<fantasai> iank_: the way that textarea works, it will more or less read the rows attribute and multiple that by the lh unit (effectively)
<fantasai> iank_: this is not fallback or anything like that
<fantasai> iank_: Since the dawn of time, ppl have wanted textareas to auto-expand
<fantasai> iank_: a lot of libraries that do this
<fantasai> iank_: most people want to show some minimum amount of rows
<fantasai> iank_: and then let it grow
<fantasai> iank_: I filed this issue, what's the best way forward with this
<fantasai> iank_: I wasn't previously aware we discussed this 4-5 years ago
<fantasai> iank_: I don't think proposed solution is the best, but can go from there
<fantasai> iank_: was that enough background?
<fantasai> iank_: I can sort of go through the previous proposal, and concerns with it
<fantasai> iank_: I've also got an alternate proposal
<fantasai> iank_: Previous proposal was to use the min-content/max-content and presumably fit-content keywords on the height
<fantasai> iank_: when you specify this, you would flip the intrinsic size to calculate based on the rows
<fantasai> iank_: to being based on content instead
<fantasai> iank_: I have a few issues with this
<fantasai> iank_: it creates a slightly weird discontinuity, currently on form controls there is an algorithm for calculating intrinsic size of form controls
<fantasai> iank_: if you say height: auto; height: min-content; or height: fit-content, shoudl behave same
<fantasai> iank_: other issue is it's difficult to feature-detect
<fantasai> iank_: it also makes it difficult for layout algorithms, when flexbox or grid ask for min-content size of something, do we take into account
<fantasai> iank_: very subtle
<fantasai> iank_: the final thing is that, and we can debate this, there's a valid path forward
<fantasai> iank_: a lot of the time you don't actually want it to be the exact height of the content
<fantasai> iank_: 2 reasons
<fantasai> iank_: 1, you want space for placeholder
<fantasai> iank_: 2, you kindof want it to work the same as the lh unit
<fantasai> iank_: if you set min-height: 2lh
<fantasai> iank_: max-height: 4lh
<fantasai> iank_: you want the content, the intrinsic size, to be based on the number of lines times the lh unit
<fantasai> iank_: when you're typing and you fall back to a different font, can create a visual glitch
<fantasai> iank_: I'll pause there ...
<fantasai> iank_: My proposal is to add a new property, e.g. textarea-rows: auto | <integer>
<TabAtkins> q+
<fantasai> s/integer/number/
<fantasai> iank_: You typically don't want to reserve space for the placholder
<fantasai> florian: I'm confused about the proposal. I presume auto is the default?
<fantasai> TabAtkins: no?
<fantasai> TabAtkins: Then what's the default?
<fantasai> iank_: ON the textarea, you have textarea-rows: 2
<fantasai> iank_: and then preshint mapping that maps rows attribute to this property
<fantasai> florian: thanks, that helps
<fantasai> iank_: today we have textarea-rows: <number> only
<fantasai> iank_: and rows attribute would get mapped to this property
<fantasai> florian: that makes more sense, thanks
<astearns> ack TabAtkins
<fantasai> TabAtkins: I agree with your reasoning against the older proposal to use max-content
<fantasai> TabAtkins: max-content messes with our existing algorithms, and the fact that you do want this to be a multiple of lh
<fantasai> TabAtkins: i was convinced by your arguments, so I support adding this property with the auto value
<fantasai> iank_: I should also say that we can bikeshed the name, this is a very basic name
<fantasai> florian: This new property is alternative to using height with lh?
<fantasai> TabAtkins: if that's what you want to impose, yes
<fantasai> iank_: It's useful even if you don't support lh unit
<fantasai> iank_: currently looking at doing both atm
<astearns> ack fantasai
<TabAtkins> fantasai: I don't really like a new specialzied property to a single element
<TabAtkins> fantasai: when we could get the same effect with a generic property
<TabAtkins> fantasai: I think the feature detection is one we have for a lot of things
<TabAtkins> fantasai: and that's not a reason to invent a new property every time
<TabAtkins> q+
<florian> s/to using height with lh?/to using height with fit-content, but you'd use it in combination with min-height / max-height with lh?/
<TabAtkins> fantasai: in terms of other things being multiples of lh vs actual height, i think that's interesting
<emilio> q+
<TabAtkins> fantasai: do we actually want that multiple-lh szie? or fit X lines?
<TabAtkins> fantasai: can we figure out how to do that with the step-sizing properties?
<TabAtkins> fantasai: if so we can apply it to other elements besides text area
<TabAtkins> fantasai: other point wrt min/max content sizing
<TabAtkins> iank_: discontinuity that height:auto and height:fit-content won't match
<TabAtkins> fantasai: I don't think that's a discontinutiy - *usually* auto maps to a content keyword
<TabAtkins> fantasai: but on some elements it maps to a fixed number
<TabAtkins> fantasai: that's fine, auto can map to anything
<TabAtkins> iank_: today auto doesn't map to a fixed number
<TabAtkins> iank_: because the way most form elements work is that min/max-content returns a number, and auto maps to one of those
<TabAtkins> fantasai: yeah but for some els auto maps to stretch
<TabAtkins> iank_: yeah that's fine
<TabAtkins> iank_: so like what happens when you do min-height:min-content; height:auto; on the textarea?
<TabAtkins> fantasai: min-height would look at the number of lines in the box and come up with a height
<TabAtkins> fantasai: and then auto would represent what it currently does
<TabAtkins> iank_: which is fit-content
<TabAtkins> fantasai: is it? isn't it a fixed number o flines/
<TabAtkins> iank_: For us and other engines, that's what fit-content means, yes
<TabAtkins> iank_: if you say height:min-content today on a form control, it'll return a form control algo
<TabAtkins> fantasai: so your concern is the backwards incompatibility of specifying a content keyword to have this behavior, rather than today's behavior
<TabAtkins> iank_: I was considering if it was a compat problem, it's probably not. it's more how the engines work is that auto maps onto two things - stretch or fit-content
<TabAtkins> iank_: and all the form elements behave similarly
<TabAtkins> iank_: min/max-content is defined by some internal algo; for textarea it's rows*lh, basically
<TabAtkins> iank_: and if you have height:min-content we swap the internal algorithm for that
<TabAtkins> q+
<TabAtkins> fremy: a point that convinced me is you don't want to snap to any random pixel value, but a proper number of lines. can't rely on that with fit-content
<TabAtkins> fantasai: content is quite limited; you can't format it
<TabAtkins> TabAtkins: but you can have fallback fonts
<TabAtkins> fantasai: yeah, but we already have problems with line heights from that
<TabAtkins> fantasai: unsure if that's the reason we should add a specialized new property that only works for text areas
<TabAtkins> fantasai: we should decide if we want the content to accommodate font changes and if we want a consistent line height we should be able to say that with text properties
<fremy> q+ (other question)
<TabAtkins> iank_: It's not affecting those line height, it's affecting the intrinsic size
<florian> q+
<fremy> q+
<fremy> ack (other
<fremy> ack question)
<TabAtkins> fantasai: if height:auto effectively computed to 40px on a textarea, how would that be different fro mthe current situation we're in today? isn't that what's being proposed?
<TabAtkins> iank_: it's not, auto only maps to stretch or fit-content
<TabAtkins> fantasai: that's not true
<TabAtkins> iank_: Where?
<TabAtkins> fantasai: It's whatever we define it to be.
<TabAtkins> iank_: Right but we don't do anything else today. Today it's just the two
<TabAtkins> fantasai: I'd prefer to add a new concept ot the engines than to author-exposed stuff
<TabAtkins> fantasai: I'd prefer not having authors have to learn a new concept
<TabAtkins> iank_: what happens for things asking for min-content sizes?
<TabAtkins> fantasai: typically asking for min-content contribution, not size
<TabAtkins> fantasai: if auto resolves to an (effectively) fixed value, your contribution will be taht size
<TabAtkins> [missed a bit]
<astearns> ack TabAtkins
<fantasai> TabAtkins: It seems 1 point of your objection is about having us this be a special-purpose property
<fantasai> TabAtkins: I think for sufficiently magic element,s I think it's fine
<fantasai> TabAtkins: we already have a large number of singe-purpose properties from SVG
<fantasai> TabAtkins: I don't think it's a problem to add them to HTML also
<astearns> ack emilio
<dholbert> q+
<TabAtkins> emilio: one issue when consdiering whethe rto change height is that the default for textarea is border-box
<TabAtkins> emilio: so it may not be right to match content there
<dbaron> s/whethe rto/whether to/
<TabAtkins> fantasai: we don't have to change that - when you say height:max-content, you don't take the size and make the border-box that height
<TabAtkins> emilio: I think for most purposes, block-size max-content is auto
<TabAtkins> fantasai: auto is treated as intrinsic, rather
<TabAtkins> fantasai: like in grid, it's stretch not auto
<TabAtkins> emilio: I'm pretty sure we've discussed how intrinsic block sizes should behave and I'm 90% sure that our beahvior maps intrinsic block sizes to auto except in special cases
<TabAtkins> fantasai: you're saying they're treated the same way, and you had "auto" first so you call it that in the codebase, but fundamentally it's the max-content size and "auto" maps to that
<TabAtkins> emilio: Not convinced. If you do width:min-content and have box-sizing:border-box and have padding, do we add padding to that width?
<TabAtkins> fantasai: yes, or else it would be broken
<TabAtkins> dbaron: I think what's supposed to happen is if you're using width/height:intrinsic-stuff, box-sizing doesn't change the box affected
<astearns> ack florian
<emilio> ack emilio
<TabAtkins> florian: One thing fantasai was concerned abou tis that this is a textarea-only property
<TabAtkins> florian: But theoretically this could apply to any block container
<TabAtkins> florian: So if you leave logical proeprties aside, couldn't this be a new set of properties that fit into the sizeing property?
<fantasai> TabAtkins: that's step-sizing
<dbaron> s/box affected/size of the box/
<astearns> ack fremy
<TabAtkins> fremy: great segue
<TabAtkins> fremy: i'm wondering about the issue of scrollbars
<TabAtkins> fremy: textarea do overflow, and they get scrollbars which can change the lines on the screen
<TabAtkins> iank_: we don't have have interop
<TabAtkins> iank_: i think most engines do... something complicated
<TabAtkins> iank_: effectively, if you have overflow:scroll, we'll reserve space
<TabAtkins> iank_: if it's overflow:auto we won't reserve space
<TabAtkins> iank_: it's like the text area has word-space;break-all on it
<TabAtkins> iank_: very hard to trigger inline overflow
<TabAtkins> iank_: if you do manage to trigger it it'll still be like two rows of overlap
<TabAtkins> fremy: how do you know in advance what you'll need?
<TabAtkins> fremy: will you get in a situation where if you don't have reserve the space it doesn't need a scrollbar?
<TabAtkins> iank_: This varies engine by engine, but Chrome's behavior we won't...
<TabAtkins> iank_: [compiles chrome in their head]
<TabAtkins> iank_: it's complicated
<TabAtkins> iank_: we'll only ever add scrollbars. that'll typically result in an extra row
<TabAtkins> iank_: but we'll never remove them after that point
<TabAtkins> fremy: makes sense
<astearns> ack dholbert
<TabAtkins> dholbert: going back to needing a special prop
<TabAtkins> dholbert: one thing not clear to me is why we are reaching for a css proerty rather than an html attribute
<fantasai> TabAtkins: Whether your item is content-sizable, might depend on the layout of the page
<fantasai> TabAtkins: in some cases, might have the space, but might change based on MQ
<florian> q?
<fantasai> TabAtkins: so needs to be in CSS so you can change as needed
<fantasai> TabAtkins: based on MQ and CQ
<florian> q+
<astearns> ack florian
<TabAtkins> florian: might have missed part of the discussion but didn't see the answer...
<TabAtkins> florian: Can we do this with step-sizing?
<TabAtkins> florian: How do we solve this with step-sizing? what's missing?
<TabAtkins> iank_: where is step-sizing proposal
<fantasai> https://www.w3.org/TR/css-rhythm-1/#block-step-size
<Rossen_> q
<fantasai> https://www.w3.org/TR/css-rhythm-1/#block-height
<oriol> q+
<TabAtkins> iank_: where is this actually counting the number of lines?
<TabAtkins> fantasai: you write 'block-step-size: 1lh' and it'll round your block to a multiple of 1lh
<fantasai> TabAtkins: This doesn't address making textarea content-sizable in the first place
<TabAtkins> iank_: there's nothing here that changes the way that the textarea calculates its size otherwise
<astearns> ack oriol
<fantasai> probably the spec should clarify that the intrinsic sizes are also stepped
<TabAtkins> oriol: instead of step-sizing, i also remember in overflow there was a max-lines property
<TabAtkins> oriol: [missed]
<florian> q+ to respond to oriol
<TabAtkins> oriol: what if we had as a pres hint converting the rows attribute into a max-lines, and if you want to size the textarea according to content, you set max-lines:none
<astearns> ack florian
<Zakim> florian, you wanted to respond to oriol
<TabAtkins> florian: Havne't totally followed logic, but max-lines in overflow is a very different beast
<TabAtkins> florian: it's fragmentation, not sizing
<TabAtkins> florian: what it does with the nmber of lines is very different and doesn't work here
<TabAtkins> florian: as a reminder, max-lines triggers a forced break after the number of lines if your container is a fragmentainer
<TabAtkins> florian: I don't think we want forced breaks, we want to size things
<TabAtkins> fantasai: also max-lines actually uses the line size, if one line is taller it breaks after a whole number of lines
<TabAtkins> fantasai: also it's max-lines, if your block is max-lines:5 but has three lines in it, it'll still be 3 lines tall
<TabAtkins> <br dur=15min>
LeaVerou commented 1 year ago

I missed this discussion, but here are my two cents about what was said:

yisibl commented 1 year ago

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?

Somnium7 commented 1 year ago
  • 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!

clshortfuse commented 1 year ago

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.

bfgeek commented 1 year ago

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; }
css-meeting-bot commented 1 year ago

The CSS Working Group just discussed [css-ui] ? Allow <textarea> to be sized by contents..

The full IRC log of that discussion <dael> iank_: The goal is to allow for textarea to grow number of content and grow in inline dimension with content
<dael> iank_: Previously discussed to achieve with min intrinsic sizing property. Seems like a reasonable idea
<dael> iank_: One way would be add 2 new values, one for textarea and one for input
<dael> iank_: you would say min-intrsinzic-sizing and it would switch to being sized based on the area
<dael> iank_: Any thoughts or concerns?
<dael> astearns: Reason you are using the element names instead of something more generic like from-content?
<vmpstr> q+
<astearns> ack fantasai
<dael> iank_: They sort of work in different axes. textarea is in block and inputs are intrinsic for block dimension. If someone sets a reset it's not clear you want both so probably need indiviual control
<dael> fantasai: I had the same question about why 2-axes. If we're splitting the axes we should maybe consider axis. Is it split on axis or on type of input?
<dael> fantasai: And then is this going to change the way auto works?
<dael> iank_: What do you mean by change how auto works?
<dael> fantasai: When the input as an auto-width it currently sizes to a magic number
<astearns> from-inline-content-size/from-block-content-size (for those who like to type a lot)
<dael> iank_: full width for textarea we haven't heard demand to grow in inline size
<dael> iank_: Is theoretically possible but I would prefer to keep. If we did it for both axes for textarea it would be weird behavior when they're in something like flexbox. Current magic is simple and looks at columns
<dael> iank_: Prefer to keep textarea just for block. If we split min-intrinsic-sizing that's the other issue if we want to add it.
<miriam> q+
<dael> vmpstr:
<astearns> ack vmpstr
<dael> vmpstr: I just wanted to ask for input if I spec from-input:min-intrinsic-size would the input shrink below default size?
<dael> iank_: Correct. Expected webdev will put a min-width on that element
<astearns> ack miriam
<dael> miriam: I like this. For me your argument it does one thing on textarea and another on input seems like an argument for one value. Seems weird to align the right value to the right element
<dael> miriam: Why do I need to say textarea as the target and the value rather than getting the right behavior
<dael> iank_: Related to next issue which is splitting axes for min-intrinsic-sizing. Might be better to talk about that first. If we split that property in 2 than from-input would only apply to one.
<dael> iank_: Could have a from-content but somewhat find that more difficult to understand. I think this is less magic. content is an overloaded word
<dael> iank_: I like the explicitness of from-input and from-textarea
<dael> astearns: I like solution of splitting min-intrinsic-size as a solution to the reset issue
<dael> iank_: I would still come back to some content would be confusing
<dael> iank_: It is possible; we could have intrinsic sized text areas in inline axis. I don't think it's desireable
<dael> astearns: That's not part of current prop, right?
<astearns> ack fantasai
<dael> iank_: Correct. Future thing to keep in mind
<dael> fantasai: I think this is workable. From author PoV I think the original proposal we had discused to use min and max-content feels more understandable. I'd prefer that if we can
<dael> iank_: I don't think we can. Flexboxes are everywhere and it would change the auto min-size. We'd need an explicit switch for switching the algo similar to what min-instrinsic-size does for scrolling. We won't be able to shift that
<dael> fantasai: What's the case wehre it changes behavior?
<dael> iank_: A few. People use min-content/max-content on things already. Would change any input elements with this already. Other thing is it would change flexbox. Flexbox uses min-content size directly and that would change
<dael> iank_: I think emilio tried to implement. I don't think we can shift it
<dael> fantasai: Make sense to put keywords onwidth and height? Seems like a pretty indirect method.
<dael> iank_: At the moment it's siwtching the algo we use to determine content size. All input elements and textarea have a special metric and this would default to the default. I think min-intrinsic-sizing is a good place to do this since the min size for scrollable is there also
<dael> fantasai: I think I'd like to talk this over with TabAtkins. But I agree we need to solve the use case
<dael> iank_: As long as it's not...been trying to come up with a proposal that satisfies everyone's desire for a while. I think this is the best so far
<dael> fantasai: min-intrinisic-sizing property is about min and not intrinsic sizing. Or is this a new proposal? We have an intrinsic-sizing property. Is this separate?
<dael> iank_: This is the original property
<dael> fantasai: That's about how we find the min instrinsic size, but not about something like max intrinsic size.
<dael> iank_: Weird thing about inputs is they're the same thing
<dael> fantasai: Yeah, some elements don't have difference between min and max. A div with one word has equal min and max.
<dael> iank_: Input elements that's also true
<dael> fantasai: Right, b/c they have a fixed length intrinsic size.
<dael> fantasai: We're trying to allow elements to be resized from their content. For a text area in block axis min and max will be the same. Concept does exist for difference if you want it
<dael> fantasai: Would make sense if you tried to size as if they're a regular element you get that behavior. And you can't here because it's only giving one size.
<dael> fantasai: Probably solves use case, but I think what makes sense is to make this behave more like a replaced element
<dael> iank_: Specifically for input case you cannot get an input to wrap multiple lines
<dael> fantasai: Yes, they're no-wrap
<dael> iank_: You can't get min vs max for these elements today
<dael> fantasai: But if we wanted to do something on inline axis for textarea there would be possible 2 sizes
<dael> iank_: But no one is asking for that
<dael> fantasai: I agree. I think if we can try and get everything to fit in that's a good directly
<dael> fantasai: I think min-intrinsic-size...what min-width:auto resolves to is a little different from what is the intrinsic size.
<dael> fantasai: I would rather try and talk this over with TabAtkins
<dael> astearns: Can you commit to that over the next week?
<dael> fantasai: If TabAtkins is available tomorrow or Friday, yeah.
<dael> [discussion on TabAtkins availability]
<dael> astearns: iank_ okay with you if we put this and 8620 on the agenda next week?
<dael> iank_: Sure
<dael> astearns: Next week we'll start with content-visibility and then this two issues
fantasai commented 1 year ago

OK, seems like we have three ways forward:

LeaVerou commented 1 year ago

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.

bfgeek commented 1 year ago

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.

bfgeek commented 1 year ago

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.

@LeaVerou - This isn't the primary compat concern. The primary concern the is auto-min size behaviour.

bfgeek commented 1 year ago

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.

tabatkins commented 1 year ago

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.

bfgeek commented 1 year ago

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).

fantasai commented 1 year ago

@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.

clshortfuse commented 1 year ago

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.

LeaVerou commented 1 year ago

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.

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?

clshortfuse commented 1 year ago

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.

tabatkins commented 1 year ago

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 commented 1 year ago

@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.

tabatkins commented 1 year ago

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.

SebastianZ commented 1 year ago

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

css-meeting-bot commented 1 year ago

The CSS Working Group just discussed [css-ui] ? Allow <textarea> to be sized by contents., and agreed to the following:

The full IRC log of that discussion <bramus> tabatkins: had discussion about ability to autosize … we landed on few possible options
<bramus> tabatkins: one is we do simply define that min-content and related keywords when used on … will flex the content size as if they were normal elements
<bramus> TabAtkins: option 2 is to add new keywords that mean that, so current beahvior would not change
<bramus> TabAtkins: option 3 is we add a new property that toggles these elements into behaving like elements filled with their content.
<bramus> TabAtkins: I believe that option 1 is a no go. these existing keywords are used quite widely and would be imcompatbile in practice
<bramus> TabAtkins: real concern is new keyword on sizing props or with a seperate property toggle
<bramus> TabAtkins: I belive that fantasai prefers keyword based, and iank prefers option 3
<lea> q?
<lea> q+
<bramus> TabAtkins: there are a number of places in the sizing algos that invoke min-content behavior implicitly
<bramus> TabAtkins: eg. textarea width 100% in table
<bramus> TabAtkins: this means that there are lot of cases that would implicitly trigger the older non-content aware behavior if we gave authors access to these keywords
<bramus> TabAtkins: if we have a way to say 'act like a normal element' it would be fine.
<bramus> TabAtkins: iank’s experiment turned out to confirm this, and its a trivial implmentation, and its predictable
<bramus> TabAtkins: 'just act like a normal element' switch seems like to be the right way
<emilio> q+
<bramus> TabAtkins: other ways would be hard to predict
<bramus> TabAtkins: would like to move into direciton of a form-sizing property
<Rossen_> ack lea
<bramus> lea: do we know option 1 is not feasible with facts or are we just worried?
<bramus> TabAtkins: have not looked at the date, but am virtually certain that it’ll break things
<bramus> TabAtkins: min-content is used fairly regularly, and might also be used on inputs and could thus break the page
<bramus> iank_: other concern is typically inputs are used heavily in etnerprise usecases, and we sort of have an anlysis blind spot, can}t entirely rely on http archive data
<bramus> lea: if compat is not an issue, is then option 1 better for authors?
<bramus> lea: maybe we should explore if these concerns are founded? or add UA css?
<bramus> lea: just thinking out loud
<oriol> q+
<bramus> TabAtkins: option 1 and 2 are pretty bad on how often we invoke intrinsic layout algos
<Rossen_> ack emilio
<bramus> emilio: i agree that going for the toggle seems like the most straightforward
<bramus> emilio: to what extent do we want to create this toggle? only intrinsic sizing? replacingnes? would they respect display?
<lea> q?
<bramus> emilio: toggle seems most reasonable but would like more details on it
<bramus> emilio: i guess that can b efigured out
<bramus> iank_: toggle would only trigger normal intrinsic sizing behavior, so auto would not be semi-magic anymore
<bramus> iank_: would change compressability (?)
<bramus> emilio: seems easier to implement and reason about
<lea> would all three approahes also work for adjusting <input> width by its contents?
<bramus> oriol: 4th option? would not require any new value
<emilio> lea: yes (afaict)
<bramus> oriol: remins me of what happens when adding size containment wit cis
<bramus> oriol: UA CSS? authors could override
<bramus> oriol: seems like less compat issues
<Rossen_> ack oriol
<bramus> iank_: does not work because some elems are a fixed length, but others are not (depending on UA)
<bramus> iank_: some are content based with an implicit minimum
<bradk> Is this just for text-entry controls, or for buttons, etc too? Presumably not for iframes?
<emilio> q+
<Rossen_> ack emilio
<bramus> Rossen_: looks like we are circling around toggle option
<bramus> emilio: maybe we can also use this prop to remove ???
<bramus> emilio: can be discussed at other time
<bramus> s/???/min line height of normal/
<bramus> emilio: its needed for compat
<bramus> iank_: we will likely get an implementation up, and then work through the inputs one by one
<bramus> iank_: should look at line height thing indeed
<bramus> Rossen_: Let’s try to resolve
<bradk> +1 for toggle
<Rossen_> ack fantasai
<bramus> fantasai: i have doubts
<bramus> fantasai: trying to understand all the compat impact
<bramus> fantasai: ok with resolving, but unsure about the direction we are about to take
<bramus> Rossen_: my understanding was you were proponent for option 3
<bramus> TabAtkins: nopes, other way around
<bramus> iank_: using auto sizing breaks option 1 and 2 very easily
<bramus> iank_: eg inputs in table
<bramus> iank_: lot of magic needed for 1 and 2
<bramus> Rossen_: lets make progress here, can always revert
<TabAtkins> proposed resolution: 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
<Rossen_> e add a new property that toggles these elements into behaving like elements filled with their content.
<bramus> Rossen_: not hearing objections
<bramus> 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
<fantasai> s/auto/contents/
clshortfuse commented 1 year ago

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?

nt1m commented 9 months ago

nitpicking here but normal / auto isn't super obvious for someone who doesn't know the history of how form controls are sized.

nt1m commented 9 months ago

@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.

nt1m commented 9 months ago

(fixed probably isn't 100% accurate either, but describes the behavior better I think)

tkent-google commented 9 months ago

<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.

nt1m commented 9 months ago

It's unclear what form-sizing: none means to me.

lukewarlow commented 9 months ago

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.

Loirooriol commented 9 months ago

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.

fregante commented 9 months ago

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

annevk commented 9 months ago

@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.

bfgeek commented 9 months ago

Adgenda+ to bikeshed.

chasebank commented 8 months ago

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.

Loirooriol commented 8 months ago

What do you mean input word wrapping? Inputs are single line.

LeaVerou commented 8 months ago

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".

chasebank commented 8 months ago

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.

yisibl commented 8 months ago

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.

bfgeek commented 8 months ago

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

chinchang commented 8 months ago

Would the textarea height be animatable?

brandonmcconnell commented 8 months ago

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.

lukewarlow commented 8 months ago

Here is the resolution https://github.com/w3c/csswg-drafts/issues/7542#issuecomment-1542505774

brandonmcconnell commented 8 months ago

@lukewarlow Thanks!

joliss commented 8 months ago

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.

image

Some interesting edges cases off the top of my head:

LeaVerou commented 8 months ago

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:

clshortfuse commented 8 months ago

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