According to MDN, a button
is an inline element.
However, button elements have default styling with display: inline-block
(See this question)
button, textarea, input, select { display: inline-block }
So far so good.
If I now set the button with display:inline
- width still applies!!
button, div { width: 200px; border: 1px solid red; display: inline; }
<button>button</button> <div>div</div>
Now, according to the spec: width
does not apply to inline elements (which are non-replaced)
Applies to: all elements but non-replaced inline elements, table rows, and row groups
That being the case:
Why does width still apply to an inline button
element?
Inline element properties: The height of an inline element is the height of the content. The width of an inline element is the width of the content. The height and width of an inline element cannot be set in CSS. You cannot set the height and width of block-level elements in CSS.
inline The element doesn't start on a new line and only occupy just the width it requires. You can't set the width or height. inline-block It's formatted just like the inline element, where it doesn't start on a new line. BUT, you can set width and height values.
Most browsers display button elements as inline-block by default, according to the (not normative) Appendix D. Default style sheet for HTML 4. Therefore, you could expect the width property to work, as described in Calculating widths and margins - Inline-block, non-replaced.
Compared to display: inline , the major difference is that display: inline-block allows to set a width and height on the element. Also, with display: inline-block , the top and bottom margins/paddings are respected, but with display: inline they are not.
As mentioned in the comments, I'm pretty sure this has to do with browser-specific rendering behavior as is so typical of form elements. What I believe is happening when you set display: inline
on the button is... nothing. Effectively, it's the same as the typical browser default display: inline-block
, on which the width
property does apply.
Refer to section 10.2, which describes the width
property itself. In particular it explains why exactly the width
property does not apply to inline elements (or inline boxes):
This property does not apply to non-replaced inline elements. The content width of a non-replaced inline element's boxes is that of the rendered content within them (before any relative offset of children). Recall that inline boxes flow into line boxes. The width of line boxes is given by the their containing block, but may be shorted by the presence of floats.
In short, it's because the content of inline elements resides in line boxes. The width of a line box cannot be controlled directly; it is determined entirely by the containing block and any incidental floats. You can see an example of line box rendering in section 9.4.2, which describes inline formatting contexts.
If display: inline
actually made a button render as an inline box, all its contents would spill over and it would no longer look, or function, like a button. It makes sense to want to prevent that from happening, and I think that's just what browsers do.
So what exactly do they do to prevent this? Is a button a replaced element? I can't say for sure. But note, in section 9.2.2, it says:
Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements) are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.
Section 10 does not explicitly mention atomic inline-level boxes, but it does have sections for calculating dimensions for inline replaced elements, as well as inline-block elements whether replaced or non-replaced, all of which are considered atomic inlines as mentioned above. In all of these cases, the width
property applies as normal if it's not auto
.
So, while it's still debatable whether or not a button is a replaced element, it probably doesn't matter at all for the purposes of this question. But it is still some kind of atomic inline element, since it still participates in an inline formatting context. For what it's worth, though, it appears to shrink to fit its contents if you don't set a width, so its behavior is probably closer to that of an inline-block in that case. One could say then that the actual value of display
becomes inline-block
, although this is never reflected in the developer tools because the computed value does not change (again a side effect of browser-specific rendering behavior).
Since like Boltclock, I don't think that there's a simple answer to this, this is as much a dump of my thoughts on the subject as an answer, but I hope it will be informative.
Although the CSS display property is superficially quite simple, it actually contains a multitude of aspects. The CSS level 3 draft spec css-display captures some of this complexity, but still doesn't seem to cover it adequately.
The HTML5 spec says for the rendering of <button>
elements:
When the button binding applies to a button element, the element is expected to render as an 'inline-block' box rendered as a button whose contents are the contents of the element.
An inline-block
box has a number of aspects to it:
This means that it participates in a inline formatting context within a line box. It flows in sequence with other elements that are on the same line. The line box's content can be centre aligned with text-align:center
property on its container, and the line box is shortened by avoiding floated elements.
Unlike non-replaced display:inline
elements, the width value applies. But also, if a width value is not specified, a shrink-to-fit algorithm is applied to determine the width. This is like floated elements, or display:table
elements, but different from display:block
elements which are as wide as possible if no width is specified. It's also unlike replaced inline elements and replaced inline-block elements which, if no width is specified, use their intrinsic width if they have one and a default value of 300px if they don't. Shrink-to-fit is a meaningless concept for replaced elements.
Block container elements are make up of a stack of line boxes. The content flows from one line box to the next and the height of the inline-block elements grows (subject to overflow) to fully contain all the line boxes.
When the inline-block element contains multiple lines, its baseline is the last of those lines. This is unlike floats or display:table-cell
elements which are also shrink-to-fit, block container elements. Floats are outside normal flow so they do not have a baseline, which display:table-cell
elements have a baseline that is the baseline of their first line box. A button that has multiple lines does vertically align according this last line box rule.
inline-block
even when the specified value is inline
. But it doesn't account for the behaviour when specified value is block
. In this case, the element has a line-break before and after it, and margin:auto
centres the box as a display:block
element would, and is not what would be expected of inline-block
. However, its width for a specified value of auto
is shrink-to-fit like inline-block, whereas the expected behaviour for display:block
is as-wide-as-possible. As far as I know, the only display value that behaves like that is display:table
, but there is nothing else to suggest that display:table
is being used.
So there's nothing in the spec that I can find which matches this precisely. We can only hope that when the css-display spec gets completed, that it will cover this behaviour.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With