Responsive Navigation for 73+ Languages

We just launched a significant update to LDS.org, the main site I am responsible for where I work. There were two main parts to this release. First, a new header that was supposed to be a style update, but ended up being a complete rewrite. Second, applying a similar style to the content portions of the site. The actual coding for this release has been about 2.5 months in the making. Probably 2 months on the header and half a month on the content portions.

I may write up something on the second part another day, but this post is about the first part… the updated header. Hopefully you will understand how about 150px of vertical real estate took me around 2 months to complete.

A Little History

If you want to get to the meat of the article, skip down to the “Make It Fit” section (can’t find a way to “deep link” on Medium). Otherwise, suffer through a little history of what got us here.

Old version of the header at 1040px

This started off as design refresh of the header (old version above). LDS.org is going to be changing in significant ways over the coming months and years, and this was one of the first steps. As I looked down through the other steps that are coming I realized that the menu was going to be changing a lot. The current system didn’t really facilitate those changes, so we decided to start from scratch with both the code in the browser as well as the system that is used to manage all the links.

New version of the header at 1040px

The header was rewritten from a set of fixed menus, with fixed sub-menus to a system that is much more robust, allowing menus to be added, removed and changed as needed as we work through the future site changes. Now you would think this would be something that would be built into any navigation system, but we previously had 5 top level and 54 sub level pieces of navigation. This has grown to 6 top level and 56 sub level as of this release, and will continue to change (some getting bigger, some getting smaller) over time. This is not your average navigation system.

Our content team will now be able to add, remove or change top level menus and sub menu items as needed without development involvement. Building a system prepared for them to do all that is pretty tricky. Getting all of that to be responsive is even trickier. I don’t know when an additional top level item will be added or changed that could require the design to wrap to a tablet or mobile look sooner. Yes, I know “mobile first,” and I know not to specifically design for mobile or tablet… but those are good general descriptions, you get the idea. Oh, I forgot one other teeny, tiny thing that we accommodate that most sites in the world don’t. We currently support 73 languages.

73 Languages

Yes, you read that correctly. We support 73 languages for the navigation as of this writing, and are regularly working to add more. There are 105 listed on the linked language page above, but we don’t have the navigation available for all of them since they have so little content translated.

Now I am the first to admit that we could do a MUCH better job of supporting all these languages. We have small columns that wrap big words poorly, our right to left languages are barely supported at best. But we do the best with the time, money and resources we have.

Ponder on all the things I described above. 5–6 top level navigation links, 54+ sub level links, these links will be added, removed and modified over coming months, and we have to support 73 languages across all responsive devices. Two months sounding more reasonable yet?

Handling the languages

Our past support of languages in the header was pretty poor. Having language specific CSS files and custom breakpoints per language is just not reasonable to maintain. When we last redid the header a few years ago I chose breakpoints based off of the lowest (largest?) common denominator, the widest language. At the time it was Deutsch (German).

Deutsch version of header at 1040px

When the Deutsch menu got too close to the logo I had do do something. This is 1px before it would wrap.

1px before it would flip at 900px

I set a breakpoint to make a change (move the search box into an icon to get some space back). For easier use later on, I will call this “tablet”.

The “tablet” view

When it got too close again I set another breakpoint (go to a hamburger). For easier use later on, I will call this “mobile”.

The “mobile” view

This worked pretty well. It did have a side effect that our most common language of English wrapped a bit early, but it wasn’t terrible. Again, 1px before it would flip to tablet.

1px before English flips at 900px, a little too early

Inversely a very short language like Chinese flipped WAY too early, but I didn’t have a good alternative that didn’t involve managing files and breakpoints per language. Things didn’t break, they were just odd. Life went on. 1px before the flip.

1px before Chinese flips at 900px. WAY too early

I should mention that not all of our languages have all of the available links. Some will have fewer links because there isn’t as much content translated into that language. This also plays into the complexity of the overall system.

Sometime last year I found out that Tongan had some issues. I didn’t know that support for the language had continued to expand, and now they had all of our main navigation items. I also learned that they are even more verbose than German.

Tongan is really verbose. This is the same 1040px wide as other screen shots

This means my lowest common denominator was now wrong, but at the same time I didn’t want to change that denominator to make all the other languages flip even sooner than they already were. I stuck my head in the sand and forgot about this issue for a season. Bad Aaron.

This is what Tongan looked like at 768px (iPad portrait). Yeah… not good.

Tongan on an iPad portrait view at 768px. Not good.

Back to the new header. I was rewriting the whole thing, this was my chance to figure out something better for supporting other languages. This wasn’t a requirement of the project (remember, this was “just” a design update) but this was something that had been nagging me for months. It was an itch I had to scratch.

By even having 73 languages we were doing a good job getting the word out to “every nation, kindred, tongue and people“, but it could definitely be a better experience for those users without one language setting the standard for other languages.

Make It Fit

There is another article that I need to write about a plugin I wrote that I call makeFit. This plugin is used on our homepage to make sure that text which goes over the top of images will fit on top of the image. This is another place where we don’t know what the content will be from day to day and we don’t know the size in different languages.

The basic idea of it is that it checks to see if a given element fits within the space of another element. For the homepage, if the div with the text won’t fit within the space of the image it covers anymore (including padding and margin) the plugin adds a class to the div. This can do whatever a class does, like reduce text size, padding/margin, line-height, etc.

If it still doesn’t fit, it applies the next class in the list. You could have two or two hundred steps (we usually do 3). It just goes through the list until it fits or runs out of classes. For us, the last step completely changes the layout putting the text under the image instead of on top of it.

I need to rewrite the full plugin to not require jQuery and the jQuery Widget Factory, but it is what it is for now. You get the idea.

// presume these vars have been set up previously
sizeClassesArray.forEach(function(item){
if(elemToCheck.offsetHeight > maxHeight ||
elemToCheck.scrollWidth > elemToCheck.offsetWidth){
elemToClass.classList.add(item);
}
});

I used this same logic within the header. I rewrote the plugin without a lot of the bells and whistles the full plugin has in a much simpler non-jQuery way (greatly simplified above). The base logic is the same. I check to see if the wrapper for the menu is less than XX height. If it is less, the content of the current menu, in the current language must fit. Nothing to do here.

If the wrapper is greater than the given height then the menu options must have wrapped so apply the first class. The classes follow the same logic described above. First it moves the search field to an icon (tablet), and then moves the menu into a hamburger (mobile).

This allows each language to only go to the next transition when it needs to. Tongan flips to mobile way early at 596px and Chinese displays at desktop all the way down to 455px. The following screenshots are all at 570px, showing how different languages are now able to show specifically what they need. No more lowest common denominator. Tongan goes to “mobile”, English goes to “tablet”, Chinese stays “desktop” all at the same width.

Tongan at 570px displaying as “mobile”
English at 570px displaying as “tablet”
Chinese at 570px displaying as “desktop” (ignore untranslated texts)

Screenshots are great, but here is a video of it in action for the full effect.

There is one unfortunate caveat to this scenario. It can’t be mobile first. If it were mobile first and started with the menu in the hamburger we wouldn’t know if the text would wrap or not because it is hidden in the hamburger already. We have to start with the text displayed (desktop) to know what wrapping will occur. Media queries also go out the door. It is completely driven by the addition of the classes that move it to the next step based off of content and language.

I guess a second caveat is the requirement of JavaScript. But with such a dynamic situation that we are needing to account for, it is the best I could do. Would love to hear other ideas.

Much of this is also made possible by leveraging some cutting edge CSS in using flex-box. Luckily our analytics support the dropping of older browsers to allow us to do this, or this would be a very different post (likely documentation of my weeping).

Not Complex Enough As It Is

Adding a section/site label

We will be introducing a new element to the header of a section label (the “A Pretend Site Label” in the above image). This goes in the empty space next to the logo. As if we didn’t have enough things going on in the header, this adds another level of complexity.

Luckily, we can use the same makeFit logic that checks to see if it fits and if it doesn’t, makes the text smaller.

Site label as smaller text

Then we allow it to wrap to two lines.

Site label wrapping to two lines

If it gets to the point where it would wrap to three lines, or if it is one or two words that won’t wrap to three lines we check to see if is too wide based on scrollWidth being larger than offsetWidth (see code snippet above), the next step is to wrap the label under the logo.

Site label wrapping under the logo

The two makeFit instances run separately allowing the respective lengths to take care of themselves. You can have a long label and short menu, or short label and long menu each wrapping when they respectively need to. Everything plays nice.

Wrap this novel up already!

And that is it. That is how we accommodate the complexity of languages, links and devices. There are more changes to the header coming, some small, some large. We will continue to do our best to accommodate the varying content in varying languages as best we can.

This is by far the most complex thing I have ever made. Hopefully it lives up to all it is supposed to do.

I am super excited to have this new functionality out and for the visitors to our site to be able to use it. Go give it a try. Resize the window, try different languages. Hopefully it works as intended. If it does work as intended, I won’t ever hear about it because it will just work no matter the language, no matter the content.

I would be remiss if I didn’t thank the members of the team who made this happen. Thanks to Joe Pemberton and Colt Pini for the designs and working with me to get all this craziness to work. Gary Gurney for all the backend work, and Kevin Pyles and Bryan Wright for lots of testing. There are also many unnamed people who helped with translating new strings into all those languages, project management, and more.

Above all that is the significant amount of inspiration I received from above. I know I didn’t come up with this stuff on my own. Many prayers were answered to get all this complexity working.


Here is a screenshot of 18 miscellaneous headers in cool looking languages.

18 headers
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.