side navigation
A side navigation can be used to enable access to first and second level pages. Second level pages can forma group of sub items.
- The navigation can be opened and closed by clicking the trigger menu icon on the top left.
- A click on a First Level Page menu item opens the corresponding page. Selecting a group opens or closes it, and closes a previous opened group.
- In the desktop viewport, when the side navigation is closed is seen as a narrow sidebar. When opened appears as a full-surface overlay panel.
See the example below, the API documentation as well as JS example code at the end of this page.
This component works with all its features only in browsers that have support for the
If you need to run this component in a browser with no support for the
:has
CSS selector. Please refer to the
reference on MDN.
If you need to run this component in a browser with no support for the
:has
CSS selector, please use the
FROK Release 3.6.x.
table of content
component variations
Default
This component is only meant to be used in a full page and won't render / behave correctly here. Please use the button below (Open on blank page) to see the component in action.
<style>
@media (min-width: 1194px) {
.a-button[data-frok-action="show"] {
display: none;
}
}
</style>
<div class="frontend-kit-example_side-navigation">
<nav
class="m-side-navigation -contrast"
aria-label="Side Navigation"
aria-hidden="false"
>
<div class="m-side-navigation__header">
<div class="m-side-navigation__header__label -size-l highlight">
App name
</div>
<button
type="button"
class="a-button a-button--integrated -without-label m-side-navigation__header__trigger -open"
aria-haspopup="false"
aria-label="Open Side Navigation"
tabindex="0"
>
<i
class="a-icon a-button__icon boschicon-bosch-ic-list-view-mobile"
></i>
</button>
<button
type="button"
class="a-button a-button--integrated -without-label m-side-navigation__header__trigger -close"
aria-haspopup="false"
aria-label="Close Side Navigation"
tabindex="-1"
>
<i class="a-icon a-button__icon boschicon-bosch-ic-close"></i>
</button>
</div>
<ul class="m-menu-group" role="menubar" aria-orientation="vertical">
<li class="a-menu-item" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<i class="a-icon boschicon-bosch-ic-login"></i>
<span class="a-menu-item__label">Login</span>
</a>
</div>
</li>
<li class="a-menu-item" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<i class="a-icon boschicon-bosch-ic-chat"></i>
<span class="a-menu-item__label">Contact</span>
</a>
</div>
</li>
<li class="a-menu-item" role="none">
<div class="a-menu-item__wrapper">
<button
type="button"
role="menuitem"
class="a-menu-item__group"
aria-disabled="false"
aria-controls="group-id-1"
aria-label="open group"
>
<i class="a-icon boschicon-bosch-ic-battery-0"></i>
<span class="a-menu-item__label">Group</span>
<i class="a-icon ui-ic-down-small"></i>
</button>
</div>
<ul id="group-id-1" class="m-menu-group__group" role="menu">
<li class="a-menu-item -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<span class="a-menu-item__label">label 1</span>
</a>
</div>
</li>
<li class="a-menu-item -disabled -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="true"
tabindex="-1"
>
<span class="a-menu-item__label">label 2</span>
</a>
</div>
</li>
<li class="a-menu-item -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<span class="a-menu-item__label">label 3</span>
</a>
</div>
</li>
</ul>
</li>
<li class="a-menu-item -disabled" role="none">
<div class="a-menu-item__wrapper">
<button
type="button"
role="menuitem"
class="a-menu-item__group"
aria-disabled="true"
tabindex="-1"
aria-controls="group-id-2"
aria-label="open group"
>
<i class="a-icon boschicon-bosch-ic-bicycle-e"></i>
<span class="a-menu-item__label">
Group 2 with some extended labels
</span>
<i class="a-icon ui-ic-down-small"></i>
</button>
</div>
<ul id="group-id-2" class="m-menu-group__group" role="menu">
<li class="a-menu-item -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<span class="a-menu-item__label">label 1</span>
</a>
</div>
</li>
<li class="a-menu-item -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<span class="a-menu-item__label">label 2</span>
</a>
</div>
</li>
</ul>
</li>
<li class="a-menu-item" role="none">
<div class="a-menu-item__wrapper">
<button
type="button"
role="menuitem"
class="a-menu-item__group"
aria-disabled="false"
aria-controls="group-id-3"
aria-label="open group"
>
<i class="a-icon boschicon-bosch-ic-agility"></i>
<span class="a-menu-item__label">
Group 3 with some extended labels
</span>
<i class="a-icon ui-ic-down-small"></i>
</button>
</div>
<ul id="group-id-3" class="m-menu-group__group" role="menu">
<li class="a-menu-item -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<span class="a-menu-item__label">label 1</span>
</a>
</div>
</li>
<li class="a-menu-item -indent" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<span class="a-menu-item__label">label 2</span>
</a>
</div>
</li>
</ul>
</li>
<li class="a-menu-item" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="false"
>
<i class="a-icon boschicon-bosch-ic-atom"></i>
<span class="a-menu-item__label">atom</span>
</a>
</div>
</li>
<li class="a-menu-item -disabled" role="none">
<div class="a-menu-item__wrapper">
<a
href="#"
role="menuitem"
class="a-menu-item__link"
aria-disabled="true"
tabindex="-1"
>
<i class="a-icon boschicon-bosch-ic-fax"></i>
<span class="a-menu-item__label">fax</span>
</a>
</div>
</li>
</ul>
</nav>
<div
style="align-items:center;display:flex;height:100vh;justify-content:center;width:100vw"
>
<button
type="button"
class="a-button a-button--primary -without-icon"
data-frok-action="show"
>
<span class="a-button__label">click me</span>
</button>
</div>
</div>
additional content
demo
export default (): void => {
const sideNavigationExamples = document.getElementsByClassName(
'frontend-kit-example_side-navigation',
);
[...sideNavigationExamples].forEach((container) => {
const trigger = container.querySelector(
'.a-button[data-frok-action="show"]',
);
const componentContainer =
container.getElementsByClassName('m-side-navigation')[0];
const linkItems = container.querySelectorAll(
'.a-menu-item:has(> .a-menu-item__wrapper > .a-menu-item__link)',
);
const updateActiveState = (item: HTMLElement, items: HTMLElement[]) => {
// reset active states
items.forEach((element: HTMLElement) => {
element.classList.remove('-selected');
});
// put active state on clicked link
item.classList.add('-selected');
};
linkItems.forEach((linkItem) =>
linkItem.addEventListener('click', () =>
updateActiveState(linkItem, linkItems),
),
);
const sideNavigation = componentContainer.component;
trigger.addEventListener('click', () => sideNavigation.show());
sideNavigation.setExternalTrigger(trigger);
});
};
styles SCSS
/* stylelint-disable max-nesting-depth */
/* stylelint-disable no-descending-specificity */
/* stylelint-disable a11y/no-display-none */
$m-side-navigation--open-width: 19rem;
.m-side-navigation {
position: fixed;
height: 100%;
min-height: 100vh;
overflow: scroll;
left: 0;
top: 0;
width: 0;
transition: 125ms width $default-transition-easing;
// remove visible scroll bars
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
// Reset
ul {
margin-bottom: 0;
}
a,
a:visited {
text-decoration: none;
}
/* App's name and Open / Close button */
&__header {
align-items: center;
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
position: relative;
&__label {
display: none;
padding: 0.5rem 1.25rem 0.5rem 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__trigger {
display: flex;
position: relative;
&:hover {
background-color: var(--plain__enabled__fill__hovered);
color: var(--plain__enabled__front__hovered);
}
&:active {
background-color: var(--plain__enabled__fill__pressed);
color: var(--plain__enabled__front__pressed);
}
&:focus-visible {
@include focus-inside;
}
&.-close {
display: none;
}
}
}
&:not(.-open) .a-menu-item {
&__link,
&__button,
&__group {
column-gap: 0;
padding-inline-end: 0.75rem;
}
&__group .a-icon.ui-ic-down-small {
display: none;
}
}
/* First Level Section */
&__menuItems {
padding: 0;
.m-side-navigation__menuSubitems {
display: none;
padding: 0;
}
}
// When the menu is open.
// Classes Logic:
// -opening = Menu is opening / the transition is happening.
// -open = Menu is open / the transition has ended.
&.-opening,
&.-open {
width: 100vw;
.m-side-navigation {
&__header {
&__label {
display: flex;
column-gap: 1rem;
}
&__trigger {
&.-open {
display: none;
}
&.-close {
display: flex;
}
}
}
&__menuSubitems {
width: 100%;
}
}
}
.a-menu-item__wrapper.-open {
+ .m-side-navigation__menuSubitems {
display: flex;
flex-direction: column;
padding: 0;
width: 100%;
}
}
}
@include tablet-and-up {
.m-side-navigation.-open,
.m-side-navigation.-opening {
width: $m-side-navigation--open-width;
}
}
@include desktop-and-up {
.m-side-navigation {
width: 3rem;
}
}
/* stylelint-enable a11y/no-display-none */
/* stylelint-enable no-descending-specificity */
/* stylelint-enable max-nesting-depth */