I’m trying to create a simple page with a menu on one side of the main content. Now I want to make the menu collapsible.
As a proof of concept, consider the following example:
#container {
display: flex;
position: fixed;
height: 100%;
}
#menu {
background: yellow;
}
#toggle {
cursor: pointer;
user-select: none
}
#menu.collapsed .content {
display: none;
}
<div id="container">
<div id="menu">
<div id="toggle" onclick="document.getElementById('menu').classList.toggle('collapsed')">X</div>
<div class="content">Menu content</div>
</div>
<div class="content">Main content goes here</div>
</div>
You can click the X to toggle the menu, which works as intended, but the transition is instant which feels jarring. I would like to make it feel smoother by slowly closing the menu.
One way I found to make it work is by setting an explicit width
on the menu and using a transition: width
property. The stylesheet becomes:
This sort of works, as you can see here:
#container {
display: flex;
position: fixed;
height: 100%;
}
#menu {
background: yellow;
}
#toggle {
cursor: pointer;
user-select: none
}
#menu.collapsed .content {
overflow: hidden;
}
#menu .content {
transition: width 1s;
width: 100px;
}
#menu.collapsed .content {
width: 0px;
}
<div id="container">
<div id="menu">
<div id="toggle" onclick="document.getElementById('menu').classList.toggle('collapsed')">X</div>
<div class="content">Menu content</div>
</div>
<div class="content">Main content goes here</div>
</div>
I don’t really like this solution for the following reasons (in decreasing importance):
-
I have to guess the initial width (
100px
in this example). I would rather that the initial width is derived from the natural width of the content, as happened before. But I can’t just removewidth: 100px
because then the transition no longer works. Andwidth: 100%
doesn’t seem to work correctly either. -
While the menu is closing, the text reflows, which looks a little ugly. I’d like to prevent this if possible.
-
Keeping the menu in the DOM tree with
width: 0px
might be less efficient thandisplay: none
because the browser still has to render it (maybe???)
Is there a better way to animate the closing/opening of the menu?
(I found a related question here: Collapsible flexible-width sidebar using only CSS. That one uses max-width
instead of width
, which sort of solves issue 1, but it introduces a new problem, namely that if max-width
is high, it effectively adds a delay to the transition animation.)
2
Answers
To animate the sidebar, you can use a combination of
transition: width;
,position: fixed
, andwhite-space: nowrap; overflow: hidden;
to get a pretty convincing result:If you don’t want to set a fixed width for the menu for some reason, then you need to do a lot of manipulation in js. And they will be directed specifically to set the menu width )) Here is an example that does not take into account adaptive resizing, but can use you as a start point: