0

I'm trying to create an element with the button look. I've created a .button class to apply the effect. I've managed to mimic the basic styles (borders, background gradient, hover and active effect). The problem is the vertical alignment of the button text if the button has a height.

If I don't specify a height on the element, there's no need for vertical aligment, and I get the desired result: JSFiddle.

However, if I specify a height on the element, the element's size (height) increases and the text stays at the top: JSFiddle.

To vertically align the text to center/middle, I add a span inside the button element and modify the span.

.button span {
    width: 100%;
    height: 100%;
    display: table-cell;
    vertical-align: middle;
}

But this doesn't work. Even though the parent .button has width and height specified, span won't get these values: JSFiddle.

To get around this problem, I set the width and height on the span to inherit. This somewhat solves the problem, but causes another: JSFiddle.

The inherited width is the remaing space, because box-sizing: border-box; applied on the parent .button . In other words, width = 100px - border - padding 86px. But the inherited height is not the remaing height, but the actual height applied to the parent 50px. This is weird and inconsistent. The text seems to vertically aligned, but it's slightly off. Also the span overflows the .button.

I could remove the box-sizing: border-box; on the .button to get around this problem, and get a perfectly aligned button text and a nice button, but the padding and border will be added to the width and height I specify. If I want a 100x50 pixels button, I'll get 100+border+padding x 50+border+padding pixels button, but a vertically aligned one: JSFiddle.

Is there any other way to make this work?

.button {
  overflow: hidden;
  white-space: nowrap;
  display: inline-block;
  vertical-align: top;
  -webkit-user-select: none;
  font-family: Arial;
  font-size: 13px;
  color: black;
  text-decoration: none;
  text-align: center;
  cursor: default;
  padding: 2px 6px;
  margin: 0;
  /* box-sizing: border-box; */
  border-radius: 2px;
  border-top: 1px solid rgb(166,166,166);
  border-bottom: 1px solid rgb(148,148,148);
  border-left: 1px solid rgb(157,157,157);
  border-right: 1px solid rgb(157,157,157);
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAAAAACFNooQAAAAHUlEQVQI12P4wfSfiYHpP9N/KP0Pyv6Pxobw4CoBqcYYFT1E4a0AAAAASUVORK5CYII=);
  background-size: contain;
}
.button:hover {
  border-top: 1px solid rgb(123,123,123);
  border-bottom: 1px solid rgb(110,110,110);
  border-left: 1px solid rgb(116,116,116);
  border-right: 1px solid rgb(116,116,116);
}
.button:active {
  border-top: 1px solid rgb(166,166,166);
  border-bottom: 1px solid rgb(148,148,148);
  border-left: 1px solid rgb(157,157,157);
  border-right: 1px solid rgb(157,157,157);
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAAAAACFNooQAAAAHUlEQVQI12O4y8TIxMDEyMQIpZmgbAhkQmPDVQIAOFkBLhpTdQIAAAAASUVORK5CYII=);
}
.button span {
  width: inherit;
  height: inherit;
  display: table-cell;
  vertical-align: middle;
}

body > * {
  width: 100px;
  height: 50px;
}
<input type="button" style="width: 150px; height: 50px" value="input">

<a href="#" class="button">anchor</a>

<a href="#" class="button"><span>a > span</span></a>

<p class="button">paragraph</p>

Update: I forgot to mention. I can't set line-height on the span. If I have multiple buttons with variable heights, I need to apply different line-heights on all of them.

I'm looking for a more general solution which would make an element work exactly like a button (width/height specified or not).


I wanna break the upvote button on this.

Community
  • 1
  • 1
akinuri
  • 10,690
  • 10
  • 65
  • 102

2 Answers2

1

You can achieve this with flexbox.

  display: inline-flex;
  justify-content: center;
  align-items: center;

jsFiddle


CODE SNIPPET:

.button {
  overflow: hidden;
  white-space: nowrap;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  -webkit-user-select: none;
  font-family: Arial;
  font-size: 13px;
  color: black;
  text-decoration: none;
  cursor: default;
  padding: 2px 6px;
  margin: 0;
  box-sizing: border-box;
  border-radius: 2px;
  border-top: 1px solid rgb(166, 166, 166);
  border-bottom: 1px solid rgb(148, 148, 148);
  border-left: 1px solid rgb(157, 157, 157);
  border-right: 1px solid rgb(157, 157, 157);
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAAAAACFNooQAAAAHUlEQVQI12P4wfSfiYHpP9N/KP0Pyv6Pxobw4CoBqcYYFT1E4a0AAAAASUVORK5CYII=);
  background-size: contain;
}
.button:hover {
  border-top: 1px solid rgb(123, 123, 123);
  border-bottom: 1px solid rgb(110, 110, 110);
  border-left: 1px solid rgb(116, 116, 116);
  border-right: 1px solid rgb(116, 116, 116);
}
.button:active {
  border-top: 1px solid rgb(166, 166, 166);
  border-bottom: 1px solid rgb(148, 148, 148);
  border-left: 1px solid rgb(157, 157, 157);
  border-right: 1px solid rgb(157, 157, 157);
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAAAAACFNooQAAAAHUlEQVQI12O4y8TIxMDEyMQIpZmgbAhkQmPDVQIAOFkBLhpTdQIAAAAASUVORK5CYII=);
}
body > * {
  width: 100px;
  height: 50px;
}
<input type="button" value="input">

<a href="#" class="button">anchor</a>

<p class="button">paragraph</p>

EDIT:

Semantically speaking, I would rather use a data-attribute to give extra information about the behavior change in my HTML element.

Here's an example using a level four selector to allow case permutation in your attribute's value: (You can remove the i from the selector since it may not be supported in all modern browsers.)


DEMO


[data-type="button" i] {
  overflow: hidden;
  white-space: nowrap;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  -webkit-user-select: none;
  font-family: Arial;
  font-size: 13px;
  color: black;
  text-decoration: none;
  cursor: default;
  padding: 2px 6px;
  margin: 0;
  box-sizing: border-box;
  border-radius: 2px;
  border-top: 1px solid rgb(166, 166, 166);
  border-bottom: 1px solid rgb(148, 148, 148);
  border-left: 1px solid rgb(157, 157, 157);
  border-right: 1px solid rgb(157, 157, 157);
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAAAAACFNooQAAAAHUlEQVQI12P4wfSfiYHpP9N/KP0Pyv6Pxobw4CoBqcYYFT1E4a0AAAAASUVORK5CYII=);
  background-size: contain;
}
[data-type="button" i]:hover {
  border-top: 1px solid rgb(123, 123, 123);
  border-bottom: 1px solid rgb(110, 110, 110);
  border-left: 1px solid rgb(116, 116, 116);
  border-right: 1px solid rgb(116, 116, 116);
}
[data-type="button" i]:active {
  border-top: 1px solid rgb(166, 166, 166);
  border-bottom: 1px solid rgb(148, 148, 148);
  border-left: 1px solid rgb(157, 157, 157);
  border-right: 1px solid rgb(157, 157, 157);
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAAAAACFNooQAAAAHUlEQVQI12O4y8TIxMDEyMQIpZmgbAhkQmPDVQIAOFkBLhpTdQIAAAAASUVORK5CYII=);
}
body > * {
  width: 100px;
  height: 50px;
}
<input type="button" value="input">

<a href="#" data-type="button">anchor</a>

<p data-type="BuTToN">paragraph</p>
Ricky Ruiz
  • 25,455
  • 6
  • 44
  • 53
0

Since it looks like you're using a constant font-size, you can do this with a little calc magic and absolute positioning:

.button span {
  display:block;
  left:0;
  right:0;
  position:absolute;
  top:calc(50% - 9px);
  text-align:center;
}

Example with three different button heights: https://jsfiddle.net/tLhr3ju3/1/

ryantdecker
  • 1,754
  • 13
  • 14
  • In order for this to work, button must always have a width (that is wider than the child). This is not always the case. It should work whether I set width or not. – akinuri Aug 21 '16 at 20:34
  • I assumed that was expected, because you are setting `overflow: hidden;` and `white-space: nowrap;` on your button... You're right - the calculation would have to change if the text wraps, but you were already explicitly preventing that. – ryantdecker Aug 21 '16 at 20:38
  • I'm basing the styles on how an input button behaves. See [jsfiddle](https://jsfiddle.net/o5tvjmmc/). – akinuri Aug 21 '16 at 20:42
  • Maybe i'm missing something, but it sounds like you have some criteria you aren't explaining...why are you including those styles in your examples if you don't expect them to affect the answer? – ryantdecker Aug 21 '16 at 20:49
  • I'm sorry if my question was not clear. I want the element to look and behave like an input button. This should be clear enough. I can't list every characteristics of a input button in the question. This should be common knowledge. Every style I've added plays a role in this button effect. Font-size, paddings, etc. are not fixed values. They can change as you can chance a button's style. – akinuri Aug 21 '16 at 21:05