diff --git a/releases.yml b/releases.yml index 313c53598..354e624e4 100644 --- a/releases.yml +++ b/releases.yml @@ -8,6 +8,14 @@ url: /docs/patterns/images#cover-image status: New notes: We've added a new cover variant to the image container component via is-cover class to support having images cover the container with predefined aspect ratio. + - component: Navigation / Sticky + url: /docs/patterns/navigation#sticky + status: New + notes: We've introduced a new sticky option to the top navigation component via is-sticky class name. + - component: Navigation / Reduced + url: /docs/patterns/navigation#reduced + status: New + notes: We've introduced a new reduced variant of the top navigation for building two level top navigation. - version: 4.13.0 features: - component: Image container diff --git a/scss/_patterns_navigation-reduced.scss b/scss/_patterns_navigation-reduced.scss new file mode 100644 index 000000000..51b041045 --- /dev/null +++ b/scss/_patterns_navigation-reduced.scss @@ -0,0 +1,85 @@ +@mixin vf-p-navigation-reduced { + .p-navigation--reduced { + position: relative; + z-index: 99; // display above sticky top navigation, but below modals/overlays + + // LOGO OVERRIDES FOR REDUCED NAVIGATION + + // orange logo tag is hidden in reduced navigation + .p-navigation__tagged-logo { + .p-navigation__link { + padding-left: 0; + } + + .p-navigation__logo-tag { + display: none; + } + } + + // reduced nav logo text uses default font size (on small screens) + .p-navigation__logo-title { + font-size: #{map-get($font-sizes, default)}rem; + } + + // REDUCED SIZE OF NAVIGATION ON LARGE SCREENS + @media (min-width: $breakpoint-navigation-threshold) { + background-color: $colors--theme--background-alt; + + // adjust font size for reduced nav on large screens + .p-navigation__link, + .p-navigation__logo-title { + color: $colors--theme--text-muted; + font-size: #{map-get($font-sizes, small)}rem; + line-height: map-get($line-heights, x-small); + } + + .p-navigation__link { + padding-bottom: $spv--small; + padding-top: $spv--small; + } + + .p-navigation__item--dropdown-toggle .p-navigation__link { + &::after { + top: $spv--small; + } + } + + .p-navigation__item--dropdown-toggle.is-active { + background-color: $colors--theme--background-default; + } + } + + // SEARCH IN REDUCED NAVIGATION + + .p-navigation__link--search-toggle { + // search label is always hidden in reduced navigation + .p-navigation__search-label { + display: none; + } + + @media (min-width: $breakpoint-navigation-threshold) { + &::after { + top: $spv--small; + } + } + } + + // hide secondary navigation banner when search is open on mobile + &.has-search-open + .has-search-open .p-navigation__banner { + display: none; + } + @media (min-width: $breakpoint-navigation-threshold) { + &.has-search-open + .has-search-open .p-navigation__banner { + display: flex; // reset to value as defined in _patterns_navigation.scss + } + + &.has-search-open { + // make sure reduced navigation items remain visible when search is open + // both classes needed for specificity + .p-navigation__nav .p-navigation__items { + display: inline-flex; + } + } + } + } +} diff --git a/scss/_patterns_navigation.scss b/scss/_patterns_navigation.scss index acf0c19e3..3beeabd50 100644 --- a/scss/_patterns_navigation.scss +++ b/scss/_patterns_navigation.scss @@ -140,7 +140,8 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px } .p-navigation, - .p-navigation--sliding { + .p-navigation--sliding, + .p-navigation--reduced { background-color: $colors--theme--background-default; display: flex; flex-direction: column; @@ -228,6 +229,7 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px // shift navigation items by the size of grid margin to align with grid .p-navigation__items:first-child { margin-left: calc(-1 * $sph--large); + width: calc(100% + $sph--large); } // on medium screen sizes (6 columns) use 2/4 column split @@ -261,12 +263,33 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px max-width: $nav-link-max-width; } + + &.is-right-shifted { + margin-left: auto; + } } .p-navigation__link { @extend %navigation-link; } + .p-navigation__link--menu-toggle { + @extend %navigation-link; + + &::after { + @extend %icon; + @include vf-icon-chevron-themed; + content: ''; + display: inline-block; + height: 1rem; + width: 1rem; + } + + .has-menu-open &::after { + transform: rotate(180deg); + } + } + // navigation logo .p-navigation__banner { @extend %navigation-link-responsive-padding-left; @@ -276,7 +299,8 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px justify-content: space-between; padding-right: 0; - .p-navigation__link { + .p-navigation__link, + .p-navigation__link--menu-toggle { // reset padding for navigation links in the navigation banner @extend %navigation-link-responsive-padding-horizontal; @extend %navigation-link-responsive-padding-vertical; @@ -494,6 +518,15 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px } } + // STICKY NAVIGATION + .p-navigation.is-sticky, + // cover both --sticky and --reduced navigation + [class^='p-navigation--'].is-sticky { + position: sticky; + top: 0; + z-index: 98; // show it above all other content, but below modal/overlays and reduced navigation + } + // SEARCH // on mobile screens search box is visible inside of the mobile navigation dropdown @@ -524,10 +557,9 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px // show both label and icon on large screens @media (min-width: $breakpoint-large) { - padding-left: $sph--large; - .p-navigation__search-label { display: initial; + padding-left: $sph--large; } } @@ -589,7 +621,8 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px // when navigation search is expanded .p-navigation.has-search-open, - .p-navigation--sliding.has-search-open { + .p-navigation--sliding.has-search-open, + .p-navigation--reduced.has-search-open { // make sure search in navigation renders on top of the overlay .p-navigation__nav { display: flex; @@ -611,6 +644,11 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px opacity: 0.5; pointer-events: all; } + + // change search toggle icon to close icon + .p-navigation__link--search-toggle::after { + @include vf-icon-close-themed; + } } .p-navigation.has-menu-open { @@ -749,13 +787,18 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px $sliding-nav-animation-settings: map-get($animation-duration, brisk) ease; - .p-navigation--sliding.has-menu-open { + .p-navigation--sliding.has-menu-open, + .p-navigation--reduced.has-menu-open { height: 100vh; overflow-y: hidden; + position: fixed; + width: 100vw; @media (min-width: $breakpoint-navigation-threshold) { height: auto; overflow-y: visible; + position: static; + width: auto; } .p-navigation__banner .p-navigation__items { @@ -792,7 +835,8 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px } /* stylelint-disable max-nesting-depth -- allow deep nesting for sliding navigation */ - .p-navigation--sliding .p-navigation__dropdown { + .p-navigation--sliding .p-navigation__dropdown, + .p-navigation--reduced .p-navigation__dropdown { display: block; height: 100vh; left: 100vw; @@ -847,7 +891,8 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px } } - .p-navigation--sliding .p-navigation__items { + .p-navigation--sliding .p-navigation__items, + .p-navigation--reduced .p-navigation__items { transition: transform $sliding-nav-animation-settings; &.is-active { @@ -878,7 +923,8 @@ $spv-navigation-logo-bottom-position: 0.125rem; // 2px when 1rem is 16px } /* stylelint-enable max-nesting-depth */ - .p-navigation--sliding { + .p-navigation--sliding, + .p-navigation--reduced { // Default positioning for nested dropdown buttons. Overridden by subsequent styles. .p-navigation__item--dropdown-toggle { position: initial; diff --git a/scss/_settings_spacing.scss b/scss/_settings_spacing.scss index db372fbc1..e09f333c6 100644 --- a/scss/_settings_spacing.scss +++ b/scss/_settings_spacing.scss @@ -18,6 +18,7 @@ $font-sizes: ( h3-mobile: 1.5, h4: 1.5, h4-mobile: 1.5, + default: 1, small: 0.875, x-small: 0.75, ) !default; diff --git a/scss/_vanilla.scss b/scss/_vanilla.scss index 90ef9438a..6f4021b9a 100644 --- a/scss/_vanilla.scss +++ b/scss/_vanilla.scss @@ -33,6 +33,7 @@ @import 'patterns_modal'; @import 'patterns_muted-heading'; @import 'patterns_navigation'; +@import 'patterns_navigation-reduced'; @import 'patterns_notifications'; @import 'patterns_pagination'; @import 'patterns_pull-quotes'; @@ -113,7 +114,6 @@ @include vf-p-heading-icon; @include vf-p-headings; @include vf-p-section; - @include vf-p-form-password-toggle; @include vf-p-icons; @include vf-p-image; @@ -127,6 +127,7 @@ @include vf-p-modal; @include vf-p-muted-heading; @include vf-p-navigation; + @include vf-p-navigation-reduced; @include vf-p-notification; @include vf-p-pagination; @include vf-p-pull-quotes; diff --git a/scss/standalone/patterns_navigation.scss b/scss/standalone/patterns_navigation.scss index 51e11fb5f..401f94807 100644 --- a/scss/standalone/patterns_navigation.scss +++ b/scss/standalone/patterns_navigation.scss @@ -2,6 +2,7 @@ @include vf-base; @include vf-p-navigation; +@include vf-p-navigation-reduced; @include vf-p-search-box; diff --git a/templates/docs/examples/patterns/navigation/_script-sliding.js b/templates/docs/examples/patterns/navigation/_script-sliding.js index 17cb6063c..e93372014 100644 --- a/templates/docs/examples/patterns/navigation/_script-sliding.js +++ b/templates/docs/examples/patterns/navigation/_script-sliding.js @@ -1,11 +1,15 @@ const initNavigationSliding = () => { - const navigation = document.querySelector('.p-navigation--sliding'); - const toggles = document.querySelectorAll('.p-navigation__link[aria-controls]:not(.js-back)'); + const navigation = document.querySelector('.p-navigation--sliding, .p-navigation--reduced'); + const secondaryNavigation = document.querySelector('.p-navigation--reduced + .p-navigation'); + const toggles = document.querySelectorAll('.p-navigation__nav .p-navigation__link[aria-controls]:not(.js-back)'); const searchButtons = document.querySelectorAll('.js-search-button'); const menuButton = document.querySelector('.js-menu-button'); const dropdowns = document.querySelectorAll('ul.p-navigation__dropdown'); - const mainList = dropdowns[0].parentNode.parentNode; - const lists = [...dropdowns, mainList]; + const lists = [...dropdowns]; + const mainList = dropdowns[0]?.parentNode?.parentNode; + if (mainList) { + lists.push(mainList); + } const hasSearch = searchButtons.length > 0; @@ -15,6 +19,9 @@ const initNavigationSliding = () => { } resetToggles(); navigation.classList.remove('has-menu-open'); + if (secondaryNavigation) { + secondaryNavigation.classList.remove('has-menu-open'); + } menuButton.innerHTML = 'Menu'; }; @@ -30,10 +37,14 @@ const initNavigationSliding = () => { }); navigation.classList.remove('has-search-open'); + if (secondaryNavigation) { + secondaryNavigation.classList.remove('has-search-open'); + } document.removeEventListener('keyup', keyPressHandler); }; menuButton.addEventListener('click', function (e) { + e.preventDefault(); closeSearch(); if (navigation.classList.contains('has-menu-open')) { closeAll(); @@ -44,10 +55,21 @@ const initNavigationSliding = () => { } }); + const secondaryNavToggle = document.querySelector('.js-secondary-menu-toggle-button'); + secondaryNavToggle.addEventListener('click', (event) => { + event.preventDefault(); + closeSearch(); + if (secondaryNavigation.classList.contains('has-menu-open')) { + closeAll(); + } else { + secondaryNavigation.classList.add('has-menu-open'); + } + }); + const resetToggles = (exception) => { toggles.forEach(function (toggle) { const target = document.getElementById(toggle.getAttribute('aria-controls')); - if (target === exception) { + if (!target || target === exception) { return; } target.setAttribute('aria-hidden', 'true'); @@ -56,10 +78,11 @@ const initNavigationSliding = () => { }); }; + // when clicking outside navigation, close all dropdowns document.addEventListener('click', function (event) { const target = event.target; if (target.closest) { - if (!target.closest('.p-navigation--sliding')) { + if (!target.closest('.p-navigation--sliding, .p-navigation--reduced')) { resetToggles(); } } @@ -72,31 +95,35 @@ const initNavigationSliding = () => { element.setAttribute('tabindex', '-1'); }); }); - target.querySelectorAll('li').forEach(function (element) { - if (element.parentNode === target) { - element.children[0].setAttribute('tabindex', '0'); - } - }); + if (target) { + target.querySelectorAll('li').forEach(function (element) { + if (element.parentNode === target) { + element.children[0].setAttribute('tabindex', '0'); + } + }); + } }; toggles.forEach(function (toggle) { toggle.addEventListener('click', function (e) { e.preventDefault(); const target = document.getElementById(toggle.getAttribute('aria-controls')); - const isNested = target.parentNode.parentNode.classList.contains('p-navigation__dropdown'); - - if (!isNested) { - resetToggles(target); - } - if (target.getAttribute('aria-hidden') === 'true') { - toggle.parentNode.classList.add('is-active'); - toggle.parentNode.parentNode.classList.add('is-active'); - target.setAttribute('aria-hidden', 'false'); - setFocusable(target); - } else { - target.setAttribute('aria-hidden', 'true'); - toggle.parentNode.classList.remove('is-active'); - toggle.parentNode.parentNode.classList.remove('is-active'); + if (target) { + const isNested = target.parentNode.parentNode.classList.contains('p-navigation__dropdown'); + + if (!isNested) { + resetToggles(target); + } + if (target.getAttribute('aria-hidden') === 'true') { + toggle.parentNode.classList.add('is-active'); + toggle.parentNode.parentNode.classList.add('is-active'); + target.setAttribute('aria-hidden', 'false'); + setFocusable(target); + } else { + target.setAttribute('aria-hidden', 'true'); + toggle.parentNode.classList.remove('is-active'); + toggle.parentNode.parentNode.classList.remove('is-active'); + } } }); }); @@ -111,7 +138,6 @@ const initNavigationSliding = () => { }; dropdowns.forEach(function (dropdown) { - console.log(window.getComputedStyle(dropdown.children[0], null).display); dropdown.children[1].addEventListener('keydown', function (e) { if (e.shiftKey && e.key === 'Tab' && window.getComputedStyle(dropdown.children[0], null).display === 'none') { goBackOneLevel(e, dropdown.children[1].children[0]); @@ -151,6 +177,9 @@ const initNavigationSliding = () => { e.preventDefault(); var searchInput = navigation.querySelector('.p-search-box__input'); + if (!searchInput) { + searchInput = secondaryNavigation.querySelector('.p-search-box__input'); + } var buttons = document.querySelectorAll('.js-search-button'); buttons.forEach((searchButton) => { @@ -158,6 +187,9 @@ const initNavigationSliding = () => { }); navigation.classList.add('has-search-open'); + if (secondaryNavigation) { + secondaryNavigation.classList.add('has-search-open'); + } searchInput.focus(); document.addEventListener('keyup', keyPressHandler); }; diff --git a/templates/docs/examples/patterns/navigation/combined.html b/templates/docs/examples/patterns/navigation/combined.html index 8991fa2e4..c5c433f4d 100644 --- a/templates/docs/examples/patterns/navigation/combined.html +++ b/templates/docs/examples/patterns/navigation/combined.html @@ -34,5 +34,6 @@
{% include 'docs/examples/patterns/navigation/sliding.html' %}
{% include 'docs/examples/patterns/navigation/sliding-dark.html' %}
{% include 'docs/examples/patterns/navigation/sliding-search.html' %}
+
{% include 'docs/examples/patterns/navigation/reduced.html' %}
{% endwith %} {% endblock %} diff --git a/templates/docs/examples/patterns/navigation/reduced.html b/templates/docs/examples/patterns/navigation/reduced.html new file mode 100644 index 000000000..29de5a36e --- /dev/null +++ b/templates/docs/examples/patterns/navigation/reduced.html @@ -0,0 +1,201 @@ +{% extends "_layouts/examples.html" %} +{% block title %}Navigation / Reduced{% endblock %} + +{% block standalone_css %}patterns_navigation{% endblock %} + +{% block content %} + + +
+
+
+ +
    +
  • + +
  • +
+
+ +
+
+
+ +
+
+
+
+

Ubuntu for desktops

+
+
+
+
+{% endblock %} + +{% block script %} + +{% endblock %} + +{% block style %} + +{% endblock %} diff --git a/templates/docs/examples/templates/z-index.html b/templates/docs/examples/templates/z-index.html index 232021af5..c505efcda 100644 --- a/templates/docs/examples/templates/z-index.html +++ b/templates/docs/examples/templates/z-index.html @@ -1,5 +1,5 @@ {% extends "_layouts/examples.html" %} {% block title %}Z-index{% endblock %} {% block content %} -