<div class="bux-menu-wrapper">
    <div class="bux-container bux-grid bux-container__menu bux-container__menu--with-search ">
        <nav id="bux-main-menu" aria-label="Main" class="disclosure-nav disclosure-nav-orientation-horizontal">
            <ul class="bux-menu">
                <li class="bux-menu__item">

                    <a class="bux-menu__link bux-menu__link--active disclosure-nav-item-with-submenu bux-menu__link bux-menu__link--active disclosure-nav-item-with-submenu--with-search" href="#" rel="noopener" data-button-aria-label="More Level 1 Link Items">
                        Level 1 Link
                    </a>
                    <ul class="disclosure-nav-submenu disclosure-nav-submenu-closed">
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--active disclosure-nav-item-with-submenu mobile-depth-cutoff bux-menu__link bux-menu__link--active disclosure-nav-item-with-submenu mobile-depth-cutoff--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                            <ul class="disclosure-nav-submenu disclosure-nav-submenu-closed mobile-visible">
                                <li class="bux-menu__item">

                                    <a class="bux-menu__link bux-menu__link--active disclosure-nav-item-with-submenu bux-menu__link bux-menu__link--active disclosure-nav-item-with-submenu--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                        Level 3 Link
                                    </a>
                                    <ul class="disclosure-nav-submenu disclosure-nav-submenu-closed">
                                        <li class="bux-menu__item">

                                            <a class="bux-menu__link disclosure-nav-item-with-submenu bux-menu__link disclosure-nav-item-with-submenu--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                                Level 4 Link
                                            </a>
                                            <ul class="disclosure-nav-submenu disclosure-nav-submenu-closed">
                                                <li class="bux-menu__item">

                                                    <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                                        Level 5 Link
                                                    </a>
                                                </li>
                                                <li class="bux-menu__item">

                                                    <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                                        Level 5 Link
                                                    </a>
                                                </li>
                                            </ul>

                                        </li>
                                        <li class="bux-menu__item">

                                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                                Level 4 Link
                                            </a>
                                        </li>
                                    </ul>

                                </li>
                                <li class="bux-menu__item">

                                    <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                        Level 3 Link
                                    </a>
                                </li>
                            </ul>

                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                    </ul>

                </li>
                <li class="bux-menu__item">

                    <a class="bux-menu__link disclosure-nav-item-with-submenu bux-menu__link disclosure-nav-item-with-submenu--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                        Level 1 Link
                    </a>
                    <ul class="disclosure-nav-submenu disclosure-nav-submenu-closed">
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                    </ul>

                </li>
                <li class="bux-menu__item">

                    <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                        Level 1 Link
                    </a>
                </li>
                <li class="bux-menu__item">

                    <a class="bux-menu__link disclosure-nav-item-with-submenu bux-menu__link disclosure-nav-item-with-submenu--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                        Level 1 Link
                    </a>
                    <ul class="disclosure-nav-submenu disclosure-nav-submenu-closed">
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                        <li class="bux-menu__item">

                            <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                                Level 2 Link
                            </a>
                        </li>
                    </ul>

                </li>
                <li class="bux-menu__item">

                    <a class="bux-menu__link bux-menu__link--with-search" href="#" rel="noopener" data-button-aria-label="More Stuff">
                        Level 1 Link
                    </a>
                </li>
            </ul>
        </nav>
        <div class="bux-menu__search">
            <div class="bux-form__text-field--menu-search__wrapper" role="dialog" aria-modal="true">
                <form id="site-search" class="bux-search" role="search">
                    <label class="bux-search__label visually-hidden" for="buxSearch">Search</label>
                    <input id="buxSearch" class="bux-search__input bux-form__text-field bux-from__text-field--menu-search" placeholder="Search" tabindex="-1" />
                    <button class="bux-search__submit is-hidden" tabindex="-1">
                        <span class="visually-hidden">Submit search</span>
                    </button>
                </form>
            </div>
            <button id="menuSearchBtn" type="button" aria-expanded="false" aria-controls="site-search">
                <span class="visually-hidden">Toggle search dialog</span>
                <span id="searchIcon" class="icon icon-search" aria-hidden="true"></span>
            </button>
        </div>
    </div>
</div>
{#

Menu Component

Available Variables:

- modifier:                     Menu variant (null or "with-search")
- nav_id:                       Unique identifier for the menu
- aria_label:                   Aria label for the menu
- mobileDepthCutoff:            Boolean. Hide deep menu levels on desktop
- items:                        Array of menu items
- item.title:                   Item title
- item.url:                     Item URL
- item.active:                  Boolean. True if active
- item.aria_label:              Aria label for the disclosureNav generated button
- item.subitems:                Array of submenu items

If using the search variant:
- label:                        Set to "with-search"
- inputId:                      Unique ID for the search input
- searchFormId:                 Unique ID for the search form

#}


{# ===================================================
                      Macros
==================================================== #}

{% macro link(item, depth, mobileDepthCutoff) %}
    {% set linkClasses = ['bux-menu__link'] %}
    {% if item.active %}
        {% set linkClasses = linkClasses|merge(['bux-menu__link--active']) %}
    {% endif %}
    {% if item.subitems %}
        {% set linkClasses = linkClasses|merge(['disclosure-nav-item-with-submenu']) %}
    {% endif %}
    {% if item.subitems and mobileDepthCutoff and depth == 1 %}
        {% set linkClasses = linkClasses|merge(['mobile-depth-cutoff']) %}
    {% endif %}
    {% embed '@link' with {
        base_class: linkClasses|join(' '),
        link_url: item.url,
        attributes: {
            'data-button-aria-label': item.aria_label
        }
    } %}
        {% block content %}
            {{ item.title }}
        {% endblock %}
    {% endembed %}
{% endmacro %}


{% macro submenu(items, depth, mobileDepthCutoff) %}
    {% import _self as menu %}
    {% if items %}
        {% set submenuClasses = ['disclosure-nav-submenu', 'disclosure-nav-submenu-closed'] %}
        {% if mobileDepthCutoff and depth == 2 %}
            {% set submenuClasses = submenuClasses|merge(['mobile-visible']) %}
        {% endif %}
        <ul class="{{ submenuClasses|join(' ') }}">
            {% for item in items %}
                <li class="bux-menu__item">
                    {{ menu.link(item, depth, mobileDepthCutoff) }}
                    {% if item.subitems %}
                        {{ menu.submenu(item.subitems, depth + 1, mobileDepthCutoff) }}
                    {% endif %}
                </li>
            {% endfor %}
        </ul>
    {% endif %}
{% endmacro %}


{# ===================================================
                      Markup
==================================================== #}

{% import _self as menu %}

<div class="bux-menu-wrapper">
    <div class="bux-container bux-grid bux-container__menu {% if modifier %}bux-container__menu--{{ modifier }} {% endif %}">
        <nav id="{{ nav_id }}" aria-label="{{ aria_label }}" class="disclosure-nav disclosure-nav-orientation-horizontal">
            {% if items %}
                <ul class="bux-menu">
                    {% for item in items %}
                        <li class="bux-menu__item">
                            {{ menu.link(item, 0, mobileDepthCutoff) }}
                            {% if item.subitems %}
                                {{ menu.submenu(item.subitems, 1, mobileDepthCutoff) }}
                            {% endif %}
                        </li>
                    {% endfor %}
                </ul>
            {% endif %}
        </nav>
        {% if label == 'with-search' %}
            <div class="bux-menu__search">
                <div class="bux-form__text-field--menu-search__wrapper" role="dialog" aria-modal="true">
                    <form id="{{ searchFormId }}" class="bux-search" role="search">
                        <label class="bux-search__label visually-hidden" for="{{ inputId }}">Search</label>
                        <input id="{{ inputId }}" class="bux-search__input bux-form__text-field bux-from__text-field--menu-search" placeholder="Search" tabindex="-1" />
                        <button class="bux-search__submit is-hidden" tabindex="-1">
                            <span class="visually-hidden">Submit search</span>
                        </button>
                    </form>
                </div>
                <button id="menuSearchBtn" type="button" aria-expanded="false" aria-controls="{{ searchFormId }}">
                    <span class="visually-hidden">Toggle search dialog</span>
                    <span id="searchIcon" class="icon icon-search" aria-hidden="true"></span>
                </button>
            </div>
        {% endif %}
    </div>
</div>
site_name_prefix: Office of
site_name: Learning Relations Excellence
site_slogan: Additional text or site slogan
address_1: 100 Building Name
address_2: 1 Oval Mall
city: Columbus
state: OH
zip: '43210'
contact_email: email@osu.edu
contact_phone: 614-292-OHIO
contact_tty: 614-688-8605
nav_id: bux-main-menu
aria_label: Main
mobileDepthCutoff: true
items:
  - title: Level 1 Link
    url: '#'
    active: true
    aria_label: More Level 1 Link Items
    subitems:
      - title: Level 2 Link
        url: '#'
        active: true
        aria_label: More Stuff
        subitems:
          - title: Level 3 Link
            url: '#'
            active: true
            aria_label: More Stuff
            subitems:
              - title: Level 4 Link
                url: '#'
                active: false
                aria_label: More Stuff
                subitems:
                  - title: Level 5 Link
                    url: '#'
                    active: false
                    aria_label: More Stuff
                  - title: Level 5 Link
                    url: '#'
                    active: false
                    aria_label: More Stuff
              - title: Level 4 Link
                url: '#'
                active: false
                aria_label: More Stuff
          - title: Level 3 Link
            url: '#'
            active: false
            aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
  - title: Level 1 Link
    url: '#'
    active: false
    aria_label: More Stuff
    subitems:
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
  - title: Level 1 Link
    url: '#'
    active: false
    aria_label: More Stuff
  - title: Level 1 Link
    url: '#'
    active: false
    aria_label: More Stuff
    subitems:
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
      - title: Level 2 Link
        url: '#'
        active: false
        aria_label: More Stuff
  - title: Level 1 Link
    url: '#'
    active: false
    aria_label: More Stuff
label: with-search
modifier: with-search
inputId: buxSearch
searchFormId: site-search
  • Content:
    @mixin transition-specs {
      transition: all 0.3s ease-in-out;
    }
    
    div.bux-container__menu,
    div.bux-container--menu { //backwards compatibility
    
        position: relative;
        z-index: 999;
    
      nav.disclosure-nav {
        &.disclosure-nav-closed {
          display: none;
        }
    
        button.disclosure-nav-button {
          background-color: transparent;
          border: none;
          padding: 0;
    
          &::before,
          &::after {
            font-family: 'bux-icons';
            color: $gray-dark-60;
            position: absolute;
            top: -1px;
            right: -4px;
            opacity: 0;
            @include transition-specs;
          }
    
          &::before { content: "\f010"; /* down arrow */  opacity: 1; } 
          &::after  { content: "\f011"; /* up arrow */    opacity: 0; }
    
          &[aria-expanded='true'] {
            &::before { opacity: 0; }
            &::after  { opacity: 1; }
          }
    
          &[aria-expanded='false'] {
            &::before { opacity: 1; }
            &::after  { opacity: 0; }
          }
      }    
    
        a, span {
          color: $gray-dark-80;
          text-decoration: none;
        }
    
        ul, ol {
          font-family: $sans;
          font-size: $ts-base;
          font-weight: 700;
    
          &.disclosure-nav-submenu-open {
            display: block;
          }
    
          &.disclosure-nav-submenu-closed {
            display: none;
          }
        }
    
        &.disclosure-nav-orientation-horizontal {
    
          & > ul > li {
            display: inline-flex;
            position: relative;
            margin: $sp-12 $sp-16 0;
    
            &:hover {
              a.bux-menu__link,
              span.bux-menu__link,
              button.disclosure-nav-button-no-link {
                border-bottom-color: $gray-dark-40;
    
                &:has(.bux-menu__link--active) {
                  border-bottom-color: $scarlet;
    
                  & + button {
                    border-bottom-color: $gray-dark-40;
                  }
                }
    
                & + button {
                  border-bottom-color: $gray-dark-40;
                }
              }
            }
    
            & > a,
            & > span {
              &.bux-menu__link {
                float:inline-block;
                padding: 0 0 $sp-4 0;
                color: $gray-dark-80;
                text-decoration: none;
                white-space: nowrap;
    
                &.bux-menu__link--active {
                  border-bottom: 4px solid $scarlet;
    
                  & + button {
                  border-bottom: 4px solid $scarlet;
                  }
                }
    
                & + button {
                  padding-right: $sp-16;
    
                }
              }
            }
    
            ul {
              border: 2px solid $gray-light-80;
              background-color: $white;
              border-top: none;
              padding-top: $sp-8!important;
              
              ul {
                padding-top: 0 !important;
              }
            }
    
            li {
              display: block;
              width: 100%;
              margin: 0;
              padding: 0;
    
              a, span, button.disclosure-nav-button-no-link {
                display: block;
                padding: $sp-12 $sp-64 $sp-12 $sp-12;
                background-color: $white;
                white-space: normal;
                border-bottom: none;
                border-left: $sp-4 solid $clear;
    
                &:hover, &:focus {
                  border-left-color: $gray-dark-60;
                  background-color: $gray-light-90;
                }
    
                &.bux-menu__link--active {
                  border-bottom-color: $clear;
                  border-left-color: $scarlet;
                }
              }
            }
    
            ul {
              position: absolute;
              left: 0;
              top: 2rem;
              width: 15rem;
              padding: 0;
              padding-top: $sp-8;
              margin-top: 2px;
    
              ul {
                top: 0;
                left: 100%;
                padding: 0;
                margin-top: -2px;
              }
            
              li {
                  position: relative;
              }
              button {
                float: right;
    
                &.disclosure-nav-button-no-link {
                  float: none;
                  text-align: left;
                  width: 100%;
                }
    
                &::after {
                  position: absolute;
                  top: rem-calc(8);
                  right:0;
                  left:auto;
                }
    
                &[aria-expanded=true], &[aria-expanded=false] {
                  &::after {
                    content: "\f111";
                    font-size: rem-calc(20);
                    padding-right: $sp-8;
                    opacity: 1;
                  }
                  &::before {
                    content: "";
                  }
                }
                &:focus {
                  &[aria-expanded=true], &[aria-expanded=false] {
                    outline: none;
    
                    &::after {
                      outline: 2px solid $focus;
                      outline-offset: -2px;
                    }
                  }
                }
              }
            }
          }
        }
    
        &.disclosure-nav-orientation-vertical {
    
        background-color: $white;
          ul {
            background-color: $white;
    
            li {
              position: relative;
              margin: $sp-12 $sp-16 0 $sp-16;
            }
            
            a, span, button.disclosure-nav-button-no-link {
              display: block;
              border-bottom: none;
              white-space: normal;
              border-left: $sp-4 solid $clear;
              padding: $sp-12;
    
              &.disclosure-nav-item-with-submenu {
                padding-right: 2.45rem;
              }
        
              &:hover, &:focus {
                color: $gray-dark-80;
                border-left-color: $gray-dark-60;
                background-color: $gray-light-90;
              }
        
              &.bux-menu__link--active {
                border-left-color: $scarlet;
              }
    
            }
    
            button.disclosure-nav-button {
    
              position: absolute;
              right:0.45rem;
              top: 0.45rem;
              padding:0;
              height: 2rem;
              width: 2rem;
    
              background-color: $gray-light-90;
    
              &.disclosure-nav-button-no-link {
                position: static;
                right: auto;
                top: auto;
                float: none;
                text-align: left;
                width: 100%;
                height:auto;
                background-color: $white;
    
                &::after {
                  position: absolute;
                  top: 1.25rem;
                  right:1.25rem;
                  left:auto;
                }
    
                &:hover::after {
                  border-right-color: $gray-dark-80;
                  border-bottom-color: $gray-dark-80;
                }
              }
    
              &:hover {
                background-color: $gray-dark-60;
                cursor:pointer;
              }
    
              &::after {
                content: '\f005';
                font-family: $icon;
                font-size: 18px;
                font-weight: normal;
                position: absolute;
                top: 2px;
                left: 7px;
              }
    
              &:hover::after {
                color: $white;
              }
              
              &[aria-expanded=true] {
                &::after {
                  transform:rotate(90deg);
                  transition: 120ms transform ease-in-out;
                }
              }
    
              &[aria-expanded=false] {	
                &::after {
                  transform:rotate(0deg);
                  transition: 120ms transform ease-in-out;
                }
              }
            }
    
            ul {
              // Second level submenus.
              li {
                margin-top: 0;
    
                // ul {
                //   // Third level submenus.
                //   li {
                    
                //   }
    
                //   ul {
                //     // Fourth level submenus.
                //     li {
    
                //     }
                //   }
                // }
              }
            }
          }
        
        
    
        }
    
        &.disclosure-nav-responsive {
          width: 100%;
        }
    
      }
    
      button.disclosure-nav-toggle {
        @include button-reset();
        background-color: $gray-light-90;
        color: $gray-dark-80;
        width: 100%;
        padding: 0.5rem 50px 0.5rem 0.5rem;
        text-align: right;
        position: relative;
        touch-action: manipulation;
        font-weight: 700;
        height: 46px;
        border: 0;
        background: transparent;
        cursor: pointer;
        position: relative;
        background:
        /* center line drawn as a background stripe */
        linear-gradient($gray-dark-80, $gray-dark-80) calc(100% - 24px) / 14px 2px no-repeat;
        
        &:focus,
        &:hover
        {
          background-color: $gray-light-90;
          color: $gray-dark-80;
          outline-color: $gray-dark-80;
          
          span {
            &,
            &::before,
            &::after
            {
              background:$gray-dark-80;
            }
          }
        }
        
        &::before,
        &::after
        {
          content: "";
          display: block;
          position: absolute;
          width: 14px;
          height: 2px;
          background: $gray-dark-80;
          right: $sp-24;
          margin-left: 8px;
          top: 22px;
          transition:
            transform 0.3s ease,
            opacity 0.3s linear;
          transform-origin: 50% 50%;
          color: $gray-dark-80;
        }
        &::before { transform: translateY(-5px); }
        &::after  { transform: translateY(5px); }
    
        &[aria-expanded=true] {
          background-size: 0 0;
          &::before {
            transform: translate(-50%,-50%) rotate(45deg);
            right: 20px;
          }
          &::after {
              transform: translate(-50%,-50%) rotate(-45deg);
              right: 20px;
          }
        }
      }
    
      .bux-menu__link, .disclosure-nav-button,  button.disclosure-nav-toggle, button.disclosure-nav-button-no-link {
        &:focus {
          outline: 2px solid $focus;
          outline-offset: -2px;
        }
      }
    
      button.disclosure-nav-toggle {
        &:focus {
          outline-offset: -2px;
        }
      }
    
    }
    
    .bux-container__menu,
    .bux-container--menu {
      .disclosure-nav-closed .bux-menu__search {
    		display: none;
    	}
    
    	.disclosure-nav-open .bux-menu__search {
    		display: flex;
            margin-top: $sp-16;
    		width: 100%;
    		padding: 0 $sp-16;
    
    		.bux-form__text-field--menu-search__wrapper {
    			width: 100%;
    		}
    
    		#menuSearchBtn {
    			display: none;
    		}
    
    		.bux-from__text-field--menu-search,
    		.bux-search__submit {
    			display: inline;
    		}
    		
    		.bux-search__submit {
    			margin-top: 2px;
    		}
    	}
    
    	.bux-search__submit {
    		margin-top: 6px;
            pointer-events: all;
    		&::after {
    			color: $scarlet;
    		}
    	}
    
    	#searchIcon {
    		font-size: 1.25rem;
            padding: 0 0 0 10px;
    	}
    
        @mixin search-icon-parameters {
          margin-top: 0px;
          margin-left: -15px;
          position: absolute;
        }
    
        .is-hidden {
          visibility: hidden;
          opacity: 0;
          pointer-events: none;
        }
    
        .bux-menu__search {
          @include transition-specs;
          overflow: clip;
          width: 100%;
          pointer-events: none;
    
          #menuSearchBtn {
                pointer-events: all;
            &:focus {
              outline-offset: -2px;
            }
          }
          #searchIcon {
            &::before {
              transform: skew(0) scale(1);
              content: "\f012";
              @include transition-specs;
              @include search-icon-parameters;
            }
            &::after {
              content: "\f105";
              font-family: bux-icons;
              transform: scale(0);
              position: absolute;
              @include transition-specs;
              @include search-icon-parameters;
            }
          }
          .bux-form__text-field--menu-search__wrapper {
            opacity: 0;
            transform: translate3d(0, 100%, 0); /* off-screen downward */
            @include transition-specs;
            pointer-events: none;
          }
          &.active {
            @include transition-specs;
            #searchIcon {
              &::before {
                transform: skew(45deg, 45deg) scale(0.5);
                content: "\f012";
                @include transition-specs;
                @include search-icon-parameters;
              }
              &::after {
                content: "\f105";
                font-family: bux-icons !important;
                font-style: normal;
                font-weight: 400 !important;
                font-variant: normal;
                text-transform: none;
                line-height: 1;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
                transform: scaleX(1);
                @include transition-specs;
                @include search-icon-parameters;
              }
            }
            .bux-form__text-field--menu-search__wrapper {
                opacity: 1;
                transform: translate3d(0, 0, 0);
                pointer-events: auto;
            }
          }
        }
    
    	@include breakpoint(md) {
    		.bux-menu__search:not(.bux-menu--mobile){
    			flex-grow: 2;
    			position: absolute;
    			right: 0;
    			top: 0;
    			display: flex;
    			justify-content: flex-end;
    			padding-right: $container-gutter;
    			height: 46px;
    
    			&.active {
    				background-color: $gray-light-90;
    				width: 100%;
    
    				.bux-from__text-field--menu-search,
    				.bux-search__submit {
    					display: inline;
    				}
    			}
    
    			.bux-form__text-field--menu-search__wrapper {
    				position: relative;
    				width: 33%;
    			}
    
    			.bux-from__text-field--menu-search {
    				margin: 0;
    				height: 46px;
    			}
    		}
    		#menuSearchBtn {
    			display: block;
    			background: none;
    			border: none;
    			color: $scarlet;
    			height: 43px;
    			margin-top: 3px;
    
    			i::before {
    				font-size: rem-calc(20);
    			}
    		}
    	}
    	@include breakpoint(lg) {
    		.bux-menu__search {
    			padding-right: ($container-gutter * 2);
    		}
    	}
    
        .disclosure-nav-orientation-vertical .bux-menu__search {
          .bux-form__text-field--menu-search__wrapper {
            opacity: 1;
            visibility: visible;
            pointer-events: all;
            transition: none;
            transform: none;
          }
        }
    }
    
    .bux-container.bux-grid.bux-container__menu,
    .bux-container.bux-grid.bux-container--menu {
    	padding: 0;
    	height: 46px;
    }
    
    @include breakpoint(md) {
    		
    	.bux-container.bux-grid.bux-container__menu,
        .bux-container.bux-grid.bux-container--menu {
    		padding-left: 48px;
    		padding-right: 48px;
    	}
    }
    
    @include breakpoint(lg) {
    		
    	.bux-container.bux-grid.bux-container__menu, 
    	.bux-container.bux-grid.bux-container--menu {
    		padding-left: 112px;
    		padding-right: 112px;
    	}
    }
    
    @include breakpoint(md) {
    		
    	.bux-menu--with-search ul.bux-menu {
    		margin-top: 0;
    	}
    }
    
    .mobile-depth-cutoff + button {
       visibility: visible; 
       @include breakpoint(lg) {
           visibility: hidden;
       }
    }
    
    .bux-menu-wrapper {
        background-color: $gray-light-90;
    }
    
    :not(.disclosure-nav-responsive) {
        .bux-menu--compact {
            li.bux-menu__item {
                margin: 12px 12px 0!important;
                .disclosure-nav-button {
                    padding: 0;
                }
                li {
                    margin-top: 0!important;
                    margin-bottom: 0!important;
                }
            }
        }
    }
    
    nav.disclosure-nav.disclosure-nav-orientation-horizontal > ul > li ul > li ul {
        margin-top: $sp-8 !important;
        border-top: 2px solid $gray-light-80 !important;
    }
    
    nav.disclosure-nav.disclosure-nav-orientation-horizontal > ul > li ul:has(li li) > li {
        border-top: none!important;
        margin-top: 0 !important;
    }
    
    nav.disclosure-nav.disclosure-nav-orientation-horizontal > ul > li:has(li li) {
        ul {
            padding-top: 0!important;
            
            li:first-of-type {
                margin-top: 0;
    
                 button:after {
                     padding-top: 0;
                     margin-top: 0;
                 }
            }
        }
        li {
            border-top: none;
            border-bottom: none;
    
            li:first-of-type {
                padding-top: 0;
                 button:after {
                     padding-top: 0;
                 }
    
            }
        }
    
        &:has(.mobile-depth-cutoff) {
            ul {
                padding: 0!important;
                border: 2px solid $gray-light-80;
                border-top: none;
            }
    
            li {
                border: none;
    
                &:first-of-type {
                    padding-top:0;
                }
                &:last-of-type {
                  padding-bottom: 0;
                }
            }
        }
    }
    
    
    div:not(.bux-container--sidebar-nav) > nav.disclosure-nav-orientation-vertical {
      ul button.disclosure-nav-button {
        &[aria-expanded="false"] {
          &:before {
            content: "\f005";
            font-family: bux-icons;
            font-weight: 400;
            top: 3px;
            right: 6px;
            opacity: 1;
            transition: none!important;
          }
          &:hover:before {
            color: $white;
            opacity: 1;
            transition: none!important;
          }
          &:after, &:hover:after {
            content:"";
            font-family: bux-icons;
            font-weight: 400;
            transition: none!important;
          }
        }
        &[aria-expanded="true"] {
          &:before {
            content: "\f007";
            font-family: bux-icons;
            font-weight: 400;
            top: 3px;
            right: 6px;
            opacity: 1!important;
            color: $gray-dark-80;
            transition: none!important;
          }
          &:hover:before {
            color: $white;
            opacity: 1!important;
            transition: none!important;
          }
          &:after, &:hover:after {
            content:""!important;
            font-family: bux-icons;
            font-weight: 400;
            transition: none!important;
          }
        }
      }
    }
    
    // Microinteractions
    
    .disclosure-nav-orientation-horizontal  {
    
      .bux-menu__link:not(.bux-menu__link--active) {
        padding-bottom: 9px!important;
      }
    
      li li a:hover {
        transition-duration: .3s;
        transition-property: background-color;
        transition-timing-function: cubic-bezier(.4,0,.2,1);
      }
    
    .bux-menu {
      > li,
      li li {
        position: relative;
        &:hover a:not(.bux-menu__link--active){
          border-left: none!important;
        }
      }
    }
    
    .bux-menu
    > li:has(> .bux-menu__link:not(.bux-menu__link--active))::before {
      content: "";
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: 4px;
      background-color: $gray-dark-40;
    
      transform: scaleX(0);
      transform-origin: right center;
      transition: transform .3s cubic-bezier(.38,.005,.215,1);
    }
    
    .bux-menu
    > li:hover:has(> .bux-menu__link:not(.bux-menu__link--active))::before {
      transform: scaleX(1);
      transform-origin: left center;
    }
    
    .bux-menu
    li li:has(> .bux-menu__link:not(.bux-menu__link--active))::before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 4px;
      height: 100%;
      background-color: $gray-dark-40;
    
      transform: scaleY(0);
      transform-origin: left bottom;
      transition: transform .3s cubic-bezier(.38,.005,.215,1);
    }
    
    .bux-menu
    li li:hover:has(> .bux-menu__link:not(.bux-menu__link--active))::before {
      transform: scaleY(1);
      transform-origin: left top;
    }
    
    .bux-menu li li:hover {
      padding-left: 4px;
      border-left: none !important; /* if you had a default border */
    }
    }
    
      .disclosure-nav-orientation-vertical {
        li li {
          margin-right: 0!important;
        }
      }
      .bux-container__menu nav.disclosure-nav ul li li a.bux-menu__link {
        font-weight: 400 !important;
      }
    
  • URL: /components/raw/menu/_menu.scss
  • Filesystem Path: src/components/menu/_menu.scss
  • Size: 18.6 KB
  • Content:
    import disclosureNav from '../../../public/libraries/disclosureNav/js/disclosureNav.js';
    
    export class BuxMenu {
        private options = {
            ariaLabel: "Main",
            breakpointMinWidth: 639,
        };
        private disclosureNavInstance: disclosureNav | undefined = undefined;
        private readonly mobileBreakpoint = 639;
    
        // DOM Elements
        private readonly buxMenu: HTMLElement | null;
        private readonly buxMenuWrapper: HTMLElement | null;
        private readonly navButton: HTMLButtonElement | null;
        private readonly menuSearch: HTMLElement | null;
        private readonly menuSearchInput: HTMLInputElement | null;
        private readonly menuSearchContainer: HTMLElement | null;
        private readonly menuSearchSubmit: HTMLElement | null;
        private readonly menuSearchBtn: HTMLElement | null;
        private readonly navMenu: HTMLElement | null;
        private readonly mainMenuFirstLink: HTMLElement | null;
        private readonly buxMainMenu: HTMLElement | null;
        private readonly menuLinks: HTMLElement | null;
    
        constructor() {
            this.buxMenu = document.querySelector(".bux-menu");
            this.buxMenuWrapper = document.querySelector(".bux-menu-wrapper");
            this.navButton = document.querySelector(".disclosure-nav-toggle");
            this.menuSearch = document.querySelector(".bux-menu__search");
            this.menuSearchInput = document.querySelector(".bux-search__input");
            this.menuSearchContainer = document.querySelector(".bux-menu__search");
            this.menuSearchSubmit = document.querySelector(".bux-search__submit");
            this.menuSearchBtn = document.querySelector("#menuSearchBtn");
            this.mainMenuFirstLink = document.querySelector(".bux-menu-wrapper nav > ul > li > a");
            this.buxMainMenu = document.querySelector("#bux-main-menu");
            this.menuLinks = document.querySelector("ul.bux-menu");
    
            this.navMenu = document.querySelector(".bux-container__menu") || document.querySelector(".bux-container--menu");
    
            if (this.buxMenu) {
                this.init();
            }
        }
    
        /**
         * Main Initialization
         */
        private init(): void {
            const sidebarNav = document.querySelectorAll(".bux-container--sidebar-nav__mobile-hidden")[0];
            if (sidebarNav) {
                this.options.breakpointMinWidth = 960;
            }
    
            this.disclosureNavInstance = new disclosureNav("#bux-main-menu", this.options);
            this.bindMenuLogic();
    
            this.setupSearch();
            this.mobileEvents();
            this.toggleMobileSearch();
    
            window.addEventListener("resize", this.handleResize);
            window.addEventListener("load", () => this.toggleMobileSearch());
        }
    
        /**
         * Logic related to the DisclosureNav interaction (Collision detection, height toggling)
         */
        private bindMenuLogic(): void {
            if (!this.disclosureNavInstance) return;
    
            this.toggleMenuHeight();
            this.collisionDetection();
    
            window.addEventListener("resize", () => {
                this.collisionDetection();
                this.toggleMenuHeight();
            });
        }
    
        private isMenuTooLong(): boolean {
            if (!this.disclosureNavInstance) return false;
    
            const navElement = this.disclosureNavInstance._navElem as HTMLElement;
            const parentNode = navElement.parentNode as HTMLElement;
    
            // Calculate Required Space (Width of all list items)
            const menuItems = navElement.querySelectorAll(".disclosure-nav > ul > li");
            let menuItemsTotalWidth = 0;
            for (const item of menuItems) {
                const element = item as HTMLElement;
                const styles = globalThis.getComputedStyle(element);
                const margins = Number.parseFloat(styles.marginLeft) + Number.parseFloat(styles.marginRight);
                menuItemsTotalWidth += element.getBoundingClientRect().width + margins;
            }
    
            // Calculate Available Space
            const mainContainer = document.querySelector('.bux-container__menu') as HTMLElement;
    
            let availableSpace: number;
            const menuButtonBuffer = 90;
    
            if (mainContainer) {
                const containerWidth = mainContainer.getBoundingClientRect().width;
                let siblingsWidth = 0;
                for (const child of mainContainer.children) {
                    if (child !== parentNode && child !== navElement && child.id !== 'bux-main-menu') {
                        const style = globalThis.getComputedStyle(child as HTMLElement);
                        if (style.display !== 'none' && style.position !== 'absolute') {
                            siblingsWidth += (child as HTMLElement).getBoundingClientRect().width;
                            siblingsWidth += Number.parseFloat(style.marginLeft) + Number.parseFloat(style.marginRight);
                        }
                    }
                }
    
                const containerStyles = globalThis.getComputedStyle(mainContainer);
                const containerPadding = Number.parseFloat(containerStyles.paddingLeft) + Number.parseFloat(containerStyles.paddingRight);
    
                availableSpace = containerWidth - siblingsWidth - containerPadding - menuButtonBuffer;
            } else {
                const menuStyles = globalThis.getComputedStyle(parentNode);
                availableSpace = parentNode.getBoundingClientRect().width -
                    Number.parseFloat(menuStyles.paddingLeft) -
                    Number.parseFloat(menuStyles.paddingRight) -
                    menuButtonBuffer;
            }
    
            if (window.innerWidth <= this.options.breakpointMinWidth) {
                return true;
            }
    
            return menuItemsTotalWidth >= availableSpace;
        }
    
        private collisionDetection(): void {
            if (!this.buxMenu || !this.disclosureNavInstance) return;
    
            const menuIsCompact = this.buxMenu.classList.contains("bux-menu--compact");
            const menuIsTooLong = this.isMenuTooLong();
    
            if (menuIsTooLong && !menuIsCompact) {
                this.buxMenu.classList.add("bux-menu--compact");
            } else if (!menuIsTooLong && menuIsCompact) {
                this.buxMenu.classList.remove("bux-menu--compact");
            } else if (menuIsTooLong && menuIsCompact) {
                this.disclosureNavInstance.addMenuToggle();
                if (this.disclosureNavInstance._options.orientation === "horizontal") {
                    this.disclosureNavInstance.switchOrientation();
                }
            }
        }
    
        /**
         * Utilities
         */
        private isSafariMobile(): boolean {
            return /iPad|iPhone|iPod/.test(navigator.userAgent);
        }
    
        private toggleMenuHeight(): void {
            let bottomProperty: 'marginBottom' | 'paddingBottom' = "marginBottom";
            if (this.isSafariMobile()) {
                bottomProperty = "paddingBottom";
            }
    
            if (this.navButton) {
                this.navButton.setAttribute("type", "button");
    
                this.navButton.addEventListener('click', () => {
                    if (!this.buxMenuWrapper || !this.buxMainMenu) return;
    
                    if (this.navButton?.getAttribute("aria-expanded") === "false") {
                        (this.buxMenuWrapper.style)[bottomProperty] = "0px";
                    } else {
                        const menuHeight = this.buxMainMenu.clientHeight;
                        (this.buxMenuWrapper.style)[bottomProperty] = `${menuHeight}px`;
                    }
                });
    
                const subLevel = document.querySelectorAll(".disclosure-nav-top-level");
                for (const subButton of subLevel) {
                    subButton.addEventListener("click", () => {
                        if (this.buxMainMenu && this.buxMenuWrapper) {
                            const menuHeight = this.buxMainMenu.clientHeight;
                            (this.buxMenuWrapper.style)[bottomProperty] = `${menuHeight}px`;
                        }
                    });
                }
            }
    
            if (
                this.buxMainMenu?.classList.contains("disclosure-nav-responsive") &&
                this.buxMenuWrapper?.style.marginBottom
            ) {
                (this.buxMenuWrapper.style)[bottomProperty] = "0px";
            }
        }
    
        /**
         * Search Functionality
         */
        private setupSearch(): void {
            if (!this.menuSearch || !this.menuSearchBtn || !this.menuSearchInput || !this.menuSearchContainer || !this.menuSearchSubmit) return;
    
            this.menuSearchBtn.addEventListener('click', () => this.toggleSearch());
    
            const mainMenuToggle = document.querySelector("#main-menu-toggle");
            if (mainMenuToggle) {
                mainMenuToggle.addEventListener('click', () => this.toggleSearch());
            }
    
            // Keyboard Accessibility
            this.menuSearchInput.addEventListener('keydown', (event) => this.handleSearchKeydown(event, this.menuSearchBtn!, this.menuSearchSubmit!));
            this.menuSearchBtn.addEventListener('keydown', (event) => this.handleSearchKeydown(event, this.menuSearchInput!, this.menuSearchSubmit!));
            this.menuSearchSubmit.addEventListener('keydown', (event) => this.handleSearchKeydown(event, this.menuSearchInput!, this.menuSearchBtn!));
    
            document.addEventListener("click", (event) => {
                const target = event.target as Node;
                const isClickInside =
                    this.menuSearchInput?.contains(target) ||
                    this.menuSearchSubmit?.contains(target) ||
                    this.menuSearchBtn?.contains(target);
    
                if (!isClickInside && this.menuSearchContainer?.classList.contains("active")) {
                    this.toggleSearch();
                }
            });
    
            this.menuSearchInput.addEventListener("focus", () => {
                this.pauseEventListeners(globalThis, "resize", this.handleResize, 100);
            });
        }
    
        private handleSearchKeydown(event: KeyboardEvent, nextElement: HTMLElement, previousElement: HTMLElement): void {
            if (!this.menuSearchContainer?.classList.contains("active")) return;
    
            if (event.key === "Tab") {
                if (event.shiftKey) {
                    event.preventDefault();
                    previousElement.focus();
                } else {
                    event.preventDefault();
                    nextElement.focus();
                }
            } else if (event.key === "Escape") {
                event.preventDefault();
                this.toggleSearch();
                this.menuSearchBtn?.focus();
            }
        }
    
        private toggleSearch(): void {
            if (!this.menuSearchContainer || !this.menuSearchInput || !this.menuSearchSubmit || !this.menuSearchBtn) return;
    
            const searchIcon = document.querySelector("#searchIcon");
            this.menuSearchContainer.classList.toggle("active");
    
            requestAnimationFrame(() => this.menuSearchInput?.focus());
    
            if (this.menuSearchInput.getAttribute("tabindex") === "0") {
                this.menuSearchInput.setAttribute("tabindex", "-1");
                this.menuSearchBtn.setAttribute("aria-expanded", "false");
            } else {
                this.menuSearchInput.setAttribute("tabindex", "0");
                this.menuSearchBtn.setAttribute("aria-expanded", "true");
            }
    
            this.menuSearchSubmit.classList.toggle("is-hidden");
            const currentTabIndex = this.menuSearchSubmit.getAttribute("tabindex");
            this.menuSearchSubmit.setAttribute("tabindex", currentTabIndex === "0" ? "-1" : "0");
    
            if (searchIcon) searchIcon.classList.toggle("icon-xmark");
    
            if (this.mainMenuFirstLink) {
                const linkTabIndex = this.menuSearchContainer.classList.contains("active") ? "-1" : "0";
                this.mainMenuFirstLink.setAttribute("tabindex", linkTabIndex);
            }
        }
    
        /**
         * Mobile Specific Logic
         */
        private toggleMobileSearch(): void {
            if (!this.menuSearch || !this.menuSearchContainer || !this.menuSearchInput || !this.menuSearchSubmit) return;
    
            const windowWidth = window.innerWidth;
            const isMenuMobile = this.buxMainMenu?.classList.contains("disclosure-nav-responsive") || false;
            const isActive = this.menuSearchContainer.classList.contains("active");
    
            if (windowWidth <= this.mobileBreakpoint) {
                this.menuSearchSubmit.classList.remove("is-hidden");
                this.menuSearchInput.setAttribute("tabindex", "0");
                this.menuSearchSubmit.setAttribute("tabindex", "0");
            } else {
                if (isActive) {
                    this.menuSearchSubmit.classList.remove("is-hidden");
                    this.menuSearchInput.setAttribute("tabindex", "0");
                    this.menuSearchSubmit.setAttribute("tabindex", "0");
                } else {
                    this.menuSearchSubmit.classList.add("is-hidden");
                    this.menuSearchInput.setAttribute("tabindex", "-1");
                    this.menuSearchSubmit.setAttribute("tabindex", "-1");
                }
            }
    
            if (windowWidth > this.mobileBreakpoint && isMenuMobile) {
                this.menuSearch.classList.add("bux-menu--mobile");
                this.menuSearchInput.setAttribute("tabindex", "0");
                this.menuSearchSubmit.setAttribute("tabindex", "0");
                this.menuSearchSubmit.classList.remove("is-hidden");
            } else if (windowWidth > this.mobileBreakpoint && !isMenuMobile) {
                this.menuSearch.classList.remove("bux-menu--mobile");
                this.menuSearchSubmit.classList.add("is-hidden");
            }
        }
    
        private moveSearchToNav(): void {
            if (this.menuSearch && this.buxMainMenu && this.menuLinks) {
                this.buxMainMenu.insertBefore(this.menuSearch, this.menuLinks);
            }
        }
    
        private moveSearchBack(): void {
            if (this.menuSearch && this.navMenu && this.buxMainMenu) {
                this.navMenu.insertBefore(this.menuSearch, this.buxMainMenu.nextSibling);
            }
        }
    
        private handleFocusTrap = (event: KeyboardEvent): void => {
            if (event.key !== "Tab") return;
            if (!this.navMenu) return;
    
            const navLinks = [...this.navMenu.querySelectorAll(".bux-menu__item > a")] as HTMLElement[];
    
            if (event.target === navLinks.at(-1) && !event.shiftKey) {
                event.preventDefault();
                this.navButton?.focus();
            }
        }
    
        private mobileEvents(): void {
            const isMobile = this.buxMainMenu?.classList.contains("disclosure-nav-responsive") || false;
    
            if (isMobile) {
                this.moveSearchToNav();
                if (this.navButton && this.navMenu) {
                    this.navButton.addEventListener('click', () => {
                        const expanded = this.navButton!.getAttribute("aria-expanded") === "true";
                        this.navMenu!.setAttribute("aria-modal", expanded ? "false" : "true");
                    });
                }
                this.navMenu?.addEventListener("keydown", this.handleFocusTrap);
            } else {
                this.moveSearchBack();
                this.navMenu?.removeEventListener("keydown", this.handleFocusTrap);
                if (this.menuSearchInput) {
                    this.menuSearchInput.setAttribute("tabindex", "-1");
                }
            }
        }
    
        private handleResize = (): void => {
            if (!document.activeElement || document.activeElement !== this.menuSearchInput) {
                this.mobileEvents();
                this.toggleMobileSearch();
            }
        }
    
        private pauseEventListeners(
            element: EventTarget,
            eventType: string,
            handler: EventListenerOrEventListenerObject,
            duration: number
        ): void {
            element.removeEventListener(eventType, handler);
            setTimeout(() => {
                element.addEventListener(eventType, handler);
            }, duration);
        }
    }
    
    export const init = () => {
        if (document.querySelector(".bux-menu")) {
            new BuxMenu();
        }
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener("DOMContentLoaded", init);
    } else {
        init();
    }
  • URL: /components/raw/menu/menu.ts
  • Filesystem Path: src/components/menu/menu.ts
  • Size: 15.8 KB

Menu

  • Implements Disclosure Navigation Menu.
  • Can be used simultaneously with non-top level links by using <span> rather than <a> in the HTML markup.
  • All necessary library files are compiled into bux.min.js, but are available separately in /public/libraries/disclosureNav.
  • When menu component is used in context, be certain to set the aria-current attribute appropriately (i. e. when on the current page add the attribute aria-current="page" to the corresponding link in the menu).