Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Text in Custom buttons randomly not centered vertically

Tags:

html

css

button

I have a form where I use several plus and minus buttons to add new elements to the form as needed by the user. I've used a custom button style. I've used this site to get the necessary css to make sure the text is centered. But this only seems to work in some instances of the button.

function unhide(id) {
  var myForm = document.forms.mrform;
  var myControls = myForm.elements[id.concat('[]')];
  for (var i = 0; i < myControls.length; i++) {
    var aControl = myControls[i];
    if ($(aControl).is(':hidden')) {
      $("#".concat(id.concat(i+1).replace(/ /g, "_"))).show();
	  break;
    }
  }
}

function hide(id) {
  var myForm = document.forms.mrform;
  var myControls = myForm.elements[id.concat('[]')];
  for (var i = myControls.length; i > 0 ; i--) {
    var aControl = myControls[i];
    if ($(aControl).is(':visible')) {
      $("#".concat(id.concat(i+1).replace(/ /g, "_"))).hide();
	  break;
    }
  }
}
.pmbutton {
  background-color: greenyellow;
  border: none;
  color: black;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 10px;
  height: 17px;
  width: 17px;
  padding: 0px;
  margin: 0px 0px 0px 4px;
  vertical-align: middle;
  line-height: 17px;
  box-sizing: border-box;
}

.hideme {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id='mrform' action='report_generator.php' method='post' autocomplete='off' onkeypress='return event.keyCode != 13;'>
<table class='subform'>
  <tr id='Objective_Item1'><td class='prompt text'>Objective Item #1 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'><button type='button' class='pmbutton' onclick='unhide("Objective Item")'>+</button><button type='button' class='pmbutton' onclick='hide("Objective Item")'>-</button></td></tr>
  <tr id='Objective_Item2' class='hideme'><td class='prompt text'>Objective Item #2 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item3' class='hideme'><td class='prompt text'>Objective Item #3 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item4' class='hideme'><td class='prompt text'>Objective Item #4 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item5' class='hideme'><td class='prompt text'>Objective Item #5 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item6' class='hideme'><td class='prompt text'>Objective Item #6 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item7' class='hideme'><td class='prompt text'>Objective Item #7 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item8' class='hideme'><td class='prompt text'>Objective Item #8 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item9' class='hideme'><td class='prompt text'>Objective Item #9 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
  <tr id='Objective_Item10' class='hideme'><td class='prompt text'>Objective Item #10 :</td><td class='input'><input class='inputbox text' type='text' name='Objective Item[]'></td></tr>
</table>
</form>

The functionality is fine, but the text is not vertically centered in the button in some instances and IS in others. Seems to be a single pixel off in random buttons. I've viewed it in Firefox and Chrome (which seems to be worse). Any suggestions as to the cause?

Edit: Adding an image of several renderings of the same button on one page of the site. The only property I change between some of them is the background color. Both vertical and horizontal alignment vary, it seems.

Button Renderings

Additional Button renderings

like image 918
Lee Blake Avatar asked Apr 07 '19 03:04

Lee Blake


People also ask

How do I align text vertically to the center of a button?

We set the display value of the buttons equal to inline-flex so that we can use the justify-content and align-items properties to center the content (particularly handy if you place an icon inside a button). We apply the same vertical padding, font-size, line-height, and border-width to buttons and inputs.

How to align div vertically in css?

Center Vertically - Using position & transform If padding and line-height are not options, another solution is to use positioning and the transform property: I am vertically and horizontally centered.


3 Answers

Before you read. I am not a font professional and I'm giving the answer based on my several hours research on the topic. I might be wrong in some places and I will gladly accept any improvement of the answer.

TL;DR

In my opinion, your safest approach would be to create images of + and and use them as buttons instead.


How fonts work

To answer your question you need to know how fonts work first.

Each character in the font is fitted into its own space container. In traditional metal type this container was the actual metal block of each character. The height of each character piece was uniform, allowing the characters to be set neatly into rows and blocks. This space is called "em-space" and it originates from the width of the uppercase M character. It was made so that the proportions of this letter would be square.

In digital type, em-space is defined like so:

  • In an OpenType font it is usually set at 1000 units.
  • In TrueType fonts it is by convention a power of two, generally set to 1024 or 2048 units.

When the font is used to set type, the em is scaled to the desired point size.

Each character in your font also has these elements:

  • Baseline is the line which defines the bottom of the character. With certain exceptions like g and q characters do not cross the baseline.
  • Cap height is the line which defines the top of a capital letter. Those are usually flat letters like H or I, whilst O or A might overshoot.
  • X-height is the height of a lower-case letter. Typically, this is the height of the letter x in the font, hence the name.
  • Ascender is the part of a lower-case letter that is taller than the font's x-height. Think of b and d characters.
  • Descender is the portion of a letter that extends below the baseline of a font. I already mentioned g and q.

All those elements are contained inside em-space.

Now, what happens when you try to scale the font down to 10px (based on your font-size: 10px; setting). Unless you changed your browser settings, the browser default font size is 16px (1 em = 2048 units for Times New Roman you seemingly use).

But Times New Roman uses 1825 ascent and 443 descent, which makes 2268 units in a 2048 unit em-square on Windows. Mac seems to use the HHead values, which are the same for Times New Roman. Here's the screenshot showing those values:

Times New Roman font info

What does it mean? It means that when you specify font-size: 10px the actual size of Times New Roman would be absolutely different. If I'm right with my calculations, it would be 11.07421875 rounded down to 11px (already an "ouch", right?)

line-height and vertical-align

Okay, next, when your button is rendered on screen, it can be composed of many lines, according to its width. Each line is made up of one or many inline elements (eg. span elements with different font settings) and is called a line-box. The height of a line-box is based on its children’s height. The browser computes the height for each inline element and chooses the tallest child as the "template" for the line-box. This ensures you see all the characters on your screen when the element is rendered.

This behavior differs from Word or Photoshop. In those programs, line-height is not based on line-box. Instead it is the distance between baselines. But in CSS it is not. In css your line-height is your line-box.

It should be noted, that CSS does not specify default value of line-height. Default value is normal:

Tells user agents to set the used value to a "reasonable" value based on the font of the element. The value has the same meaning as . We recommend a used value for 'normal' between 1.0 to 1.2. The computed value is 'normal'.

And this is the specs for vertical-align:

This property affects the vertical positioning inside a line box of the boxes generated by an inline-level element.

From all the information above we can conclude, that it's the content-area being centered vertically, not the character purely.

Indeed

I've checked + and - in font editing software and it looks like this:

Plus and Hyphen in Times New Roman

While + seems symmetrical, it's still lowered a bit inside it's content area; and - is lowered down towards baseline. So the answer for - is obvious. Based on everything I've researched and posted above - you will not be able to center it vertically, it will always be slightly lower. On small font sizes it might be a pixel or two; on huge font size you will see it right away.

Note, that the - you are used to typing (top right key on num pad; or key right of 0) is not minus, it's hyphen. Minus and hyphen looks differently: vs. -.

Would − (minus) look differently? It surely has more symmetry to it:

Plus and Minus in Times New Roman

Here you can see more clearly, that + and − (minus) are closer to the base line.

Flexboxes as possible solution?

Let's take a look at flexbox. When you define display: flex, a box is generated called flex container. In-flow children of a flex container are called flex items and are laid out inside flex lines.

Unlike block and inline layout, whose layout calculations are biased to the block and inline flow directions, flex layout is biased to the flex directions.

flex container and flex items

Now, flex lines, apart from line-boxes, work differently:

Once content is broken into lines, each line is laid out independently; flexible lengths and the justify-content and align-self properties only consider the items on a single line at a time.

In a multi-line flex container (even one with only a single line), the cross size of each line is the minimum size necessary to contain the flex items on the line (after alignment due to align-self), and the lines are aligned within the flex container with the align-content property. In a single-line flex container, the cross size of the line is the cross size of the flex container, and align-content has no effect. The main size of a line is always the same as the main size of the flex container’s content box.

So, when you define align-items: center,

The flex item’s margin box is centered in the cross axis within the line.

And finally, if I understood the specification correctly, when you specify align-items: center, the baseline is generated dynamically, allowing you to actually perfectly center the + and .

In conclusion

You should use flexboxes and use div, not a button, as button seems to behave differently (I'd welcome a documented suggestion why). Even though if you specify huge numbers (like width and height to 400px and font-size to 750px) you will see slight shifting towards the bottom. But on small sizes everything seems to work perfectly.

In my opinion, your safest approach would be to create images of + and and use them as buttons instead.


Sources I researched:

  • http://designwithfontforge.com/en-US/The_EM_Square.html
  • https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align
  • https://en.wikipedia.org/wiki/Cap_height
  • https://en.wikipedia.org/wiki/X-height
  • https://en.wikipedia.org/wiki/Ascender_(typography)
  • https://en.wikipedia.org/wiki/Descender
  • https://drafts.csswg.org/css2/visudet.html#propdef-line-height
  • https://drafts.csswg.org/css2/visudet.html#propdef-vertical-align
  • https://www.w3.org/TR/css-flexbox-1/#flex-line
  • https://www.w3.org/TR/css-flexbox-1/#flex-item
  • https://www.w3.org/TR/css-flexbox-1/#align-items-property
  • https://www.w3.org/TR/css-flexbox-1/#flex-baselines

App to show the font info - https://fontforge.github.io/en-US/

like image 78
Alex Karshin Avatar answered Oct 20 '22 07:10

Alex Karshin


I believe this may be because of the button element itself:

When placing the + inside a seperate span, and aligning the span perfectly in the center,
by using flexboxes, the +is still not exactly in the center. When I do the same with a div element,
the + is exactly centered.

Check this fiddle to see what I mean.
Therefore I recommend using a div with a click event instead of a button.

Also, if you want have a minus sign which is vertically aligned at the same height as the plus,
you should use &minus; instead of - inside your element.

like image 27
Ignace Vau Avatar answered Oct 20 '22 06:10

Ignace Vau


You need specify the font-family in .pmbutton,

you can choose a widely using web safe font or you can declare your own font-face

.pmbutton {
   font-family:arial;
}
like image 29
Sajan Avatar answered Oct 20 '22 07:10

Sajan