tutos

Let's build a brand context menu

Friday 12th March, 2021 —

About Strapi.io

Strapi is an open-source headless CMS that gives developers the freedom to choose their favorite tools and frameworks and allows editors to manage and distribute their content using their application's admin panel. Based on a plugin system, Strapi is a flexible CMS which admin panel and API are extensible - and which every part is customizable to match any use case. Strapi also has a built-in user system to manage in detail what the administrators and end-users have access to.

What we'll be building

If you visit the Strapi website and try to save their navbar brand logo by clicking on the right side of your mouse, you'll see a custom context menu appear, which provides direct links to download the company's brand assets. When I first discovered this, I immediately thought that's a fantastic UX and wanted to do the same for Css Ninja. Why not learn how you can do it for your own website?


HTML Markup

Let's begin with the html markup we are going to need for our context menu.

<div class="mediakit-box">
  <div class="box-inner">
    <div class="close">
      <i class="material-icons">close</i>
    </div>
    <h3>Looking for our Logo?</h3>
    <img
      src="https://cssninja.io/themes/cssninja/assets/images/mediakit/cssninja-logotype.svg"
    />
    <div class="button-wrap">
      <a
        href="https://cssninja.io/themes/cssninja/assets/images/mediakit/mediakit.zip"
        download
        class="button is-cta is-primary is-curved is-raised is-fullwidth"
      >
        Download MediaKit
      </a>
      <a href="#" class="button is-cta is-curved is-fullwidth">
        See All Assets
      </a>
    </div>
  </div>
</div>

The box is composed of a close button, a heading, an image and a group of 2 buttons. The first button is a download button, the second one is a link button that leads to a details page. Right now, it looks so bad because we still need to add the styles to create a nice looking box.

CSS Styles

We will use some basic SCSS rules to style this box and therefore increase the quality of the experience. Let's paste in some SCSS:

$primary: #7218cc;
$secondary: #7f00ff;
$fade-grey: #ededed;
$muted-grey: #999;
$light-text: #a2a5b9;
$white: #fff;
$sidebar: #1f293d;
$blue-grey: #444f60;
$font-classy: 'Montserrat', sans-serif;
$font: 'Roboto', sans-serif;
$light-box-shadow: -1px 3px 15px 0 rgba(0, 0, 0, 0.06);

.mediakit-box {
  position: fixed;
  top: 20px;
  left: 60px;
  background: $white;
  min-width: 360px;
  border: 1px solid darken($fade-grey, 3%);
  border-radius: 1rem;

  opacity: 0;
  pointer-events: none;
  text-align: center;
  z-index: 11;
  box-shadow: $light-box-shadow;
  transform: translateY(5px) scale(0);
  transition: transform 0.2s, opacity 0.3s;

  &.is-active {
    transform: translateY(0) scale(1);
    opacity: 1;
    pointer-events: all;
  }

  .box-inner {
    position: relative;
    height: 100%;
    width: 100%;
    padding: 3rem;

    .close {
      position: absolute;
      font-size: 2rem;
      top: 6px;
      right: 10px;
      color: $light-text;
      cursor: pointer;
    }
  }

  h3 {
    font-family: $font-classy;
    font-weight: 600;
    font-size: 1.5rem;
    color: $blue-grey;
    margin-bottom: 2.5rem;
  }

  img {
    display: block;
    max-width: 160px;
    margin: 0 auto;
  }

  .button-wrap {
    padding-top: 50px;
    padding-bottom: 0 !important;

    .button {
      min-height: 52px;
      border-radius: 0.5rem;

      &:first-child {
        margin-bottom: 0.75rem;
        background: $primary;
        border-color: $primary;
      }
    }
  }
}

The subtle thing here is to handle the box active and inactive state using a .is-active modifier class like Bulma does. If your navbar is fixed, prefer using position: fixed;, if not prefer absolute positionning: position: absolute;. Since the box is permanently staying in the DOM, we need to disable pointer events (pointer-events: none;) by default so it doesn't conflict with other DOM elements. We're then bringing them back when the box is active: (pointer-events: all;). Good, we have our styled box but it still doesn't show up in the DOM. We are going to need a few lines of javascript to do that.

Javascript

Before we dive into the code, let's review what we need to do. To reproduce the behaviour seen on Strapi.io, we need to tell the browser to replace a right click on the logo by our custom action. In this example, we'll be using jQuery because it is easily understandble by beginners. For those who don't use jQuery, the vanilla version is also available below. Let's add some javascript:

// jQuery Version
$(document).ready(() => {
  $('.brand').on('contextmenu', (e) => {
    e.preventDefault()
    $('.mediakit-box').addClass('is-active')
  })

  $('.mediakit-box .close').on('click', () => {
    $('.mediakit-box').removeClass('is-active')
  })

  $('.mediakit-box').on('mouseleave', () => {
    $('.mediakit-box').removeClass('is-active')
  })
})

// Vanilla Version
document.addEventListener('DOMContentLoaded', () => {
  const brand = document.querySelector('.brand')
  const box = document.querySelector('.mediakit-box')
  const close = document.querySelector('.mediakit-box .close')

  if (brand) {
    brand.addEventListener('contextmenu', (e) => {
      e.preventDefault()
      box.classList.add('is-active')
    })
  }

  if (close) {
    close.addEventListener('click', (e) => {
      e.preventDefault()
      box.classList.remove('is-active')
    })
  }

  if (box) {
    box.addEventListener('mouseout', () => {
      box.classList.remove('is-active')
    })
  }
})

Let's break what is happening in there. First we are listening to the muse right click by using the contextmenu event listener. We are using the preventDefault() to prevent the clicked link to fire it's default behaviour. We're then setting the box we built to the active state. The rest is event more simple. We add to more event listeners to close the box if the user clicks on the close icon or if the mouse leaves the box.

Tutorial Source

See the Pen Brand Context Menu by Driss Chelouati (@cssninjaStudio) on CodePen.

Epilogue

We've did a quick and dirty example of a brand assets download box. While it is fun to build, it is also a great way to improve the UX on your website. You should definitely give it a try. See you soon on Css Ninja!

Back to Blog

Continue reading

Server Cache Invalidation in Nuxt and Nitro

Top 10 Vue Components Libraries

How to build a filterable list with Nuxt and Tailwind