<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
@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;
}
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();
}
<span> rather than <a> in the HTML markup.bux.min.js, but are available separately in /public/libraries/disclosureNav.aria-current="page" to the corresponding link in the menu).