This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

Updating to a touch-friendly UI

Description

I was recently redesigning a web page and wanted to make it easier to use from touch-screen browsers. Links made only of text are relatively easy to click with a mouse, but tend to make poor touch targets. If the layout has enough space around the link, this can be remedied by applying CSS. <h>The basic box</h> <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%"><a href="#" style="padding: 0px 20px; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0px 20px; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0px 20px; color: yellowgreen; background-color: #8F8F8F">Third</a></div> Suppose we have a box with three links in it, as shown to the right. <h>Setting the height</h> The first step is to make this box taller, so the logical thing to do is to set the height. We'll have to pick a value, so set <c>height: 40px</c> on the gray box. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%; height: 40px"><a href="#" style="padding: 0px 20px; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0px 20px; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0px 20px; color: yellowgreen; background-color: #8F8F8F">Third</a></div> <h>Aligning vertically</h> This isn't exactly what we want, though; we'd rather have the vertical space equally distributed. Also, if you hover over the links, you can see that the space below the text is not active. Maybe we can try to add <c>vertical-align: middle</c> to align the content. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%; height: 40px; vertical-align: middle"><a href="#" style="padding: 0px 20px; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0px 20px; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0px 20px; color: yellowgreen; background-color: #8F8F8F">Third</a></div> Unfortunately, this doesn't have the desired effect. The <c>vertical-align</c> property works when used this way in table cells, but otherwise has no effect for block elements. Knowing that, we can set <c>display: table-cell</c> for the gray box. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%; height: 40px; vertical-align: middle; display: table-cell"><a href="#" style="padding: 0px 20px; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0px 20px; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0px 20px; color: yellowgreen; background-color: #8F8F8F">Third</a></div> And now the box has become longer, because the 50% width of the box is calculated differently for table cells than for regular boxes (especially when a table cell is found outside of a table). <h>Relative positioning</h> Let's abandon the vertical-alignment approach and try using positioning instead. Set <c>position: relative</c> and <c>top: 25%</c> to center the links vertically. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%; height: 40px"><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: yellowgreen; background-color: #8F8F8F">Third</a></div> Now that looks much better, but the space above and below the links is still not active. Perhaps we can use the height trick again, to make the individual links taller as well. So we set <c>height: 100%</c> on each of the links. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%; height: 40px"><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: goldenrod; background-color: #8F8F8F; height: 100%">First</a><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: gold; background-color: #8F8F8F; height: 100%">Second</a><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: yellowgreen; background-color: #8F8F8F; height: 100%">Third</a></div> We didn't get the expected result, but we should have expected that: the links are <c>inline</c> elements and can only have a height set if we set <c>display: inline-block</c> on each link as well. We use <c>inline-block</c> rather than <c>block</c> so that the links stay on the same line. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; width: 50%; height: 40px"><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: goldenrod; background-color: #8F8F8F; height: 100%; display: inline-block">First</a><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: gold; background-color: #8F8F8F; height: 100%; display: inline-block">Second</a><a href="#" style="padding: 0px 20px; position: relative; top: 25%; color: yellowgreen; background-color: #8F8F8F; height: 100%; display: inline-block">Third</a></div> The links are now the right size, but they stick out below the gray box, which isn't what we wanted at all. We're kind of out of ideas with this approach, but there is another way we can get the desired effect. <h>Positive padding and negative margins</h> Let's start with the original gray box and, instead of choosing a random height as we did above---<c>40px</c>---let's set <c>padding: 8px</c> on the gray box to make room above and below the links. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; padding: 8px 0; width: 50%"><a href="#" style="padding: 0px 20px; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0px 20px; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0px 20px; color: yellowgreen; background-color: #8F8F8F">Third</a></div> With just one CSS style, we've already got the links nicely aligned and, as an added benefit, this technique scales even if the font size is changed. The 8-pixel padding is preserved regardless of how large the font gets.<fn> <div style="font-size: 14pt; background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; padding: 8px 0; width: 50%"><a href="#" style="padding: 0 20px; color: goldenrod; background-color: #8F8F8F">First</a><a href="#" style="padding: 0 20px; color: gold; background-color: #8F8F8F">Second</a><a href="#" style="padding: 0 20px; color: yellowgreen; background-color: #8F8F8F">Third</a></div> This approach seems promising, but the links are still not tall enough. The naive approach of setting <c>height: 100%</c> on the links probably won't work as expected, but let's try it anyway. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; padding: 8px 0; width: 50%"><a href="#" style="padding: 0 20px; color: goldenrod; background-color: #8F8F8F; height: 100%">First</a><a href="#" style="padding: 0 20px; color: gold; background-color: #8F8F8F; height: 100%">Second</a><a href="#" style="padding: 0 20px; color: yellowgreen; background-color: #8F8F8F; height: 100%">Third</a></div> It looks like the links were already 100% of the height of the container; in hindsight it's obvious, since the height of the gray box is determined by the height of the links. The 100% height refers to the client area of the gray box, which doesn't include the padding. We'd actually like the links to have padding above and below just as the gray box has. As we saw above, the links will only honor the padding if they also have <c>display: inline-block</c>, so let's set that in addition to <c>padding: 8px</c>. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; padding: 8px 0; width: 50%"><a href="#" style="padding: 0 20px; color: goldenrod; background-color: #8F8F8F; padding-top: 8px; padding-bottom: 8px; display: inline-block">First</a><a href="#" style="padding: 0 20px; color: gold; background-color: #8F8F8F; padding-top: 8px; padding-bottom: 8px; display: inline-block">Second</a><a href="#" style="padding: 0 20px; color: yellowgreen; background-color: #8F8F8F; padding-top: 8px; padding-bottom: 8px; display: inline-block">Third</a></div> We're almost there. The only thing remaining is to make the vertical padding of the links overlap with the vertical padding of the gray box. We can do this by using a <i>negative</i> vertical margin, setting <c>margin: -8px</c>. <div style="background-color: gray; border: 1px solid black; border-width: 1px 0; text-align: center; padding: 8px 0; width: 50%"><a href="#" style="padding: 0 20px; color: goldenrod; background-color: #8F8F8F; padding: 8px 20px; display: inline-block; margin: -8px 0">First</a><a href="#" style="color: gold; background-color: #8F8F8F; padding: 8px 20px; display: inline-block; margin: -8px 0">Second</a><a href="#" style="color: yellowgreen; background-color: #8F8F8F; padding: 8px 20px; display: inline-block; margin: -8px 0">Third</a></div> We finally have the result we wanted. The links are now large enough for the average finger to strike without trying too hard. Welcome to the CSS-enabled touch-friendly world of web design. The code for the final example is shown below, with the sizing/positioning styles highlighted: <code> .gray-box { background-color: gray; border: 1px solid black; border-width: 1px 0; width: 50%; text-align: center; <hl>padding: 8px</hl> 0; } .gray-box a { background-color: #8F8F8F; <hl>display: inline-block; padding: 8px</hl> 20px; <hl>margin: -8px</hl> 0; } </code> <hr> <ft>Naturally, we could also use <c>.8em</c> instead and then the padding will scale with the font size. This would work just as well with the height. Let's pretend that we're working with a specification that requires an 8-pixel padding instead of a flexible one.</ft>