Icons and Type
As of late, I’ve been thinking about icons and type.
Here’s the example HTML I’m working with here:
<div><i class="icon"></i>cases</div>
Now, that div
could be a button
, it could be anything. That i
could be an image, it could be an svg. Essentially, it’s something that is icon-like, set to display: inline-block
. My goal is to center it next to the text beside it.
Let’s take a look at this first example:
Here’s the CSS I’m using to position the icon next to the text:
.icon {
width: 1ex;
height: 1ex;
display: inline-block;
background-color: salmon;
vertical-align: middle;
}
The key part here is vertical-align: middle
. Instead of resting the icon on the baseline, as it would be default, I’m centering the image with the text.
The reason for this becomes evident when I go with a larger image:
Awesome.
So, what’s the problem?
Of course, rarely is copy conveniently all lowercase. Usually it’s title case and full of ascenders, sometimes it’s all caps.
Not so awesome.
In this case, aligning the icon to the middle isn’t quite what I want. None of the usual vertical-align
values do the trick, though. (Except in cases where we get lucky and the image height just happens to be at the right size to use text-bottom, baseline, or text-top.)
Thankfully, I can adjust the alignment manually.
.icon {
vertical-align: -.25ex;
}
Ex Unit
In researching this, I came across the ex unit. I may have already known about it but usually when I want to size relative to type, I’ll use the em or rem unit. The ex unit can be useful because it’s based on the x-height—the height of the letter X.
I decided to use the ex unit for sizing the icon. I like that the icon size is relative to the type. I can change the font size or change the font family and the icon will match the x-height. (Which, with the first example of lowercase type works really well.)
Sadly, most icons at smaller resolutions will look blurry if not aligned to their designed pixel dimensions. Using ex units for icon sizing probably isn’t all that practical. (For example, Arial versus Helvetica will render the icon at different sizes.)
But I digress.
Back to Px
Using pixel units for the icon, none of vertical-align options work “out of the box”. I’ll go go back to vertical-align: middle
to, at least, align to the middle of the type, regardless of font size.
.icon {
width: 16px;
height: 16px;
display: inline-block;
background-color: salmon;
vertical-align: middle;
}
I need one more piece. The icon is currently aligned to the middle of the text including ascenders and descenders (or aligned to the middle of the x-height, I didn’t actually confirm which but close enough).
To align to the middle of the x-height plus the height of the ascenders, I need to move the icon up.
Using Translate
With one extra line of code*, I used em units to tweak the alignment. Increasing or decreasing the font size will keep the icon centered due to the em-based translate.
.icon {
width: 16px;
height: 16px;
display: inline-block;
background-color: salmon;
vertical-align: middle;
transform: translateY(-.1em);
}
Sweet, everything aligns!
* One line? I still have browser prefixes to deal with for now, so not quite one line…
Using Top
If we don’t want to deal with browser prefixes, we can also use position:relative
and top
to adjust accordingly.
.icon {
width: 16px;
height: 16px;
display: inline-block;
background-color: salmon;
vertical-align: middle;
position:relative;
top:-.1em;
}
Recap
Using ex units to size and position the icon offers lots of flexibility but will likely result in blurry icons at smaller sizes. (SVGs or hi-res images on higher res displays may make this more of a moot point in the future.)
Using px units to size the icon and then using em units to adjust the position of the icon results in a more predictable situation (at least, at default browser settings for font size).
Conversation
Thanks for this, Jonathan. I totally like the idea about aligning the icon based on the x-height of the given font. One question though. What do you mean with"... more of a moot point in the future."? Do you just refer to your project? I just don't get the "in the future" part ;) Why not using it now?
@Verpixelt: Because not everybody has high density displays right now, therefore leading to blurring. If, in the future, everybody has 2x or 3x displays, then icons can be rendered to look nice regardless.
I usually use margin-top: -.2em to achieve this effect. This can lead to problems?
Since the icon has its display set to inline-block, we can also use `margin-top: -0.1em` to achieve the same effect, right?
Incidentally, does using transform have any impact on performance?
margin-top
can work, too. Transform can create a new positioning context which *may* be problematic.Why not use display: flex; and align-items: center; ?
Why not just use display:table; with display table-cell; and vertical-align:middle; ?
Here is a code pen example of what I am talking about.
http://codepen.io/anon/pen/Wvvaed
I usally use
line-height:0
or sometimes1px
. It makes it easier to control the vertically centering, I think.I use always following solution when I know icon size.
Pros:
1. Less DOM elements
2. You can modify line-height, font-family, font-size but icon is always perfectly aligned.
div{
position: relative;
padding-left: 20px;
}
div:after{
content: "";
position:absolute;
left: 2px; /*2px gaps between div left edge and icon, icon and text*/
top: 50%;
width: 16px;
height: 16px;
margin-top: -8px;
}
> The ex unit can be useful because it’s based on the x-height—the height of the letter X.
Just to be more exact. ex unit is the height of the lowercase "x".
I usually use
verical-align: -1.em
: possible because ofdisplay: inline-block
. It's a little more cross browser than transforms and, as far as I know, doesn't create a new context.I believe the offset could also be generalized as
whoops, bad formatting. this:
When using:
How does the -0.1em hold up over a range of possible text sizes?
I usually give height and line-height the same value to achieve the same result.
div {height: 16px;line-height: 16px;}
Percentages can also be used.
vertical-align: 50%
+1 for using line-height, always fixes it for me.
Huh, I did not know that vertical-align took a percentage (of line-height) or a length (both relative to the parent baseline).
Nice.
The problem surrounds your choice of markup. Unless you are deliberately using the bare minimum; creating what seems (to me) to be an edge/use case. Personally I would either use a pseudo element and wrap a span around the text. Then set both to inline-block and apply vertical align[whatever] to both the span and the pseudo element. Why not do that instead? Or does this have to work only the specific situation you have demonstrated?
Life saver, works for me with fontello.
@Morgan Feeney: That works if the text next to the icon doesn't need to wrap. While minor, it also means creating an extra class along with the extra HTML. Byte for byte, I don't think you end up ahead.
I often use tricks like top, bottom or line-height to fix this stuff. Now I see the heaven. Thanks alot J