Languages

Web Style Sheets CSS tips & tricks

See also the index of all tips.

A sliding menu

The page ‘Fixed menus’ shows how to make a menu that stays at the same place at the edge of the window, even if the rest of the page scrolls. We can make that menu more interesting by making it fold when not in use.

The menu on the right of this page only shows as a small green tab, until you move the mouse pointer over it (or click it on a touch screen) and then it opens to show the text it contains.

To make the unfolding more pleasing, a short transition of just 0.2 seconds makes the menu slide in smoothly from the right.

Especially when space is at a premium, such as on the small screen of a mobile phone, partially hiding a menu may be useful. (On the other hand, it also slows people down: They can't see right away what options are available, but have to move their mouse or finger to a specific, fairly small spot on the screen first.)

Details

Our menu is just a UL list and looks like this:

<ul id=menu>
<li><a href="#sliding">A sliding menu</a>
<li><a href="#details">Details</a>
<li><a href="#a11n">Accessibility</a>
<li><a href="#misc">Menus of unknown size</a>
</ul>

Below is the essential part of the style sheet. See the page ‘Fixed menus’ for explanations. For brevity, we left out the border, shadow and rounded corners. You can see the missing style rules by looking at the source of this page.

#menu {
  position: fixed;
  right: -8.5em;
  top: 50%;
  width: 8em;
  background: hsla(80, 90%, 40%, 0.7);
  color: white;
  margin: -3em 0 0 0;
  padding: 0.5em 0.5em 0.5em 2.5em;
}

The difference with the previous fixed menu is that the ‘right’ property is not 0, but -8.5em. That causes most of the menu to be outside the window and thus invisible. Note that the menu, including the padding, is 11em wide (8em + 0.5em + 2.5em), so there is still 2.5em visible.

The only thing we need to add now is a rule to reset the ‘right’ to 0 when the menu is hovered over:

#menu:hover { right: 0 }

With these rules, the menu springs back and forth when the mouse enters and leaves the menu. We wanted it to slide smoothly in and out. That requires one more rule: a ‘transition’ property to slow down the transition, in our case to 0.2 seconds:

#menu { transition: 0.2s }

By default, a transition starts slow, speeds up a bit and then gradually slows down again. Many other possibilities can be selected by adding extra keywords to the ‘transition’ property, but for this example, the default is good enough.

Accessibility

Unfortunately, our sliding menu has an accessibility problem: When somebody tries to navigate the page with just the keyboard, without a mouse or touchscreen, there is no way to make the menu visible.

We would like the menu to slide in when one of its items receives the keyboard focus, but that is impossible. There is currently (in 2017) no CSS selector that is able to select an element when one of its children gets the focus. (There may such selectors in the future.) We can, however, make the menu items themselves slide in.

It is not as beautiful, but it works. Try it! Push the tab key repeatedly (or ctrl+↓ or ⌘+↓ on Opera until version 12) to select each link on this page in turn and see how the menu items slide out one at a time.

The way that is done is by making the links relatively positioned and adding a ‘left’ property to move them to the left, when they have the focus. We also give them the background color of the menu:

#menu a {
  position: relative;
  left: 0;
}
#menu a:focus {
  left: -7em;
  background: hsla(80, 90%, 40%, 0.7);
}

To make it slide smoothly, instead of just pop out, we use a transition again:

#menu a { transition: 0.2s }

It's because of that smooth transition that we set the ‘left’ explicitly to 0: The default value is namely ‘auto’, but the ‘left’ property can only animate between numbers.

And finally, because it is possible that a menu item has the focus at the same time that the mouse hovers over the menu, we make sure the menu item does not slide out in that case:

#menu:hover a:focus {
  left: 0;
  background: none;
}

Menus of unknown size

Our menu contains four items and some padding, so it is easy to center it vertically: Each line is about 1.3em (we could have set the ‘line-height’ explicitly, if we wanted to be sure) and the padding is 1em, which makes about 6.2em total, so a top margin of -3em moves the menu back up by about half its height. But what if we don't know how many lines the menu has?

The section ‘Centering vertically in CSS level 3’ on the page ‘Centering things’ shows a way to center an element vertically. The example there uses absolute positioning, but the same idea can be applied to fixed positioning.

We'll have to remove the negative top margin (-3em) and set it to 0 instead. And we use the ‘transform’ property with a translation that moves the element up by half (50%) of its own height:

#menu {
  margin: 0;
  transform: translate(0, -50%);
}

Make sure you only use this for fairly short menus, though. The menu will be as high as it needs to be and if it happens to be taller than the window, the top and bottom will be clipped without a way to scroll to them.

Older browsers may not know the ‘transform’ property, so it's wise to also keep the negative top margin with half the estimated height of the menu. The ‘@support’ rule comes in handy here. It makes sure the ‘margin-top: 0’ is only applied by browsers that understand ‘transform’:

#menu { margin: -3em 0 0 0 }
@supports (transform: translate(0, -50%)) {
  #menu {
    margin-top: 0;
    transform: translate(0, -50%);
  }
}

That's it! Of course, you can also make menus that slide in from the top or bottom of the window, or indeed from anywhere (from behind another element, e.g.), but we leave that as an exercise.

Bert Bos, style activity lead
Copyright © 1994–2021 W3C® Privacy policy

Created 24 February 2017;
Last updated Wed 06 Jan 2021 05:40:49 AM UTC

Languages

About the translations