SU-SWS/decanter

View on GitHub
core/src/scss/components/main-nav/_main-nav.scss

Summary

Maintainability
Test Coverage
//
// Main Navigation
//
// The primary site navigation that is usually in the header region of a website.
// It is a horizontal menu bar on larger breakpoints, while on smaller breakpoints,
// it changes into a vertical menu and can be expanded or collapsed by a toggle button.
//
// The following Javascript events are dispatched:
// - `openNav` - fires when a mobile nav is opened - dispatched on `.su-main-nav`
// - `closeNav` - fires when a mobile nav is closed - dispatched on `.su-main-nav`
// - `openSubnav` - fires when a subnav is opened - dispatched on `li.su-main-nav__item--parent`
// - `closeSubnav` - fires when a subnav is closed - dispatched on `li.su-main-nav__item--parent`
//
// The Main Navigation component is implemented to be accessible. For more information on building
// accessible site navigation, please see
// <a href="https://github.com/SU-SWS/decanter/wiki/About-the-Main-Nav-Component">About the Main Nav Component</a>
// on Decanter's wiki.
//
//**Modifier classes for the Main Nav:**
// - .su-main-nav--light          - Light colored theme
// - .su-main-nav--dark           - Light on dark theme (for use on dark background colors).
// Note: The dark background color in below example is just for demo. The actual background is transparent.
// - .su-main-nav--mobile-search  - Include search form inside the mobile nav
// - .su-main-nav--center         - Center both the desktop and mobile nav
// - .su-main-nav--right          - Right justify both the desktop and mobile nav
//
//**Modifier classes for the hamburger toggle button:**
// - .su-main-nav__toggle--center - Center only the toggle
// - .su-main-nav__toggle--right - Right justify only the toggle
//
// .su-main-nav--light          - Light colored theme
// .su-main-nav--dark           - Light on dark theme (for use on dark background colors).
// The dark background color in below example is just for demo. The actual background is transparent.
// .su-main-nav--mobile-search  - Include search form inside the mobile nav
// .su-main-nav--center         - Center both the desktop and mobile nav
// .su-main-nav--right          - Right justify both the desktop and mobile nav
//
// Markup: templates/components/main-nav/main-nav.twig
//
// Style guide: Composite.Main Navigation
//

.su-main-nav {

  @include font-smoothing;
  @include sans;
  display: block;
  position: relative;
  z-index: 9999;

  // General menu styles
  ul {
    @extend %nav-menu;
  }

  li {
    @extend %nav-item;

    a {
      @extend %nav-item-link;
    }
  }

  .su-main-nav__item--expanded {
    @extend %nav-item--expanded;
  }

  .su-main-nav__item--current {
    @extend %nav-item--current;
  }

  .su-main-nav__item--parent {
    @extend %nav-item--parent;

    > a::after {
      @include grid-media('lg') {
        @include margin(null null 1px 6px);
        background: url("#{$su-image-path}/caret-down-black.svg") no-repeat 0 0;
        background-size: 100%;
        position: relative;
        right: 0;
        top: 0;
        height: 11px; // Small svg gets chopped off if use em instead of px
        width: 11px;
        transition: transform 0.3s ease-out;
      }
    }

    &.su-main-nav__item--expanded {
      @extend %nav-item--parent-expanded;

      @include grid-media('lg') {
        > a {
          &::after {
            background: url("#{$su-image-path}/caret-down-black.svg") no-repeat 0 0;
            background-size: 100%;
            transform: rotate(180deg);
          }

          &::before {
            background-color: color(h-border--expanded, $su-nav-colors);
          }

          &:hover::before {
            background-color: color(h-border--hover, $su-nav-colors);
          }

          &:active::before {
            background-color: color(h-border--active, $su-nav-colors);
          }
        }
      }
    }
  }

  .su-main-nav__item--current {
    &.su-main-nav__item--expanded {
      @extend %nav-item--current-expanded;

      @include grid-media('lg') {
        > a {
          &[aria-expanded="true"] {
            &:active {
              &::before {
                background-color: color(h-border--active, $su-nav-colors);
              }
            }
          }
        }
      }
    }
  }

  // Top level only menu styles
  > ul {
    @include grid-media-max('md') {
      @include box-shadow('medium', none);
    }

    > li {
      &:first-of-type:not(.su-main-nav__item--expanded) {
        > a {
          border-top: 0;
        }
      }
    }

    // Top level menu desktop styles
    @include grid-media('lg') {
      @include padding(null null null 0);
      flex-direction: row;
      flex-wrap: wrap;
      background-color: transparent;

      > li {
        > a {
          @include fancy-hover(color(h-border--hover, $su-nav-colors), color(h-border--active, $su-nav-colors));
          @include padding(null 0);
          @include margin(0 1.2em 0 0);
          color: color(h-link, $su-nav-colors);
          transition: color 0.3s ease-out;
          font-size: 2.1rem;
          border-top: 0;

          &:hover,
          &:focus {
            color: color(h-link--hover, $su-nav-colors);
          }

          &:active,
          &[aria-expanded="true"] {
            color: color(h-link--active, $su-nav-colors);
          }

          &[aria-expanded="true"] {
            &:hover {
              color: color(h-link--active-hover, $su-nav-colors);
            }

            &:active {
              color: color(h-link--active, $su-nav-colors);
            }

            &::before {
              visibility: visible;
              transform: scaleX(1);
              background-color: color(h-border--expanded, $su-nav-colors);
            }
          }
        }

        &:last-child > a {
          @include margin(null 0 null null);
        }
      }

      > .su-main-nav__item--current {
        > a {
          &::before {
            background-color: color(h-border--active, $su-nav-colors);
          }

          &:hover,
          &:focus {
            &::before {
              left: 0;
              background-color: color(h-border--active-hover, $su-nav-colors);
              transition: background-color 0.3s ease-out;
            }
          }

          &:active::before {
            background-color: color(h-border--active, $su-nav-colors);
          }
        }

        &.su-main-nav__item--expanded > a {
          &:focus::before {
            background-color: color(h-border--active, $su-nav-colors);
          }

          &[aria-expanded="true"] {
            &:hover::before {
              left: 0;
            }
          }
        }
      }

      > .su-main-nav__item--expanded {
        > a:focus::before {
          background-color: color(h-border--expanded, $su-nav-colors);
        }

        > a[aria-expanded="true"] {
          &:hover::before {
            background-color: color(h-border--hover, $su-nav-colors);
          }

          &:active::before {
            background-color: color(h-border--expanded, $su-nav-colors);
          }
        }
      }
    }
  }

  // When js is disabled, submenus is visible when parent trigger is hovered
  &.no-js {
    //Expand everything on mobile
    @include grid-media-max('md') {
      .su-main-nav__toggle[aria-expanded="false"] + .su-main-nav__menu-lv1 {
        display: flex;
      }

      li > ul {
        display: flex;
      }
    }

    // On desktop, if mouse user hovers over a parent menu item,
    // submenu is expanded
    @include grid-media('lg') {
      li:hover > ul {
        display: flex;
      }
    }
  }
}

.su-main-nav__toggle {
  $_su-main-nav-color-map: $su-nav-colors;

  @include grid-media-max('md') {
    @include fancy-hover(color(h-border--hover, $su-nav-colors), color(h-border--active, $su-nav-colors));
    @include padding(0 0 2rem);
    @include margin(0);
    display: flex;
    flex-direction: column-reverse;
    align-items: center;
    outline: none;
    width: 40px;
    background-color: transparent;
    color: color(h-link, $su-nav-colors);
    font-size: 1.6rem;
    line-height: 0.7;

    &::after {
      @include margin(0 auto);
      display: inline-block;
      width: 30px;
      height: 26px;
      background: url("#{$su-image-path}/hamburger-black.svg") no-repeat 3px 0;
      content: "";
    }

    &[aria-expanded="true"] {
      &::before {
        visibility: visible;
        transform: scaleX(1);
        background-color: color(h-border--expanded, $su-nav-colors);
      }

      &::after {
        width: 22px;
        background: url("#{$su-image-path}/close-black.svg") no-repeat 3px 0;
        background-size: 16px 16px;
      }

      &:hover::before {
        background-color: color(h-border--hover, $su-nav-colors);
      }

      &:active::before {
        background-color: color(h-border--active, $su-nav-colors);
      }
    }

    &:hover,
    &:active,
    &:focus {
      background-color: transparent;
      color: color(h-link, $su-nav-colors);
      box-shadow: none;
    }

    &[aria-expanded="false"] + .su-main-nav__menu-lv1 {
      display: none;
    }

    &[aria-expanded="true"] + .su-main-nav__menu-lv1 {
      position: absolute;
    }

    // Toggle button variants
    &--center {
      @include margin(null auto);
    }

    &--right {
      @include margin(null 0 null auto);
    }
  }

  @include grid-media-only('md') {
    &[aria-expanded="true"]  + .su-main-nav__menu-lv1 {
      max-width: 24em;
    }
  }

  @include grid-media('lg') {
    display: none;
  }
}

// 2nd level menu
.su-main-nav > ul > li > ul {
  @include padding(null null null 2rem);

  // Desktop dropdown menu
  @include grid-media('lg') {
    @include padding(1px null null 1.2rem);
    @include margin(null null null -1.8rem);
    @include box-shadow('medium', none);
    z-index: 11111; // When menu wrap to 2nd line make sure dropdown covers up top level links
    position: absolute;
    max-width: 30rem;

    li:first-child {
      a {
        border-top: 0;
      }
    }
  }
}