I started learning React in general about a year back through work. What makes React so interesting to me is how easy it is to get off the ground with high-quality libraries like Material UI. As a relative newcomer to web development, it felt great to be able to make sites with high quality, and polished components without having to really understand much about CSS more than a basic knowledge of Flexbox. Adding a drawer to any app can really be as easy as going to the Material UI documentation for drawers, copying the example and rolling on with your day. Because of this, it took me a while to realize just how easy it is to accomplish similarly slick UI elements with just React and CSS. No dependencies needed.

In this exercise, I will be implementing the code with a few style principles:

All functions will be declared as constant function expressions. I like the immutable nature of declaring my functions in this manner.

All functions are written with arrow style. I just like this. Sorry if you don’t, but it can be easy to refactor out if you feel strongly enough.

All components are written as functional components. With React Hooks, there has never been more of a reason to write functional components. Here is an excellent article outlining some pros of this approach.

CSS class names will be written in the BEM methodology. This is nothing more than a way of defining styling blocks to avoid entering an unpredictable cascading style hell in more complex systems. In this case, just assume that I’m being needlessly verbose. Don’t worry, all the underscores and dashes do not correspond to any CSS language specific magic.

While I won’t cover testing in this exercise, I have found these practices to be invaluable in helping me write concise, testable code at my day job. Here is a starter project that you can use to follow along if you would like. Without further ado, let’s get into it!

Part 1: Toggle Button

The first thing I’m going to do is to create and wire up a simple button we can use to toggle the drawer. A button is actually really simple when you break it down. It just needs to be a clickable element that triggers some function when clicked. Something like this satisfies what we need:

const DrawerButton = ({ onClick }) => (

<div className="DrawerButton_Container" onClick={onClick}>

Toggle Drawer

</div>

); const App = () => {

const toggleDrawer = () => alert('You clicked the button!');

return (

<div className="App__Container">

<DrawerButton onClick={toggleDrawer} />

</div>

);

};

If you’re following along from the starter project, you probably have a pretty ugly piece of text right now with nothing to indicate that it is a button, but when you click it, an alert should pop up indicating it’s functional. Let’s just apply some simple styles to it to give it a little more indication.

.DrawerButton_Container {

height: 50px;

width: 100px;

background-color: #225950;

border-radius: 10px;

display: flex;

align-items: center;

justify-content: center;

}

This set of styling indicates a few things. It specifies dimensions for the button, some background color, and uses flexbox to center the text.

Now that we have a button, we can hook up some state to it! With React Hooks, it is easier than ever to do this step. All we need to use is useState to accomplish what we need.

const App = () => {

const [isOpen, setIsOpen] = React.useState(false);

const toggleDrawer = () => setIsOpen(!isOpen); return (

<div className="App__Container">

<div>{isOpen ? "Im open" : "Im closed"}</div>

<DrawerButton onClick={toggleDrawer} />

</div>

);

};

Here we define toggleDrawer to be a function that sets the isOpen value to be the opposite of what it is currently set to. In order to see this variable’s state change, we have a simple div block that contains a ternary which returns either the string “Im open” when isOpen is true, or ‘Im closed’ when it’s false. At this point, these changes should look like this:

If you have somehow gone astray no worries, here is a codepen for this section. If not, congrats! You have essentially finished the bulk of the logic needed for the drawer. All that’s left is to generate the drawer component itself.

Part 2: The Drawer

Just like before, lets first break down what the drawer needs to be. It should be some container that has two states: open and closed. Inside it we can have a container for contents. Simple enough. From this we can write something like:

const DrawerContents = () => (

<div className="DrawerContents__Container">Hi Im the drawer contents</div>

); const Drawer = ({ isOpen }) => (

<div className={`Drawer__Container ${isOpen && "Drawer__Container--isOpen"}`}>

<DrawerContents />

</div>

);

And wire it up in our App component with <Drawer isOpen={isOpen} />

Now a few things are happening here. Drawer renders the component DrawerContents inside. The juicy part is how Drawer uses the isOpen prop. While there are libraries that do this part, the actual operation is quite simple.

`Drawer__Container ${isOpen ? "Drawer__Container--isOpen" : ''}`

Is a template literal string that uses the isOpen prop in another ternary operation (if you recall that from part 1). In this case, it will add the class name Drawer__Container--isOpen to the Drawer class name when isOpen is true, otherwise it’s false and will return an empty string instead.

This is fundamentally the same as the end of part 1, except with more steps. Sure if you open dev tools and inspect the drawer element as you click the button, you can see the Drawer class name update, but that’s no fun, so let’s make it do something.

With just a few simple lines of CSS, we can see the drawer brought to life.

.Drawer__Container {

height: 100vh;

width: 200px;

background-color: orange;

transition: all 0.3s ease-in-out;

transform: translate(-200px);

position:absolute;

} .Drawer__Container--isOpen {

transform: translate(0);

}

We assign the drawer container some width and height dimensions as before, as well as a color to distinguish it from everything else. Then we declare the transition property with some shorthand values that map to:

[transition-property] [transition-duration] [transition-timing-function]

Basically saying “For all properties that change, perform a transition over the course of 0.3 seconds using the default ease in and ease out effect (slower at the start and finish). Then we transform the container to be 200px off-screen to the left. Notice that this is the exact width of the Drawer. We do this because we are using the Drawer__Container class name to describe the state of the Drawer when it is closed. In this case, we want it to be just off-screen, waiting patiently.

Additionally, we will set the position of the Drawer to be fixed. What this means is that basically it stands separate from what’s on the document and will not disrupt any other elements when it moves around. It is simply a fixed element that will sit in its spot and not bother anyone else.

From there, all we need is to set how the Drawer should appear when it is open. In this case, we just want to transform it onto the screen (ie: transform it to its original 0px moved position).

And that’s it for this part! You should have something that behaves like this:

If you got messed up along the way, here is the completed code for this section. What we have achieved at this point is a react component that manages a simple true/false state to toggle between two different CSS animation states. With the transition property, we let CSS take the wheel and drive the animations smoothly between different class properties. You could end this here, but I think we can do a little bit better.

Part 3: Polish

The code up to part 2 should hopefully show you how you can use a lot of simple fundamentals and chain them together to create something pretty neat. However, this code in this part isn’t quite what you would expect an actual use case to look like. For example: Real pages has content on the page. Let’s create that:

const PageContents = () => (

<div className="PageContents__Container">

<p>

Sed nulla cred Banksy jean shorts. Reprehenderit mumblecore incididunt

anim accusamus. Keffiyeh Cosby sweater in cornhole elit, tote bag cillum

banjo. Shabby chic YOLO banh mi sunt. Artisan blog Neutra, polaroid

adipisicing Banksy +1 lo-fi umami distillery fixie seitan. Semiotics

artisan flannel mollit craft beer Blue Bottle. Bespoke biodiesel banh mi

literally.

</p> <p>

YOLO Marfa drinking vinegar polaroid laborum veniam, est disrupt umami

wayfarers try-hard before they sold out fap. Culpa bitters kitsch

adipisicing, nesciunt brunch squid minim post-ironic assumenda irony. Qui

bespoke organic, photo booth authentic PBR velit before they sold out

fingerstache nostrud flannel. Leggings chillwave est sustainable, tofu raw

denim sint 8-bit keffiyeh High Life. Nostrud Austin put a bird on it,

typewriter Godard gastropub ennui mlkshk deserunt assumenda. Blog

letterpress meggings, nihil Shoreditch Etsy try-hard vinyl Echo Park cray

farm-to-table McSweeney's High Life. Qui stumptown ex Cosby sweater street

art ethnic irure dolore.

</p>

</div>

); const App = () => {

const [isOpen, setIsOpen] = React.useState(false);

const toggleDrawer = () => setIsOpen(!isOpen); return (

<div className="App__Container">

<div className="App__TitleBar">

<DrawerButton onClick={toggleDrawer} />

</div>

<Drawer isOpen={isOpen} />

<PageContents />

</div>

);

};

This is just some gibberish I found on the internet, but it serves the purpose of being our page content. We then clean up the app container a bit to remove all the debug fluff accumulated along the way and nested the drawer button in a simple bar container.

So now with just some simple changes to our page, we have a working (albeit ugly as sin) representation of how this drawer would act on a real page. Now, that we got all the React components set up, all we need is to add a little bit of CSS to doll this bad boy up. I’m not the best at picking out color palettes but luckily there are a ton of results when you google ‘color palettes’. I will pick an arbitrary result and apply those colors to this project. Additionally, let’s make the title bar container actually be a title bar.

.App__Container {

font-family: "Roboto Condensed", sans-serif;

background-color: #1f3c88;

height: 100vh;

width: 100vw;

} .App__TitleBar{

width: 100vw;

background-color: #5893d4;

padding: 5px;

} .DrawerButton_Container:active {

transform: translateY(5px);

} .DrawerButton_Container {

height: 50px;

width: 100px;

background-color: #5893d4;

border-radius: 10px;

border-style: solid;

border-width: 2px;

border-color: #1f3c88;

display: flex;

align-items: center;

justify-content: center;

} .Drawer__Container {

height: 100vh;

width: 200px;

background-color: #f7b633;

transition: all 0.3s ease-in-out;

transform: translate(-200px);

position:fixed;

box-shadow: 5px 0px 18px rgb(0, 0, 0, 0);

} .Drawer__Container--isOpen {

transform: translate(0);

box-shadow: unset;

box-shadow: 5px 8px 18px rgb(0, 0, 0, 0.7);

} .PageContents__Container {

color: #d8d8d8;

padding: 10px;

}

The main changes here are:

Updated a bunch of colors

Added a border to the button

Added padding to the page contents

Added a box shadow to the Drawer__Container and open state. Basically to turn the transparency of the black border to full (ie: completely transparent) when closed, and slightly opaque when the drawer is open. This will give us a sort of depth effect.

Here’s what we have after making these simple CSS changes.

It looks a lot better, but there is something missing. Looks like we missed adding in some items to the drawer. Let’s fix that. On the React side, we just need to add some items like so:

const DrawerItem = ({ label }) => (

<div className="DrawerItem__Container noSelect">{label}</div>

); const DrawerContents = () => (

<div className="DrawerContents__Container">

<DrawerItem label="Item 1" />

<DrawerItem label="Item 2" />

<DrawerItem label="Item 3" />

<DrawerItem label="Item 4" />

</div>

);

and in CSS

.DrawerItem__Container {

font-size: 2em;

padding-top: 10px;

padding-left: 20px;

color: black;

transition: all 0.1s ease-in-out;

} .DrawerItem__Container:hover {

color: white;

}

Again, virtually everything here is similar to the fundamentals covered in part 1. The only new thing is that I am introducing the pseudo selector for hover state when the mouse hovers over the element. In this case, we set the color of the text to white when the mouse hovers. Now if we look at what we have, we can see the final result:

If you got lost in this part, here is the final code for it. What’s great about this is that there is so much more you can do to make your own version pop. In a relatively short span of time, we covered so many fundamentals that can be reapplied to make this project more functional and interesting.

For one thing, we didn't even make any of the items clickable, but from part 1, you have a pattern to do so.

You could make the button translate downwards when pressed with something like:

.DrawerButton_Container:active {

transform: translateY(5px);

}

Hopefully, this exercise has offered some new perspective on how easy it can be to leverage CSS to drive really fluid animations. The point of this article is not to discourage using polished third-party libraries in your projects, but to show how powerful you already are with nothing more than React and CSS at your disposal.