There are some major differences to designing navigation for an app, versus navigation for desktop or even responsive layouts. Mobile navigation is often tight and compact. Four anchors in a mobile nav is a lot. And 6 is basically too much. Even with icons only, five anchors can feel cramped on most screens still. There's also not a need to hide most navigation from the user under a hamburger or shishkabob menu, because again, if you have too much navigation, you're doing it wrong anyways.
Desktop navigation has tons of room for navigation and websites often have crazy complicated sitemaps. But a mobile app needs to simplify down, not just because of a lack of space, but because thumbs can't click too accurately anyways. You need to space things out, even as you compact them together. It's a delicate balancing act, that is often handled by stretching your navigation out over page jumps.
Before we get into any actual navigation, Let's just define up a quick mobile layout for us to use and build upon. This will help us visualize our concept better, and maybe we can have a relative page structure that makes sense and is industry appropriate.
<div data-role="page">
<div data-role="header"><h1>Header</h1></div>
<div data-role="main">Body</div>
<div data-role="footer">Footer</div>
</div>
[data-role="page"] {
display:flex;
flex-direction:column;
--header-height:3rem;
overflow:hidden;
position:relative;
}
[data-role="header"],
[data-role="footer"] {
height:var(--header-height);
line-height:var(--header-height);
position:relative;
z-index:2;
}
[data-role="page"]>[data-role="header"],
[data-role="page"]>[data-role="footer"] {
box-shadow: 0 0 5px rgba(0,0,0,0.2);
background-color:white;
}
[data-role="main"] {
padding:1em;
overflow:hidden;
position:relative;
z-index:1;
}
[data-role="header"] h1 {
color:#666;
margin: 0;
font-size: 1em;
}
[data-role="header"]>h1 {
position: absolute;
top:0;
left:0;
width: 100%;
text-align: center;
}
Cool. I hate it. But it will get better as we add some content to it. Some things we're doing here are worth mentioning. If we build our headers and footer appropriately, they should probably never have overflow and should be a single line. This lets us use the line-height trick to center their content. Use a css variable for the header height, so that you can reuse it whenever you need to define something by that size.
By using flexbox we get a layout that will fill our space, and by using relative positioning and some z-index, we place our header and footer in front of our main, and make sure that its content goes behind if necessary, and allows us to put a drop shadow on the header/footer.
If our h1
is an immediate child of the header, then let's pull it out of flow, and center it's content. This gives us an always perfect background heading, that isn't touchable but also isn't affected by the flow of anything around it.
If we bring in the Flex Classes, and just use text, designing a core mobile navigation is extremely simple. Flex-child can be used to space out links on the header, and each link in the footer can be a flex-child to evenly space them out.
<ul class="flex-parent compact">
<li class="flex-none"><a href="#">Link</a></li>
<li class="flex-child"></li>
<li class="flex-none"><a href="#">Link</a></li>
</ul>
<div data-role="main">Body</div>
<div data-role="footer">
<ul class="flex-parent compact text-center">
<li class="flex-child"><a href="#">Link</a></li>
<li class="flex-child"><a href="#">Link</a></li>
<li class="flex-child"><a href="#">Link</a></li>
</ul>
</div>
Using the flex-parent
and compact
classes can give us quick access to simple layout, but if we're going to be making enough of these, let's add on to our Nav Classes and do a new nav-mobile
class.
<div data-role="page">
<div data-role="header" class="nav-mobile">
<h1>Header</h1>
<ul class="flex-parent compact">
<li class="flex-none"><a href="#">Link</a></li>
<li class="flex-child"></li>
<li class="flex-none"><a href="#">Link</a></li>
</ul>
</div>
<div data-role="main">Body</div>
<div data-role="footer" class="nav-mobile">
<ul class="flex-parent compact text-center">
<li class="flex-child"><a href="#">Link</a></li>
<li class="flex-child"><a href="#">Link</a></li>
<li class="flex-child"><a href="#">Link</a></li>
</ul>
</div>
</div>
.nav-mobile ul {
list-style-type:none;
padding-left:0;
margin-top:0;
margin-bottom:0;
display:flex;
position:relative;
}
.nav-mobile li>a,
.nav-mobile li>span {
padding:0 0.5rem;
color:inherit;
display:block;
}
.nav-mobile li:first-child>h1,
.nav-mobile li:first-child>a,
.nav-mobile li:first-child>span {
padding-left: 1rem;
}
.nav-mobile li:last-child>h1,
.nav-mobile li:last-child>a,
.nav-mobile li:last-child>span {
padding-right: 1rem;
}
.nav-mobile li>a:hover {
text-decoration:none;
background-color:#eee;
}
Something to be very aware of when designing mobile concepts, is that there is no hover on mobile. Your finger can't hover over a link, and although I've designed a hover effect on these links, that actually most likely will only be experienced as a sort of shadow on a clicked link in a real mobile environment.
We offset the position absolute we placed on the h1 with a position relative on the ul next to it. This will ensure that that two items are placed in the normal linear order and make sure our navigation is in front of the heading text. At this point, just design your mobile nav anchors so they have some natural spacing from each other, and that's about it.
With just the flex classes, we can design a number of common patterns and variations.
<h1>Header</h1>
<ul>
<li class="flex-child"><!-- Just for Spacing --></li>
<li class="flex-none"><a href="#">Link</a></li>
</ul>
<h1>Header</h1>
<ul>
<li class="flex-none"><a href="#">Link</a></li>
</ul>
<ul>
<li class="flex-none"><a href="#">Link</a></li>
<li class="flex-child"><h1>Heading</h1></li>
<li class="flex-none"><a href="#">Link</a></li>
</ul>
Using our different text alignment classes can give us variation in position of header or even span text.
<ul>
<li class="flex-none"><a href="#">Link</a></li>
<li class="flex-none"><a href="#">Link</a></li>
<li class="flex-child text-center"><span>Span</span></li>
</ul>
<ul>
<li class="flex-child"><span>Span</span></li>
<li class="flex-child text-center"><h1>Heading</h1></li>
</ul>
Adding in icons is easy enough. It doesn't necessarily change anything at this point, but we should probably add an .icon
class to better define what it means to be an icon. In fact, let's make a rule that our icons will be square.
<i class="icon"><</i>
<i class="icon">></i>
<i class="icon">≡</i>
.icon {
font-size:1.3em;
width:1em;
height:1em;
display:inline-block;
line-height:1em;
text-align:center;
vertical-align: text-bottom;
font-style: normal;
font-weight: normal;
}
We're using an i
tag here to indicate an icon element. That's not actually what the i
tag was intended for. But technically the i
tag has been changed from its original italics meaning. Since some browsers still italicize the element, it's worth turning off the font-style of it. We also give it a defined height and width, but can only do that if we define it as at least inline-block, and if we do that, we should set the vertical align of our element to make sure that it isn't hoisted up on the baseline.
<ul>
<li class="flex-none"><a href="#"><i class="icon">
<svg viewBox="0 0 100 100"><polygon points="80,10 20,50 80,90 80,75 42,50 80,25 "/></svg>
</i></a></li>
<li class="flex-child"><span>Span</span></li>
<li class="flex-none"><a href="#"><i class="icon">
<svg viewBox="0 0 100 100"><path d="M50,25.2L64.5,39H64v11v29H36V50V39h-0.5L50,25.2 M50,10L8,50h17v40h50V50h17L50,10L50,10z"/></svg>
</i></a></li>
<li class="flex-none"><a href="#"><i class="icon">
<svg viewBox="0 0 100 100">
<rect x="15" y="15" width="70" height="14"/>
<rect x="15" y="43" width="70" height="14"/>
<rect x="15" y="71" width="70" height="14"/>
</svg>
</i></a></li>
</ul>
.icon>img,
.icon>svg {
width: 100%;
height: 100%;
pointer-events: none;
}
SVG also have a bad habit of grabbing events, so let's just remove the pointer-events from anything inside the .icon
. You may note that icons from fonts tend to be inconsistent in their placement, and ultimately should probably just be made into svg files, or code, anyways. Don't bother searching or downloading them, icons are literally the easiest thing in the world to make. I don't care what you were told. Just make some. They're usually just squares and circles.
In fact, here's a couple common icons in svg format. Don't take these. Make them yourself. I can't believe you thought I wanted you to use these. How dare you. I mean... you could use them... there's really nothing stopping you. But I'll think less of you. I guess I should ask that if you use these, please credit this website.
Well since we've introduced icons into the header, we might as well do it in the footer as well.
But not everyone knows what our icons mean. It is dangerous to make assumptions about their understanding of our icons, unless there's been enough research to prove most of our users, and all new users, intuitively understand the symbolism. So maybe some labels are a good idea.
<div data-role="footer" class="nav-mobile">
<ul class="text-center">
<li class="flex-child"><a href="#">
<i class="icon"><img src="/img/icon/icons_map-marker.svg"></i>
<span>Map</span>
</a></li>
<li class="flex-child"><a href="#">
<i class="icon"><img src="/img/icon/icons_list.svg"></i>
<span>List</span>
</a></li>
<li class="flex-child"><a href="#">
<i class="icon"><img src="/img/icon/icons_profile.svg"></i>
<span>Profile</span>
</a></li>
</ul>
</div>
Adding labels like this isn't too bad. But it takes up all the space real quick. And honestly, these labels shouldn't actually be for reading. They're just for recognizing the words. We could make the text smaller, but I think we should go even further. We're going to have alter a number of things to get this to work correctly. We're going to have to alter the line-heights especially of our elements to give them enough space to co-exist.
<div data-role="footer" class="nav-mobile icon-labels">
<ul class="text-center">
<li class="flex-child">
<a href="#">
<span class="icon-img"><i class="icon"><img src="/img/icon/icons_map-marker.svg"></i></span>
<span class="icon-label">Map</span>
</a>
</li>
<li class="flex-child">
<a href="#">
<span class="icon-img"><i class="icon"><img src="/img/icon/icons_list.svg"></i></span>
<span class="icon-label">List</span>
</a>
</li>
<li class="flex-child">
<a href="#">
<span class="icon-img"><i class="icon"><img src="/img/icon/icons_profile.svg"></i></span>
<span class="icon-label">Profile</span>
</a>
</li>
</ul>
</div>
.icon-labels .icon-img {
height: calc(var(--header-height) * 0.7);
line-height: calc(var(--header-height) * 0.7);
display: block;
}
.icon-labels .icon-label {
height: calc(var(--header-height) * 0.3);
line-height: calc(var(--header-height) * 0.3);
font-size: 0.6em;
display: block;
}
Not bad. At this point we can create all manner of common navigations.
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_home.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_search.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_camera.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_heart.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_profile.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_arrow-up.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_arrow-down.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_star.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_world.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_shishkabob-vertical.svg"></i></a></li>
<li class="flex-none"><a href="#"><i class="icon"><img src="/img/icon/icons_arrow-left.svg"></i></a></li>
<li class="flex-child text-left"><span>Files</span></li>
<li class="flex-none"><a href="#"><i class="icon"><img src="/img/icon/icons_search.svg"></i></a></li>
<li class="flex-none"><a href="#"><i class="icon"><img src="/img/icon/icons_list.svg"></i></a></li>
<li class="flex-none"><a href="#"><i class="icon"><img src="/img/icon/icons_shishkabob-vertical.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_home.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_search.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_bell.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_mail.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_feed.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_group.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_chat-empty.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_bell.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_search.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon"><img src="/img/icon/icons_hamburger.svg"></i></a></li>
Well now that we've got a basic setup going, let's give us an active status. This is going to change up our design possibilities a little. Maybe a lot. Up until now, everything has been even and horizontal. But now we might break out a bit, and try some more unorthodox concepts.
.nav-mobile-background li.active>a {
background-color: var(--main-light-color);
}
First things first. The active class simply highlights the anchor that is active. But we don't add the active to the anchor itself. We add it to the li
sibling which is active, and then allow each of our anchors to take on design based on their parent's class.
But there's so much more that can be done here. Why not change size?
.nav-mobile-grow-icon .icon {
transform: scale(1,1);
transition: all 0.3s;
}
.nav-mobile-grow-icon li.active .icon {
transform: scale(1.6,1.6);
}
Break out a hump?
.nav-mobile-hump a {
position: relative;
z-index: 3;
background-color: white;
}
.nav-mobile-hump a::before {
content: '';
position: absolute;
z-index: 2;
top: 0;
left: 50%;
width: 5em;
height: 3em;
background-color: white;
border-radius: 50%;
transform: translate(-50%,1em);
transition: all 0.3s;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
}
.nav-mobile-hump li.active a::before {
transform: translate(-50%,-1em);
}
.nav-mobile-hump a::after {
content: '';
position: absolute;
z-index: 2;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: white;
transition: all 0.3s;
}
.nav-mobile-hump .icon {
content: '';
position: relative;
z-index: 4;
transition: all 0.3s;
}
Well that's goofy. A lot of interesting things are happening here. First off, we're using a ::before
and ::after
to create this effect. The before is placing an oval onto the screen, and the after is making a rectangle that covers over top of that. This allows the ::before
oval to overflow out of the anchor and cast a shadow out over the body, while the ::after
simply covers up the shadow that would have been present inside the anchor.
The z-index
is helping this effect along, as each piece needs to be placed around each other, including the icon. At that point we use a transition
on a transform:translate
, and the circle slides up into view from below. The animation is really quite effective. Especially if we keep going.
Why not combine some?
.nav-mobile-grow-icon.nav-mobile-hump li.active .icon {
transform: scale(1.6,1.6) translateY(-0.25rem);
}
Combine all the things!
.nav-mobile-background li.active a::before,
.nav-mobile-background li.active a::after {
background-color: var(--main-light-color);
}
Now that's looking good. Animation is something that should serve the purpose of grabbing attention, not distracting, and this one really pops. It's short. It's simple. Even though there are three things happening here, they're all in service of making the user want to touch these buttons, and helping them to know which button is pressed.
Before we get too much further, let's just discuss the concept of badges. They're a lot easier to make than you'd think, and since we made us some icon elements, we attach badges right to those sweet sweet squares. Badges are the little red circles that pop up and tell you how many things are ready for you to check somewhere. Let's just get those out of the way.
<li class="flex-child"><a href="#"><i class="icon" data-badge="0"><img src="/img/icon/icons_chat-empty.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon" data-badge="100"><img src="/img/icon/icons_bell.svg"></i></a></li>
<li class="flex-child"><a href="#"><i class="icon" data-badge="7"><img src="/img/icon/icons_mail.svg"></i></a></li>
[data-badge]::after {
content: attr(data-badge) '';
position: absolute;
top: -0.5em;
right: -0.5em;
font-size: 0.6rem;
background-color: red;
line-height: 1em;
padding: 0.2em 0.5em;
border-radius: 1em;
color: white;
vertical-align: middle;
font-family: arial,sans-serif;
}
This is actually pretty fun. Without adding any more elements to the page, we can program a value into a data- attribute on one of our icons. This value can be pulled out by a ::before
or ::after
content
property by using the attr()
function. At that point we just design it and place it where we want, making sure our icon has a position of some kind to contain the badge. If you don't want a badge, just leave off the data- attribute.
The simplest way to handle search is to add a chromeless class to your css.
<div data-role="header" class="nav nav-mobile">
<ul>
<li class="flex-none">
<a href="#"><i class="icon"><img src="/img/icon/icons_arrow-left.svg" alt=""></i></a>
</li>
<li class="flex-child" style="padding-right:0.5em">
<input type="search" class="chromeless" placeholder="Search">
</li>
</ul>
</div>
.chromeless {
border-width: 0;
font: inherit;
color: inherit;
background-color: transparent;
outline: 0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
display: inline-block;
}
Making any element chromeless in our app is going to knock out a ton of design concepts. We'll turn almost everything off, and make the element fill its parent.
<div data-role="header" class="nav nav-mobile">
<ul>
<li class="flex-child flex-parent flex-align-center" style="padding:0 0.5em">
<div class="hotdog">
<input type="search" placeholder="Search">
<i class="icon"><img src="/img/icon/icons_microphone.svg" alt=""></i>
</div>
</li>
</ul>
</div>
.hotdog {
background-color: var(--neutral-light-color);
border-radius: 2em;
padding: 0 1rem;
display:flex;
align-items:center;
width: 100%;
height: calc(var(--header-height) - 0.6em);
}
At this point, let's design a hotdog for general use. This element will look like a hotdog wherever it is, and is useful for search inputs, chat inputs, and other such stuff. Let's also include any input elements in hotdogs to our chromeless definition. Hotdogs will be placed around inputs, and therefore icons can be placed around or inside of them.
For design sake, we'll make the hotdog be a bit shorter than our header height. It's just for fun, but it does make us have to add flex-parent
and flex-align-center
to the parent li
, so that our hotdog will be centered.
<div data-role="header" class="nav nav-mobile">
<ul>
<li class="flex-none">
<a href="#"><i class="icon"><img src="/img/icon/icons_home.svg" alt=""></i></a>
</li>
<li class="flex-child flex-parent flex-align-center">
<div class="hotdog" style="padding-left:0.3em">
<span style="padding:0">
<a href="#"><i class="icon"><img src="/img/icon/icons_lock-closed.svg" alt=""></i></a>
</span>
<input type="search" placeholder="Search or Type URL" style="margin-left:1em">
</div>
</li>
<li class="flex-none">
<a href="#"><i class="icon"><img src="/img/icon/icons_shishkabob-horizontal.svg" alt=""></i></a>
</li>
</ul>
</div>
.hotdog a {
padding: 0;
background-color: rgba(255,255,255,0.5);
border-radius: 2em;
width: 1.8em;
height: 1.8em;
align-self: center;
display: block;
text-align: center;
line-height: 1.8em;
}
Now it's all about coming up with ideas and using this new tool however you need.
<div data-role="page">
<div data-role="main">
<div data-role="header" class="nav nav-mobile">
<ul>
<li class="flex-child"><h1>Title</h1></li>
<li class="flex-none"><a href="#"><i class="icon"><img src="/img/icon/icons_plus.svg"></i></a></li>
</ul>
</div>
<div data-role="footer" class="nav nav-overlay">
<ul class="text-center flex-justify-center">
<li class="flex-none quarter"><a href="#"><i class="icon"><img src="/img/icon/icons_map-marker.svg"></i></a></li>
<li class="flex-none quarter active"><a href="#"><i class="icon"><img src="/img/icon/icons_list.svg"></i></a></li>
<li class="flex-none quarter"><a href="#"><i class="icon"><img src="/img/icon/icons_profile.svg"></i></a></li>
</ul>
</div>
</div>
</div>
.nav-overlay[data-role='header'],
.nav-overlay[data-role='footer'] {
position: absolute;
left: 0;
width:100%;
padding:0 1em;
}
.nav-overlay[data-role='header']{
top:1em;
}
.nav-overlay[data-role='footer'] {
bottom:1em;
}
.nav-overlay a {
padding:0;
border-radius:50%;
width:var(--header-height);
background-color:white;
box-shadow:1px 1px 10px rgba(0,0,0,0.2);
}
Ok. So making our nav into an overlay requires a bit of a reorganization of our structure. Let's move our header or footer into the main. This way we can design them differently, and they'll be organized and positioned according to the main. For a basic overlay nav let's just give everything some circles.
There are definite reasons to design overlay navigations. The main reason is because you want to show something that's behind. But let me tell you, a spread of circles is not the right design to put over a text list. It's best used in front of an image or a map. You can put one circle in front of a list, but generally, there is a reason we sequester most navs away from our content.
.nav-overlay-grow a {
transition:all 0.3s;
transform:scale(1,1);
}
.nav-overlay-grow li.active a {
transform:scale(1.4,1.4);
transform-origin:bottom center;
}
Adding an active to overlays is just as easy as before.
<div data-role="page">
<div data-role="main">
<div data-role="header" class="nav nav-overlay">
<label class="hotdog">
<i class="icon"><img src="/img/icon/icons_search.svg" alt=""></i>
<input type="search" placeholder="Search">
</label>
</div>
<div data-role="footer" class="nav nav-mobile">
<ul class="text-center">
<li class="flex-child"><a href="#">Prev</a></li>
<li class="flex-child"><a href="#">Next</a></li>
</ul>
</div>
</div>
</div>
.nav-overlay .hotdog {
background-color: white;
box-shadow:1px 1px 10px rgba(0,0,0,0.2);
width: initial;
height: var(--header-height);
}
Adding search into our overlay, let's give ourself back the header height, and make the color simply be white.
<div data-role="page">
<div data-role="main">
<div data-role="header" class="nav nav-overlay text-right">
<label class="hotdog search-box-min">
<i class="icon"><img src="/img/icon/icons_search.svg" alt=""></i>
<input type="search" id="search-min">
</label>
</div>
</div>
<div data-role="footer" class="nav nav-mobile nav-material">
<ul class="text-center">
<li class="flex-child third"><a href="#">Gallery</a></li>
<li class="flex-child third"><a href="#">Photo</a></li>
<li class="flex-child third active"><a href="#">Video</a></li>
</ul>
</div>
</div>
.search-box-min {
display:inline-flex;
max-width:100%;
}
.search-box-min input {
display:inline-block;
width: 0;
padding: 0;
transition:width 0.3s, padding 0.3s;
}
.search-box-min input:focus {
width: var(--screen-width);
}
With only a little css, we can even make an interactive search box that slides open, using the label trick to open up the input when it is given :focus
. Sometimes it's amazing what can be done with only CSS and no javascript.
In this particular example, I'm providing a screen-width variable, but on a normal device, this could simply be done with a 100vw
value.
You might also notice I've brought in the material tab design from a previous tutso navigations. Because why not?
<div data-role="page">
<div data-role="header" class="nav nav-mobile">
<ul>
<li class="flex-none">
<a href="#"><i class="icon"><img src="/img/icon/icons_hamburger.svg" alt=""></i></a>
</li>
<li class="flex-child"><h1>Hangouts</h1></li>
</ul>
</div>
<div data-role="main">
<div data-role="footer" class="nav nav-overlay">
<ul>
<li class="flex-child"></li>
<li class="flex-none">
<a href="#"><i class="icon"><img src="/img/icon/icons_plus.svg" alt=""></i></a>
</li>
</ul>
</div>
</div>
</div>
It's entirely possible that some of you won't make the connection, but at this point making a floating button is very easy. Pick whether you want it in the head or foot. Then use any of the systems in place to move it wherever you want.