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-forms] Allow making <button> a real inline (or block/flex/etc) #3226

Open
zcorpan opened this issue Oct 19, 2018 · 27 comments
Open

[css-forms] Allow making <button> a real inline (or block/flex/etc) #3226

zcorpan opened this issue Oct 19, 2018 · 27 comments

Comments

@zcorpan
Copy link
Member

zcorpan commented Oct 19, 2018

Use cases & requests from web developers

  • https://twitter.com/ned/status/1051798190951940096

    button { display: inline; } should work.

    Yes, I want to make buttons that wrap lines like a normal inline elements, but retain the semantics and accessibility of a button.

    that's the point: it should be able to just flow its contents, it's crucial for action items inserted in text as pseudolinks. For now we have to hack around with <span role=button tabindex=0 onkeydown=handleEnterOrSpace(event) onclick=handleClick(event)> or just use <a href=#>

  • https://twitter.com/micahgodbolt/status/1047218367432544257

    So, what if you have some long text in a paragraph where on click, it performs an action (not an href)

    You'd use a <button> right?

    Okay, then how would you make that button LOOK like text in a paragraph.

    https://codepen.io/micahgodbolt/pen/zmrxQd

  • https://www.scottohara.me/blog/2018/10/03/unbutton-buttons.html

    This post discusses the problem, and explores styling buttons with display: contents but notes that it loses the ability to receive keyboard focus and there's no accessibility node so it's not exposed as a button to screen readers.

Defining layout model for buttons

In whatwg/html#4081 I'm working on specifying the layout model that buttons use. Currently my working draft for this is part of https://docs.google.com/document/d/1FE5YIoirPKxYbbnMd8kS6w39M8bzLTl5tf4wwOxR1wc/edit?usp=sharing

In particular, buttons have an inner anonymous box that is a block formatting context, and I think this is the cause of the inability to make buttons be inline.

Proposal

One way to solve this could be to add a pseudo-element to target the anonymous box, so that web developers can set it to 'display: inilne'. The button would still be focusable and be exposed the same in the accessibility tree.

Thoughts?

cc @tkent-google @MatsPalmgren @hober @FremyCompany

@Loirooriol
Copy link
Contributor

I don't think you can explain all the <button> magic just by adding an inner box.

For example, height doesn't apply to non-replaced inlines, but a <button> obeys height even with display: inline. Also, the ::part(button-contents) { display: flow-root } which you include in the default UA stylesheet would split a non-replaced inline <button>.

So it seems <button> always needs to be replaced, not just "if the element is absolutely positioned" or "For the purpose of the ‘normal’ keyword of the ‘align-self’ property".

@zcorpan
Copy link
Member Author

zcorpan commented Oct 19, 2018

It indeed needs to act as a replaced element for some purposes by default. But we could disable the replaced element-ness if the inner box is not block-level, or some such.

Basically like this, as author CSS to unstyle a button:

button { appearance: none; display: inline; font: inherit; background: initial; border: initial; padding: initial }
button::part(button-contents) { display: inline }

@Loirooriol
Copy link
Contributor

I don't like that the kind of box that is generated depends on the display value of the children, because the computed display value of the children depends on the formatting context in which they participate.

IMO a better way to approach this would be adding a new CSS property which can force an element to establish an independent formatting context (or behave like an inline-block if it's a non-replaced inline). Enable this on buttons via UA stylesheet, but let authors revert it to normal behavior.

@zcorpan
Copy link
Member Author

zcorpan commented Oct 21, 2018

Good point, and good idea. If that property is used to turn off the independent formatting context, it would also turn off the element acting like a replaced element? And the inner box would inherit that property?

@jonjohnjohnson
Copy link

I don't think I understand the reasoning for not just specifying appearance: none on <button> as simply removing the layout "magic" and turning off the replaced element behavior? Affording a computed inline value on display? Or is it just too late to do this? #3024

@zcorpan
Copy link
Member Author

zcorpan commented Oct 21, 2018

I doubt that would be web compatible.

@SelenIT
Copy link
Collaborator

SelenIT commented Oct 21, 2018

What if the appearance: none alone would preserve the default display: inline-block behavior of the button (making it behave similarly to how replaced elements behave, but without any magic), and changing its display to inline would convert it to the regular non-atomic inline box? Maybe this could be web compatible? Are there many web pages that use both appearance: none and display: inline for buttons while relying on their "replaced-element-like" behavior?

However, having the pseudo element that wraps all the content of the button could be useful for solving #2632 — it could get the default focus outline (similarly to that of the inline a element) even when the button element doesn't generate its own box.

@Loirooriol
Copy link
Contributor

Good point, and good idea. If that property is used to turn off the independent formatting context, it would also turn off the element acting like a replaced element?

Well, I didn't think much about the differences between a normal inline-block and a replaced one, I'm not an expert in this. I don't think the property should allow to convert arbitrary elements to replaced, just create an independent formatting context, but maybe handle this case specially for buttons. Then yes, setting it to the initial value in buttons could make them normal inlines.

And the inner box would inherit that property?

I don't really see how the inner box helps explaining the behavior of buttons. It can be useful for some unrelated purposes, but then I prefer my ::contents proposal (#2406).

Are there many web pages that use both appearance: none and display: inline for buttons while relying on their "replaced-element-like" behavior?

I don't know but I have seen "CSS reset" stylesheets which use something like

* { display: inline }
section, main, div /*...*/ { display: block }

It's possible that they forgot button { display: inline-block } and relied on this behavior anyways.

@MatsPalmgren
Copy link

Using both appearance: none and display: inline might work from an implementation POV, assuming that we define all buttons to have display: inline-block in the UA sheet. (Currently, Gecko only has that on <button> but not on <input type=button> etc. Chrome has it on all buttons AFAICT, so it should be OK to change it in Gecko.) I'm a bit worried about changing the meaning of that combination of values though. We probably need to add a counter to see how common that is first.

@zcorpan
Copy link
Member Author

zcorpan commented Oct 30, 2018

Here are rules and their counts in httparchive that set *-appearance: none and display: inline

https://gist.github.com/zcorpan/4fea68aad8265cfa45d83f80586c9cf8

The sum of the nums is 2361. The data set is 1.3M pages. There could be multiple matches for a single page. Not all of these are buttons, and maybe some of these want to really unstyle their buttons.

@SelenIT
Copy link
Collaborator

SelenIT commented Oct 30, 2018

There could possibly be some buttons with display: inline implicitly set via all: initial/unset, but I suppose that it would be safe now to imply that authors who use this do want to remove all the rendering "magic" from the buttons, including "magic inline-block-ness".

@zcorpan
Copy link
Member Author

zcorpan commented Oct 30, 2018

To clarify, an explicit appearance: none would be needed, not just implicit by setting a border or a background color, right?

@SelenIT
Copy link
Collaborator

SelenIT commented Oct 30, 2018

I believe so. Honestly, in my opinion, what happens to the button when its default background is changed is "simplifying" its appearance rather than "undoing" it: the button loses the default OS-themed style, but (at least in Windows Chrome) it still has "pseudo 3d"-style border and it appears "pressed down" when activated.

@SelenIT SelenIT closed this as completed Oct 30, 2018
@SelenIT SelenIT reopened this Oct 30, 2018
@zcorpan
Copy link
Member Author

zcorpan commented Oct 30, 2018

Yeah but appearance: none does the same (setting border or background implicitly sets appearance: none).

@zcorpan
Copy link
Member Author

zcorpan commented Oct 30, 2018

See whatwg/html@c1e1f6f for a proposal for this.

@SelenIT
Copy link
Collaborator

SelenIT commented Oct 30, 2018

appearance: none does the same

Thanks, I didn't realize this before! Then, it seems that the real meaning of display: none is "remove OS/browser-specific theming" rather than "remove the element's specific behavior". If so, it seems that my idea to use display as an extra switch was not very good. Maybe it would be better to leave appearance: none as is and introduce a new value (e.g. appearance: plain) with the latter meaning, with no extra dependency on other properties?

@zcorpan
Copy link
Member Author

zcorpan commented Jan 25, 2019

Discussion on twitter here

https://twitter.com/benoitrouleau/status/1088883974296997890?s=20

Made me think that maybe display: inline isn't a good hook for this. It's good if you want an inline, but what about if you want a without-magic block/grid/other?

Maybe a new property is better, that is set by the UA stylesheet so that all: unset removes the magic.

@ExE-Boss
Copy link
Contributor

I’d much rather have display: inline; act like a normal inline element and the current behaviour use display: inline-block;

@benface
Copy link

benface commented Jan 28, 2019

@zcorpan Might I suggest changing the title of this issue to "Allow making a real inline/block/flex/whatever"?

@zcorpan zcorpan changed the title [css-forms] Allow making <button> a real inline [css-forms] Allow making <button> a real inline (or block/flex/etc) Jan 28, 2019
@SelenIT
Copy link
Collaborator

SelenIT commented Jan 29, 2019

@benface, the display:inline case for buttons is a bit special: it's a "final frontier" that is yet not achievable by CSS in any implementation. Implementers seem to have agreed that the ability to style buttons as flex or grid container is necessary, and at least Firefox has already implemented it somehow. But making a button display as a regular non-replaced non-atomic inline element is currently not achievable anywhere. That's why it was initially stressed in the title.

@zcorpan, when I proposed display:inline as a hook for removing "magic" replaced-like behavior, I didn't realize that explicit and implicit change of appearance are equivalent, I assumed that explicit change has deeper effect. Sorry, it was my error. Now I believe that there probably should be two different values of appearance – the good old none to remove the platform-specific default theming (borders, background, focus/hover highlighting etc.) while still preserving the "magic replaced-like" rendering behavior, and a new one to remove that "magic" altogether.

@benface
Copy link

benface commented Jan 29, 2019

@SelenIT Ah, I understand. I was more referring to the outer display though, e.g. make a <button style="display: block;"> expand to its container's width like a <div style="display: block;"> would.

@OliverJAsh
Copy link

I have another use case, but I'm not sure if it fits under the umbrella of this issue or whether it should be filed elsewhere?

By default, the contents of a button are vertically centred. I want to disable this, so that their behaviour matches all other (non-button) elements.

(If we want developers to use buttons for the best semantics/a11y, we must have ways to fully reset their styles.)

@SelenIT
Copy link
Collaborator

SelenIT commented Mar 18, 2019

@OliverJAsh, interestingly, different browsers seem to have different bits of control over this "magic". Chrome/Mac changes the vertical position of the button contents if I add flex-direction: column to the button styles (even without display: flex!), Firefox/Mac seems more logical and changes the position if I apply display: flex/grid to the button (but not block/flow-root, unfortunately).

@Loirooriol
Copy link
Contributor

@SelenIT In Firefox, if you use display: flex on a button, it will obey align-content and align-items. I think the idea is that buttons with display: block should also obey them, but alignment properties are not supported on blocks yet (https://bugzil.la/1105571)

@zcorpan
Copy link
Member Author

zcorpan commented Mar 18, 2019

That button is implemented in terms of Flexbox by default in Chromium is considered a bug per whatwg/html#4081 (comment)

The magic defined in whatwg/html#4143 is more likely to match what we end up doing I think, but it's this issue is still an open issue, i.e. how to disable the magic.

@SelenIT
Copy link
Collaborator

SelenIT commented Mar 21, 2019

Maybe it would make sense to define the default vertical centering of the anonymous button content box in terms of align-content for the "main" button box (it would be safe center, I guess?), so once browsers implement content distribution properties for block containers, this becomes part of the general rule rather than a "magical" exception?

@benface
Copy link

benface commented Aug 22, 2022

I recently created a separate issue about the impossibility to make some elements, including button, behave like a regular block element with no explicit width. But since 90% of the time, it's a button that I would like to see this for, I am going to repeat myself a little here.

I work on design systems / UI libraries, and in pretty much every library I've worked on, buttons are core components. And I always struggle with the same issue. I want my components to be flexible/responsive by default, meaning that when no explicit width is passed, I want them to adapt to their context: in block layout, they should stretch to fill their container's width (just like a div does), and in flex layout or other contexts (e.g. position: absolute), the shrink-to-fit algorithm should be used. Unfortunately, the only way I found to achieve that in a component that renders a button element is to render a div wrapper with no width, that contains a button element with a width of 100%. I wish it was possible for a button to be sized like a regular block element (e.g. div), so that the wrapper would no longer be necessary. In the issue I mentioned above, @dbaron mentioned that width: stretch should do that, but after reading [css-sizing-4] 6.1. Stretch-fit Sizing: filling the containing block, I don't think that's the case. Perhaps a solution could be a new keyword for width (I'm thinking width: block), but I am wondering if that's the right way to approach it or if the approaches proposed in this thread (i.e. appearance: none + display: block, or a new property altogether) would make more sense for this problem as well.

Thank you for reading. ❤️

EDIT: I just made a proposal in #10282 that should fix the above, which I realize is just one part of this issue (doesn't solve the "real inline" part).

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

9 participants