tobbe tobbe - 3 months ago 10
CSS Question

Vertically align characters inside input

How do I vertically align the characters/text inside an input without changing the height of the input (it has to be exactly 28px)? The input has this CSS, so I don't understand why it has some padding-top (?):



input {
font-family: arial;
font-size: 28px;
line-height: 28px;
height: 28px;
padding: 0;
border: none;
outline: none;
background-color:#cdcdcd;
}

<input value="asdg">





Some letters like g, p and q get cut off



Removing the margin-bottom doesn't help.

https://jsfiddle.net/4rtL6415/

Answer

The Problem

In some fonts, characters with descenders, like g, p, q, and y, "overflow" the vertical space defined by the font-size property. Normally, that's not a problem, because the line-height property provides enough extra space to accommodate the descenders. However, if the characters are placed in a container element with a fixed height that's less than the line-height, the descenders may get clipped if that's how the container handles overflow (text inputs being one example of such).

If you were hoping to bump the text up a few notches to avoid the clipping, then you'll be disappointed to know that there is currently no way to reposition text within its own line-height. (vertical-align, in case you were wondering, positions an inline element relative to its parent.) However, there are a few CSS tricks that we can use to achieve the same visual effect...

Solution 1 (Webkit only)

This one works by giving the input a large enough height to fit the font's lower extremities, and then using clip-path to trim it back down to 28px. This is probably the most elegant solution, but unfortunately, clip-path isn't well supported outside of Webkit browsers (Chrome, Safari, Opera).

input {
  display: inline-block;
  padding: 0;
  border: none;
  height: 32px;
  font-size: 28px;
  line-height: 32px;
  font-family: arial;
  background: #cdcdcd;
  vertical-align: baseline;
  -webkit-clip-path: inset(4px 0px 0px 0px);
  clip-path: inset(4px 0px 0px 0px);
}
input: <input value="asdg">

Solution 2

This one was inspired by DebRaj's answer, but mine uses an inline-block wrapper instead of a block (not sure how you would use it otherwise). Like the previous solution, it increases the height of the input, but instead of using clip-path to trim it back down, it uses a container element with overflow: hidden;. This is probably the most practical approach until support for clip-path improves.

.text {
  display: inline-block;
  vertical-align: baseline;
  overflow: hidden;
  margin: 7px 0 -7px 0;
  height: 28px;
}
.text > input {
  margin-top: -4px;
  border: none;
  padding: 0;
  background: #cdcdcd;
  font-size: 28px;
  line-height: 32px;
  font-family: arial;
}
input:<span class="text"><input value="asdg"></span>

Solution 3

Although you can't reposition text within its own line-height, this may be the next best thing. If you set the line-height to something less than the font-size, the text will indeed move upward relative to its normal baseline. That means you can bring the clipped parts into view without changing the container height. Unfortunately, if you try this with a text input, you'll discover a strange quirk: line-height is completely ignored if it's less than the input's height. So we'll have to substitute a different element, and turn it into an editable textbox somehow. That can be accomplished with the contenteditable attribute.

.fauxTextInput {
  display: inline-block;
  vertical-align: baseline;
  margin: 6px 0 -6px 0;
  padding: 0 3px 0 3px;
  width: 9em;
  height: 28px;
  overflow: hidden;
  font-size: 28px;
  line-height: 23px;
  font-family: arial;
  background: #cdcdcd;
}
Faux input: <span class="fauxTextInput" contenteditable>asdg</span>

Comments