The <button> is a very special element. From a CSS perspective it comes with many default styles compared to a lot of other elements. In addition to that it comes with at least one really strange behaviour. I’m talking about the vertical alignment of content inside the button.

If you have a <button> with fixed dimensions and have any content inside it, it will be vertically and horizontally centered. For a second that sounds awesome, because (vertical) centering can be a pain in CSS sometimes - though it got a lot better over the past years and often that’s what you want in a button anyways, right?

The horizontal center is because of the default text-align: center and can be reset easily. But as far as I know this default behaviour can not be traced back to a CSS property which in turn means, you can not reset it if you need exact control over how the content inside the <button> is positioned.

The other day I was creating a <button> as one does if the user interaction should not go to a url and I needed to precisely position something inside when I was reminded of this issue. So the goal for me was to get the same baseline in all browsers, which means position the content inside in the top left corner and then go from there.

The solution is relatively simple but it’s annoying that we need to use it.

Step 1

Set text-align: left.

button {
	width: 50px;
	height: 50px;
	text-align: left;
}

Step 2

Remove the default border either with border: none or a new one like border: 1px solid black. The default <button> border not only creates a border, it also creates some sort of padding inside even though you set padding: 0;, which we will also do.

button {
	width: 50px;
	height: 50px;
	text-align: left;
	border: none;
	padding: 0;
}

Step 3

Add a helper element as the first item of the <button>. Use a <span> and have it fill the whole button with display: block, width: 100%; and height: 100%;.

<button>
	<span>
		👋
	</span>
</button>
button {
	width: 50px;
	height: 50px;
	text-align: left;
	border: none;
	padding: 0;
}

button > span {
	width: 100%;
	height: 100%;
}

This way the content inside the <span> will be in the upper left corner and we can go from here we whatever we want to do.

I made a CodePenso you can see it in action.

About Safari & Flexbox

In the past the same trick was necessary when you wanted to use Flexbox directly on a <button> element. Safari ignored it, but if you added the helper <span> and made it cover the whole button, you could use display: flex; on the <span> instead and you were good to go.

It seems like Safari 12 fixed this issue. It’s still good to know if you want to be somewhat downwards compatible.