Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which magic CSS causes the difference of text-vertical-align between <button> and <div>?

Tags:

html

css

I found that the text inside of <button> is automatically vertically centered, while text inside of <div> is top aligned.

I tried to find out which CSS rule made the difference but failed.

div,
button {
  width: 4em;
  height: 4em;
  background-color: red;
  padding: 0;
  border: 0;
  margin: 1em;
  font-size: 1em;
  font-family: Arial;
}

div {
  text-align: center;
  display: inline-block;
  cursor: default;
  box-sizing: border-box;
}
<div>text text</div>
<button>text text</button>

<div>text text text text</div>
<button>text text text text</button>

<div>text text text text text text</div>
<button>text text text text text text</button>

As for the above example, comparing all the computed CSS rules from Chrome, I could only find one different pair -- align-items: stretch for <div> while align-items: flex-start for <button>.

But assigning align-items: flex-start doesn't help. So I got totally confused.

what confused me is that the text-vertical-alignment is different between <div> and <button> even if you set all the CSS rules with the same corresponding value. In other words, with the same CSS rules, <div> and <button> behave differently. Why?

What is the magic under the hood?


I can vertically center text inside of <div> (example below). I'm just curious about what causes the difference between the text-vertical-alignment.

Maybe it's not caused by a particular CSS rule, but because the layout algorithms for the two elements are different in browser?

div,
button {
  width: 4em;
  height: 4em;
  background-color: red;
  padding: 0;
  border: 0;
  margin: 1em;
  font-size: 1em;
  font-family: Arial;
}

div { /* basic CSS rules to button-fy  */
  text-align: center;
  display: inline-block;
  cursor: default;
  box-sizing: border-box;
}

/* Magic */
div, button {
  vertical-align: middle;
}

div span {
  display: inline-block;
  position: relative;
  top: 50%;
  -webkit-transform: translateY(-50%);
          transform: translateY(-50%);
}
<div><span>text text</span></div>
<button>text text</button>

<div><span>text text text text</span></div>
<button>text text text text</button>

<div><span>text text text text text text</span></div>
<button>text text text text text text</button>
like image 838
lzl124631x Avatar asked Oct 10 '16 11:10

lzl124631x


People also ask

How do I vertically align text in a div using CSS?

The CSS just sizes the div, vertically center aligns the span by setting the div's line-height equal to its height, and makes the span an inline-block with vertical-align: middle. Then it sets the line-height back to normal for the span, so its contents will flow naturally inside the block.

How do I align a button vertically in CSS?

Other than flexbox property, we can also center align the button horizontally and vertically using a set of CSS properties. Add position: relative to the container class and position: absolute to the class containing the button. Now use left:50% and top:50% to position the button to the center of the container.

What is vertical alignment in CSS?

CSS Demo: vertical-align The vertical-align property can be used in two contexts: To vertically align an inline element's box inside its containing line box. For example, it could be used to vertically position an image in a line of text. To vertically align the content of a cell in a table.


2 Answers

Since you are using inline-block, you need to use vertical-align as the default is baseline:

Magic CSS:

vertical-align: middle;

The above will fix it:

div,
button {
  width: 4em;
  height: 4em;
  background-color: red;
  padding: 0;
  border: 0;
  margin: 1em;
  font-size: 1em;
  font-family: Arial;
  vertical-align: middle;
}

div {
  text-align: center;
  display: inline-block;
  cursor: default;
  box-sizing: border-box;
}
<div>text</div>
<button>text</button>

And for the text inside the div to be centred, you need to use line-height to the height of the div.

Magic CSS:

line-height: 4em;

div,
button {
  width: 4em;
  height: 4em;
  background-color: red;
  padding: 0;
  border: 0;
  margin: 1em;
  font-size: 1em;
  font-family: Arial;
  vertical-align: middle;
  line-height: 4em;
}

div {
  text-align: center;
  display: inline-block;
  cursor: default;
  box-sizing: border-box;
}
<div>text</div>
<button>text</button>
like image 70
Praveen Kumar Purushothaman Avatar answered Oct 05 '22 12:10

Praveen Kumar Purushothaman


If you look at Chrome source code you can kind of see how it works, at least for Chrome. It seems there's an anonymous flex box created with a specific style applied. It's not that straightforward — at least not for me — but still, you can deduce what style is applied to this anonymous element. You can see here: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/layout/LayoutButton.cpp?sq=package:chromium

The interesting part:

void LayoutButton::updateAnonymousChildStyle(const LayoutObject& child,
                                             ComputedStyle& childStyle) const {
  ASSERT(!m_inner || &child == m_inner);

  childStyle.setFlexGrow(1.0f);
  // min-width: 0; is needed for correct shrinking.
  childStyle.setMinWidth(Length(0, Fixed));
  // Use margin:auto instead of align-items:center to get safe centering, i.e.
  // when the content overflows, treat it the same as align-items: flex-start.
  childStyle.setMarginTop(Length());
  childStyle.setMarginBottom(Length());
  childStyle.setFlexDirection(style()->flexDirection());
  childStyle.setJustifyContent(style()->justifyContent());
  childStyle.setFlexWrap(style()->flexWrap());
  // TODO (lajava): An anonymous box must not be used to resolve children's auto
  // values.
  childStyle.setAlignItems(style()->alignItems());
  childStyle.setAlignContent(style()->alignContent());
}

This gives something like this :

div span {
  display: flex;
  text-align: center; 
  min-width: 0px;
  flex-grow: 1;
  justify-content: center;
  cursor: default;
  margin: 0 auto;
  height: 100%;
  align-items: center;
  align-content: center;
}

Then you just need to wrap the div content in that span and apply the style. All these rules are probably not all necessary or accurate but the result seems ok:

div,
button {
  width: 4em;
  height: 4em;
  background-color: red;
  padding: 0;
  border: 0;
  margin: 1em;
  font-size: 1em;
  font-family: Arial;
  float: left;
}
div span {
  display: flex;
  text-align: center;
  min-width: 0px;
  flex-grow: 1;
  justify-content: center;
  cursor: default;
  margin: 0 auto;
  width: 100%;
  height: 100%;
  align-items: center;
  align-content: center;
}
<div><span>text text</span>
</div>
<button>text text</button>

<div><span>text text text text</span>
</div>
<button>text text text text</button>

<div><span>text text text text text text</span>
</div>
<button>text text text text text text</button>
like image 32
Julien Grégoire Avatar answered Oct 05 '22 10:10

Julien Grégoire