Off-canvas navigation is a popular UI interaction in repsonsive design. CSS Grid Layout makes adjusting the interaction for different device widths easy thanks to its ability to overlay items.
The final CSS Grid Layout: Off-canvas demo can be accessed by clicking the link.
A stripped down version of this demo is available on CodePen.
The base HTML and CSS.
The basic structure of the page is fairly simple.
When the grid is defined, We’ll want to assign the
main-content to the same
grid-row regardless of viewport size. Whether or not the
main-nav is visible and how wide it is will depend on the viewport.
The grid will have a simple structure of three rows; an 80px tall header, a fluid content area and a footer with a fixed height of 120px. The content area’s height uses
calc to set the minimum height to 100vh minus the combined height of the header and footer. This applies to all viewports. The grid’s
overflow property is set to
hidden for large viewports that have the
main-content area restricted to 90vw and centered in the viewport. This prevents the
main-nav from leaking outside the container.
The simplified placement of the grid items is as follows:
Now that the basic grid is defined, some CSS is needed on the
main-nav to push it off the viewport and a rule to trigger the animation when the class
is-active is present.
- Moves the
main-navout of the viewport. When animating an element it’s best to use
transformsover offset properties like
leftif you want a smooth animation.
- Defines the transition on the animation, using a cubic-bezier for the timing function is probably going to feel nicer than one of the standard keyword based timing functions.
will-changeproperty allows CSS authors to provide a hint to the browser that a property will change which allows it to start optimzation before the change occurs. It’s a potential antidote to a bad case of the Janks. Read more on MDN.
- When the
is-activeclass is added to the element it will animate from the left across the
We’ll need to adjust the grid layout from its mobile first version to support an unknown number of device widths.
Once the viewport gets too wide that it would be weird to have the off-canvas menu take up the full width so we’ll want to adjust the grid. I’ve set it at 45em/720px. This only requires a change to the
grid-column properties that defined the grid index placement in the mobile version still work in the 720px and wider version. By adjusting the values of the
grid-template-columns property on the
.grid class it just works on larger screens. The difference is the grid’s first column is now set to 240px and the rest of the content can span across 4 equal units plus the 240px reserved for the nav. All of the rules that were applied to the
main-nav class above are all still valid here as well.
For users with really, really wide screens (or about 118em/1900px) there will be enough screen real estate to have the menu always visible. Only two rules needs to be changed to do this:
This adjusts the positioning of the
main-content area to start at the second line of the grid column axis. The previously defined position of the
main-nav is still set to
1 which is where we want it to be. The only other CSS you need to apply is to hide the
<button> used to toggle the visibility of the menu. The rule on
main-nav resets the transform on the menu so it’s always visible on the page.
Note (May 11, 2017): Heydon Pickering has a very thorough article on his Inclusive Components site on Menus and Menu Buttons that you should read when implementing this pattern. I will update this post to add things I missed in the next few days.
Now that the basic grid is set up and the
main-nav has been booted off to the side we need to provide the user with a button to display it. While it’s possible to handle the visibility of an element with some clever CSS hacks is rarely a good idea. Unless you’re using elements with baked in interaction like
To start, I need an element to toggle the visibility of the menu. The only option for that is the humble
<button> element. For this demo, I’m going to place the
<button> in the
js-navigationclass exists as a hook for the JS function that will show the off-canvas navigation
aria-haspopupattribute indicates that the element has a hiddent context menu. More information on this attribute is available here
aria-ownsattribute accepts an ID and “define a visual, functional, or contextual parent/child relationship between DOM elements where the DOM hierarchy cannot be used to represent the relationship.” More information on this attribute is available here
aria-expandedattribute, “Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.”. More information on this attribute is available here
Note: If there’s something incorrect or harmful in this approach to a11y please let me know.
<nav> element and visibility
On smaller viewports, that navigation menu is only visible when the user clicks on the Menu button. We have to go back and add an attribute to the
<nav> to indicate it’s visibility:
On really wide viewports (say something like 1600px or wider) the navigation will always be visible. To make sure the aria role is acurate include a titch of JS to change the attribute on larger screens using
The value of the
<nav> that triggers the animation.
As I mentioned earlier, Heydon Pickering released a small library to help with accessibility and buttons called Inclusive Menu Button. I’d strongly recommend following that project.
The layout for various screen sizes has been defined by the CSS Grid including the positioning of the off-canvas menu. Once the
is-active class is added to the menu will animate in from the left.
CSS Grid Layout makes creating the base layout for this pattern very easy. This is a simple layout that demonstrates how much you can accomplish with CSS Grid with a little code. The layout of each grid area isn’t defined. Other elements within the grid can be layed out using nested grids, flexbox or any other layout method you feel like. You can see a page using this basic layout here. I’m not a designer as should be evident the second you click the link.
Tuber: Uber for Hot Dogs (Using CSS Grid Layout)
Learn more about CSS Grid Layout.
Corrections or comments can be directed to my twitter account @jshvgt.
Send a response
If you've written a response to this post enter the url of your post and send it over.