Sliding Side Panel Menu (Vue)

Chronicles of Chris
4 min readOct 20, 2020

The Stats

Feelings

  • Before: Good
  • During: Bad
  • After: Fine

Time

  • Estimated: >1 morning (3 work hours)
  • Actual: ~4 hours

Imposter Syndrome Check: Negative

The Background

I am making an Instagram-like website for my family. In desktop view, all of the family’s names are clickable links in the navbar. In mobile view, that won’t fly.

I wanted to find a mobile-friendly solution. I’d played with the idea of a drop down menu, but I found my eventual solution in this article: A slide-in menu panel.

The Feeling

The solution involves a lot of copying and pasting of content that I don’t truly understand. Therein lies the root of my imposter syndrome acting up: yeah I solved it, kinda. But it is only partially mine, kinda. And it could be better, kinda.

But here’s also where I realised that kinda-finished features are actually a way to treat yourself. “Perfectionism is unhealthy” is all to easy to say and not too easy to believe. You lie to yourself a feature is “good enough” but also think back on it later and how you could’ve done it better, without actually internalising that it really is good enough and so are you. I was getting increasingly frustrated with myself for not understanding all of the code, like my ability to know why it transitions in but not out is a measure of my value as a developer. But I came to understand that the feature serves its purpose, my family (for whom I’m making the website) are going to see the value in it, and I’m already creating way more than I’d ever dreamed on January 1st 2020.

Cut yourself some slack.

The Solution

The solution, as mentioned, was to copy in a lot of stuff from the above pasted article. I did, however, choose to do a few things differently.

Vuex instead of observable

The title of the article, (How to create an animated Vue Sidebar menu with Vue.Observable) drew me in because I had no experience with Vue.Observable and wanted to see what it’s all about. Upon further reading, it seems like it’s the Vue version of React Context, which is unnecessary in this project as I’ve already set up a global state with vuex. The author uses Vue.Observable to maintain a global isNavOpen prop, that is toggled by the user clicking the menu button or clicking outside of the frame. Instead, I created functions in my store.js to handle this functionality.

// store.jsstate: {
isMobileNavOpen: false,
},
// allows component to get the state prop
getters: {
nav(state) {
return state.isMobileNavOpen;
},
},
// mutate state (obviously)
mutations: {
TOGGLE_MOBILE_NAV(state) {
state.isMobileNavOpen = !state.isMobileNavOpen;
},
},
// allows component actions to trigger mutations
actions: {
toggleNav({ commit }) {
commit("TOGGLE_MOBILE_NAV");
},
},

Burger component? No thanks.

The author works with a pretty damn fancy animated burger icon. I wanted to keep things streamlined, and merely set a menu button on my navbar with a close toggle in the panel. Taking the “easy way out”, serving the purpose, and protecting your sanity does not equal failure, kids.

// NavBar.vue
// template
<div
class="side-panel-button div-button"
@click.prevent="toggleSidePanel">
Menu
</div>
// script
methods: {
toggleSidePanel() {
store.dispatch("toggleNav")
}
},
computed: {
...mapGetters({
isMobileNavOpen: "nav"
}),
},

RIP Slots

Man, I wanted to use slots so bad… I’d never encountered them before, and when I saw how powerful they could be when playing around with hard-coded values, I absolutely loved them. But I could. not. make. them. dynamic. This took a huge bulk of my 4 hours, and I do still feel like I gave up a little too quickly. Maybe I’ll revisit them another time, but I’ve got other stuff to work on and I want to be done with this project by the end of the month, so I went with a fallback method that still 100% works and 100% looks great, but just isn’t quite as… nifty.

All of the styling, transitions etc. were copied in directly from the article. Most of the template was too. The only things I changed were using the family prop I sent in from NavBar.vue and iterating over it to generate links, plus the close button with the same toggle function from store.js.

// SidePanel.vue<transition name="slide">
<div v-if="isMobileNavOpen" class="side-panel-container">
<h3 v-for="member in family"
:key="member.name"
@click.prevent="toggleSidePanel">
<router-link :to="member.route" class="link-text">
{{ member.name }}
</router-link>
<hr/>
</h3>
<br/>
<br/>
<div @click.prevent="toggleSidePanel"
class="close-panel div-button">Close
<md-icon>close</md-icon>
</div>
</div>
</transition>

And that’s pretty much all there is to it. Like I said: copy-and-paste + not fully understanding the transitions + 0 slots = an unavoidable feeling that this victory has not been earned. Then again, I highly doubt any field marshal ever caught a lucky tactical break and let themselves feel down about not “earning” the victory. I’m quite sure my family are going to be overjoyed even without a fancy sliding menu, and I will be even if they’re not. It still looks pretty slick, I must say.

--

--