Pure CSS Collapsible Left Nav Bar
July 29, 2019
Creating a collapsible left navigation bar was a scary thought when I first did it a while back. Turns out, it's easy! Don't let yourself be frightened by transitions, disappearing text, or other parts. Approach the problem one thing at a time.
Essentially all you need for a generic collapsible navigation bar is this:
- Create a standard left nav bar
- Add a button which can toggle state. i.e. collapsed, not collapsed.
- Add collapsed class to tags when state is collapsed
- For the navbar, use a smaller collapsed width when the collapsed class is present
Here is the styling used for my left navigation bar. The styling is pretty generic and will be applied to a tag with the class left-nav-section
. See the notes about what each property does for you below.
.left-nav-section {
background-color: aquamarine; /* make it easier to see nav */
position: fixed; /* avoids issues with nav bars that arise when it is part of the document flow */
height: 100%; /* take full height of page */
width: 250px; /* default width */
top: 0; /* stick fixed tag to top of the page */
left: 0; /* stick fixed tag to left of the page */
padding: 15px; /* keep children from edges */
/* Collapsible Related Properties! */
white-space: nowrap; /* don't let text wrap */
overflow: hidden; /* don't allow text to extend beyond nav bar */
transition: width 1s ease; /* when width styling changes, transition over a second */
}
and when it is collapsed, we want the width to be changed to a smaller value. The styling below will be applied with a class name left-nav-section collapsed
.
.left-nav-section.collapsed {
width: 75px;
}
I created my left navigation bar using React. There are three parts to this: 1) Parent component 2) Left Nav Bar 3) App Content
Here is my Parent Component:
class App extends Component {
constructor(props) {
super(props);
this.toggleCollapse = this.toggleCollapse.bind(this);
this.state = {
collapsed: false
};
}
toggleCollapse() {
this.setState(prevState => ({
collapsed: !prevState.collapsed
}));
}
render() {
return (
<>
<LeftNav
collapsed={this.state.collapsed}
toggleCollapse={this.toggleCollapse}
/>
<Main collapsed={this.state.collapsed} />
</>
);
}
}
All you really need to take away from this is that there is state collapsed
at the root so that both my app and nav bar can receive the collapsed state via a prop. It's far more likely this will be stored in Redux or some other state management tool, so don't read into having this component too much.
My left nav bar then looks something like this:
const LeftNav = ({ collapsed, toggleCollapse }) => {
const collapsedClass = collapsed ? " collapsed" : "";
return (
<section className={`left-nav-section${collapsedClass}`}>
<p>Link 1</p>
<p>Link 2</p>
<p>Link 3</p>
<button onClick={toggleCollapse}>collapse</button>
</section>
);
};
As you can see, if collapsed
is false
, then the className
will simply be left-nav-section
, but if it is true
, then the className
will be left-nav-section collapsed
. By adding the collapsed, the width will be overridden to 75px
as specified in the CSS above, and the component will transition between states as it is told to do.
There is still one more thing that needs to happen. Because the navigation bar is fixed
, the navigation bar changing in width will not affect the app content; we need the move it directly. The CSS and React code are done very similarly, and look like this:
const Main = ({ collapsed }) => (
<div className={`main${collapsed ? " collapsed" : ""}`}>
<h1>App Content!</h1>