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 @@