We recently rolled out an update to the main navigation on the GoSquared site. If you’ve been to gosquared.com lately, you may have already noticed the update – it looks like this:
Or this, if you’re on a mobile device:
Building semantic navigation
We knew from the outset of this update that we wanted to build a dropdown-style navigation for desktop browsers, and fullscreen accordion-style navigation for smaller screens.
Besides that, we also had some other requirements:
- It should be simple for us to maintain if we want to add or modify items.
- It should work in as many browsers as possible without any ugly hacks.
- It should be lightweight – that is, if it’s included on every page of the GoSquared site, it shouldn’t require any weighty library code either slowing down the page or leading to weird broken states while code loads.
This led us to set a few implementation goals to direct how we built it:
- Make the markup as semantic as possible (i.e. use the well-defined HTML elements in a logical way, no putting bits and pieces of markup in illogical places just to make dropdowns work)
- Reuse the same elements for both mobile and desktop (i.e. no duplicating of code)
These three goals in combination would hopefully mean that we get the benefits of performance, graceful degradation, accessibility and maintainability pretty much for free.
The code behind the navigation – start with a list
At its core, the GoSquared site navigation is, just like any on any other site, a collection of links. It starts out life as an unordered list inside a
<nav>, with each list item containing an anchor tag.
<nav> <ul> <li> <a href="/one">Item 1</a> </li> <li> <a href="/two">Item 2</a> </li> </ul> </nav>
We can put some styles on this to align the items horizontally, and give them some whitespace and hover states. That looks something like this:
Now that’s all well and good if your navigation has only two or three links that you want to include, but we have a whole hierarchy that we want to make accessible: under each item in the main navigation, we have one or two lists of places we want to link to.
We start out by putting each of these sub-navigations inside the corresponding item in the root list. This starts out with another
<nav>; since we have a couple of different sections to include, each gets its own
<section> with a
<header>, and then the familiar
<ul> with a list of anchor tags.
<nav> <ul> <li> <a>Item 1</a> <nav class="subnav"> <section> <header>Subnav list title</header> <ul> <li> <a> <h5>Subitem 1.1</h5> <p>A short description</p> </a> </li> <li> ... </li> </ul> </section> <section> <header>A second subnav column</header> <ul> ... </ul> </section> </nav> </li> <li> <a>Item 2</a> <nav class="subnav"> ... </nav> </li> </ul> </nav>
We can then position these subnavs absolutely to show as dropdowns, then use CSS
:focus-within pseudo-selectors to show them when the visitor hovers or focuses the parent list item.
By putting the sub-navigation directly inside the parent elements (as opposed to in a separate
:hover pseudoselector on the parent when the visitor moves their mouse away from the root list and starts interacting with the dropdown.
Throw in some CSS transitions on
visibility (we don’t want the dropdowns intercepting the visitor’s mouse when they’re not visible) and the result looks something like this:
Making our responsive navigation work on mobile devices
So far we’ve focused on the desktop experience for this navigation. But nowadays the mobile version of your experience is just as important, if not more so.
Not only do mobile devices come with smaller screens, but they also have a different set of interactions – there’s no such thing as a “hover” state on a touch display without a mouse, for example.
By and large browsers normalise these differences fairly well, so they are at least usable (for example, triggering a hover state after one tap, and the actual “click” only on the second tap), but this often feels like a “fix” retrofitted to an experience solely to prevent it from being broken. So we decided to do away with the hover-based approach and create a better-designed experience for mobile devices.
The mobile design calls for toggling the sub-navs between visible and hidden based on clicking or tapping the header of the corresponding section. This can also be achieved using CSS by incorporating a hidden checkbox and using the
~ (sibling) rules.
The changed markup looks something like this:
<li> <input type="checkbox" id="toggle"> <label for="toggle"> <a>Item</a> </label> <nav class="subnav"> ... </nav> </li>
… and the only change to the CSS to make this work is to add a rule that shows the subnav under
:checked ~ .subnav.
For the whole of the rest of the mobile navigation we can reuse all of the existing markup we already have, simply overriding and resetting any rules we previously had to show subnavs as dropdowns.
The end result is something like this (you’ll need to view on CodePen and resize your browser to see the difference between desktop and mobile versions)
That’s all, folks!
Obviously there’s a lot more little things that go into the navigation that you see on the GoSquared site today (icons, arrows, and a few more other bits specific to mobile), but hopefully this post has helped explain the interesting parts of how we built the new navigation for GoSquared.
Have any thoughts? Anything we could have done differently or better? Let us know via Twitter!