Skip to content
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

[css-sizing] Possible to reproduce a <div>'s width: auto behavior on any element? #7427

Open
benface opened this issue Jun 28, 2022 · 5 comments

Comments

@benface
Copy link

benface commented Jun 28, 2022

Hello dear CSS Working Group people!

I spend a lot of time building UI components, and I always try to make them as flexible as possible, in the sense that they should adapt well to different contexts (and different contents, if they accept children), ideally on their own (i.e. with no changes to their HTML/CSS and without relying on any JS).

One reliable way I've found to achieve that is to make components "responsive" or "fluid" by default, meaning that they should behave basically like a block element: their width should stretch to their parent's width, unless they are in a context that dictates their width or makes them "shrink-to-fit" to their intrinsic width, like if they are a Flexbox or Grid child, or the child of a position: absolute element that doesn't have a width. That is quite easy to do by making the root element of each component a <div> with no explicit width (so it uses the default value of auto), making it behave exactly as I just described.

That works, but in many cases, I would love the root element to be something other than <div>. For instance, if my component is a <button>, I haven't managed to reproduce the above behavior without a wrapper <div> (note: it doesn't have to be specifically a <div> – other block-level elements like <section> or even non-block elements like <span> with display: block on them work as well – but <div> makes the most sense semantically, since it's just a generic block container). That is because, unfortunately, width: auto doesn't have the same behavior on all elements, even with display: block. For instance, a <button> will "shrink-to-fit" its contents, as if it had display: inline-block or was inside a flex container or something (related issues: #3226, #6789). It's not just about <button> or (semi-)replaced elements, though: notably, <table> has the same problem.

I have tried all those new intrinsic sizing keywords for the width property (min-content, max-content, fit-content, and stretch), but none of them seem to do what I want, which is to reproduce the behavior that auto has for block-level elements such as <div>. I realize that the behavior I'm after is a mix between extrinsic and intrinsic sizing based on the context, but hopefully it makes sense to others.

width: 100% is also not an acceptable solution, as it doesn't behave the same as auto in a Flexbox or Grid context: instead of shrinking-to-fit, the component would always stretch to the container's (or grid area's) width, or at least mess with the flex sizing algorithm (assuming it has flex-basis: auto and there are other flex items that don't wrap). Just to explain why I want the shrink-to-fit behavior, let's take the button example again. On a given page or screen size (e.g. mobile), maybe we want the button to be "full width" in its container (e.g. a sidebar), but on another page or screen size (e.g. desktop), maybe we want the same button component to be centered inside its container. With the shrink-to-fit behavior, that centered requirement becomes as simple as placing it in a <div style="display: flex; justify-content: center;">. No need to change the button component itself; it adapts well to both contexts.

Again, I do have a functional workaround, which is to wrap the <button> in a <div> inside the component, and then set width: 100% on the <button> itself (it still shrinks-to-fit when desired/expected, because the parent <div> does). However, I can't help but wonder if it would make sense to introduce a new keyword for width that would reproduce the <div>'s width: auto behavior for any element, including <button>, <select>, <input>, <table>, etc. Maybe I missed something and it's already possible, in which case, please let me know. :)

Thank you for reading!

@dbaron
Copy link
Member

dbaron commented Jun 29, 2022

What about width: stretch isn't sufficient for what you want? I think it's intended to do basically what you're asking (at least based on a quick read of your comment).

I think to test it in current implementations, you'll need to use width: -webkit-fill-available and width: -moz-available, at least according to MDN. I think it's also likely that the existing implementations may need some improvement, so it would be good to find out what improvements are needed.

@benface
Copy link
Author

benface commented Jun 29, 2022

@dbaron – Thank you for your response. Unless the implementation of stretch / -webkit-fill-available / -moz-available is buggy in all browsers, it is not sufficient, no, as it doesn't shrink-to-fit when placed inside a flex container like a <div> does. In fact, I don't immediately see how it differs from width: 100%, which doesn't do what I want as mentioned in my first post. See this Codepen for a more visual explanation: https://codepen.io/benface/pen/KKowwYY

@dbaron
Copy link
Member

dbaron commented Jun 29, 2022

I think the shrink-to-fit behavior of width: stretch should be the same as the shrink-to-fit behavior that you get for a <div> with width: auto. If that's not the case, I think it's probably something we should fix.

@Loirooriol
Copy link
Contributor

What happens here is that width: auto typically means width: stretch for a normal block box.

But in flex layout they have different meanings. You can then use width: stretch; flex-basis: content, but you will have similar problems in grid layout.

Also, AFAIK width: auto can be different than width: stretch even for a normal block box, e.g. with justify-self: center.

Strictly speaking, I think what you want is a way for resolving justify-self: normal as if the element wasn't replaced.

But I'm not convinced that this is worth doing. It seems to me that e.g. if you turn the container into a flex container, then you can also make the button adapt to being a flex item.

@benface
Copy link
Author

benface commented Sep 28, 2022

Thank you so much for the insightful response @Loirooriol.

Strictly speaking, I think what you want is a way for resolving justify-self: normal as if the element wasn't replaced.

That very well could be! I didn't realize that justify-self was supposed to have an effect in block layout. That doesn't seem to be supported by any browser at the moment (see Codepen), but MDN does say:

In block-level layouts, it aligns an item inside its containing block on the inline axis.

Strangely, the "Browser compatibility" table doesn't have a "Supported in Block Layout" row, nor did I find the feature listed on caniuse.com.

I would add that the problem isn't necessarily about "replaced" elements. As I mentioned in my (way too long) initial post, it affects <table> as well.

But I'm not convinced that this is worth doing. It seems to me that e.g. if you turn the container into a flex container, then you can also make the button adapt to being a flex item.

Definitely, there are multiple ways to achieve the desired flexibility by allowing some kind of communication between the component and its consumer (e.g. in terms of React: props or context; or in terms of pure CSS: parent class + direct child selector, custom property, etc.). But the point is that the consumer should not have to do anything IMO. There should be a way to make it "just work", like it does with other element types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants