<nav id="example-nav--sub--light" class="cwf-nav cwf-nav--sub cwf-nav--light" aria-label="Sub">
<div class="cwf-nav__container">
<div class="cwf-nav__controller">
<ul class="cwf-nav__list cwf-nav__list--level-2 cwf-scroll--y">
<li class="cwf-nav__item cwf-nav__item--has-dropdown ">
<a href="/components/global/" class="cwf-nav__link">
Global
</a>
<button class="cwf-nav__toggle">
<i class="cwf-nav__icon" aria-hidden="true"></i>
</button>
<ul class="cwf-nav__list cwf-nav__list--level-3 ">
<li class="cwf-nav__item ">
<a href="/components/global/breadcrumb/" class="cwf-nav__link">
Breadcrumb
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/button/" class="cwf-nav__link">
Button
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/footer/" class="cwf-nav__link">
Footer
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/form/" class="cwf-nav__link">
Form
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/general-content/" class="cwf-nav__link">
General content
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/grid/" class="cwf-nav__link">
Grid
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/header/" class="cwf-nav__link">
Header
</a>
</li>
<li class="cwf-nav__item cwf-nav__item--has-dropdown ">
<a href="/components/global/navigation/" class="cwf-nav__link">
Navigation
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/global/skip-links/" class="cwf-nav__link">
Skip links
</a>
</li>
</ul>
</li>
<li class="cwf-nav__item cwf-nav__item--has-dropdown ">
<a href="/components/content/" class="cwf-nav__link">
Content
</a>
<button class="cwf-nav__toggle">
<i class="cwf-nav__icon" aria-hidden="true"></i>
</button>
<ul class="cwf-nav__list cwf-nav__list--level-3 ">
<li class="cwf-nav__item ">
<a href="/components/content/accordion/" class="cwf-nav__link">
Accordion
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/content/card/" class="cwf-nav__link">
Card
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/content/hero/" class="cwf-nav__link">
Hero
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/content/modal/" class="cwf-nav__link">
Modal
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/content/notification/" class="cwf-nav__link">
Notification
</a>
</li>
<li class="cwf-nav__item ">
<a href="/components/content/tabs/" class="cwf-nav__link">
Tabs
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
{% macro generate(title, routes, level, limit, class) %}
{% if routes|length %}
{% set listClasses = [
'cwf-nav__list',
'cwf-nav__list--level-' ~ level,
class
] %}
<ul class="{{ listClasses|join(' ') }}">
{% for route in routes %}
{% set hasDropdown = route.children
? 'cwf-nav__item--has-dropdown'
: ''
%}
{% set isCurrent = route.name == title
? 'cwf-nav__item--is-current'
: ''
%}
{% set classes = ['cwf-nav__item', hasDropdown, isCurrent] %}
<li class="{{ classes|join(' ') }}">
{% set href = route.href %}
<a href="{{ href }}"
class="cwf-nav__link"
{{ isCurrent ? ' aria-current="page"' }}>
{{ route.name.short ?? route.name.full ?? route.name }}
</a>
{% if
route.children is defined
and (route.children|length)
and level <= limit %}
<button class="cwf-nav__toggle">
<i class="cwf-nav__icon" aria-hidden="true"></i>
</button>
{{
_self.generate(
title,
route.children,
level + 1,
limit
)
}}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
{% macro main(title, routes) %}
{% set filteredRoutes = [] %}
{% for route in routes %}
{% if route.level == 1 %}
{% set filteredRoutes = filteredRoutes|merge([route]) %}
{% endif %}
{% endfor %}
{{- _self.generate(title, filteredRoutes, 1, 2) -}}
{% endmacro %}
{% macro sub(title, routes, siblings) %}
{% set filteredRoutes = [] %}
{% set parentLink = '' %}
{% set break = false %}
{% for route in routes if not break %}
{% if route.name.full == title or route.name == title %}
{% set break = true %}
{% set parent = false %}
{% set hasParent = route.parents is defined
and (route.parents|length)
%}
{% if hasParent %}
{% set parent = route.parents|last %}
{% endif %}
{% set hasChildren = route.children is defined
and (route.children|length)
%}
{% set hasSiblings = parent
and parent.children is defined
and (parent.children|length)
and (parent.children|length) > 1
%}
{% if hasChildren %}
{% set filteredRoutes = route.children %}
{% elseif hasSiblings and siblings %}
{% set filteredRoutes = parent.children %}
{% endif %}
{% endif %}
{% endfor %}
{{- _self.generate(title, filteredRoutes, 2, 2, 'cwf-scroll--y') -}}
{% endmacro %}
{% import _self as nav %}
{% set list %}
{% if type == 'main' %}
{{ nav.main(title, routes) }}
{% endif %} {% if type == 'sub' %}
{{ nav.sub(title, routes, siblings) }}
{% endif %}
{% endset %}
{% if list|trim|length %}
<nav id="{{ id ?? 'cwf-nav--' ~ type }}"
class="cwf-nav cwf-nav--{{ type }} cwf-nav--{{ theme }}"
aria-label="{{ type|capitalize }}">
{% if type == 'main' %}
<button id="{{
id is defined
? id ~ '__hamburger'
: 'cwf-nav__hamburger'
}}"
aria-label="Open or close the menu with this button"
class="cwf-nav__hamburger">
<span class="cwf-nav__hamburger-text cwf-nav__hamburger-text--close">
Close
</span>
<i class="cwf-nav__icon" aria-hidden="true"></i>
<span class="cwf-nav__hamburger-text cwf-nav__hamburger-text--menu">
Menu
</span>
</button>
{% endif %}
<div class="cwf-nav__container">
<div class="cwf-nav__controller">
{{ list|trim }}
</div>
</div>
</nav>
{% endif %}
{
"id": "example-nav--sub--light",
"type": "sub",
"theme": "light",
"siblings": true,
"routes": [
{
"level": 1,
"name": "Blog",
"href": "https://blogs.vcu.edu/compass/"
},
{
"level": 1,
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 2,
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
}
],
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"level": 3,
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "Button",
"href": "/components/global/button/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "Footer",
"href": "/components/global/footer/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "Form",
"href": "/components/global/form/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "General content",
"href": "/components/global/general-content/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "Grid",
"href": "/components/global/grid/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "Header",
"href": "/components/global/header/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 3,
"name": "Navigation",
"href": "/components/global/navigation/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
],
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"level": 4,
"name": "Main navigation",
"href": "/components/global/navigation/main/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
}
]
},
{
"level": 4,
"name": "Sub navigation",
"href": "/components/global/navigation/sub/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
}
]
},
{
"level": 3,
"name": "Skip links",
"href": "/components/global/skip-links/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
}
]
},
{
"level": 2,
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
}
],
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
},
{
"level": 3,
"name": "Accordion",
"href": "/components/content/accordion/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 3,
"name": "Card",
"href": "/components/content/card/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 3,
"name": "Hero",
"href": "/components/content/hero/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 3,
"name": "Modal",
"href": "/components/content/modal/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 3,
"name": "Notification",
"href": "/components/content/notification/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 3,
"name": "Tabs",
"href": "/components/content/tabs/",
"parents": [
{
"name": "Components",
"href": "/components/",
"children": [
{
"name": {
"full": "Global components",
"short": "Global"
},
"href": "/components/global/",
"children": [
{
"name": "Breadcrumb",
"href": "/components/global/breadcrumb/"
},
{
"name": "Button",
"href": "/components/global/button/"
},
{
"name": "Footer",
"href": "/components/global/footer/"
},
{
"name": "Form",
"href": "/components/global/form/"
},
{
"name": "General content",
"href": "/components/global/general-content/"
},
{
"name": "Grid",
"href": "/components/global/grid/"
},
{
"name": "Header",
"href": "/components/global/header/"
},
{
"name": "Navigation",
"href": "/components/global/navigation/",
"children": [
{
"name": "Main navigation",
"href": "/components/global/navigation/main/"
},
{
"name": "Sub navigation",
"href": "/components/global/navigation/sub/"
}
]
},
{
"name": "Skip links",
"href": "/components/global/skip-links/"
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"name": {
"full": "Content components",
"short": "Content"
},
"href": "/components/content/",
"children": [
{
"name": "Accordion",
"href": "/components/content/accordion/"
},
{
"name": "Card",
"href": "/components/content/card/"
},
{
"name": "Hero",
"href": "/components/content/hero/"
},
{
"name": "Modal",
"href": "/components/content/modal/"
},
{
"name": "Notification",
"href": "/components/content/notification/"
},
{
"name": "Tabs",
"href": "/components/content/tabs/"
}
]
}
]
},
{
"level": 1,
"name": "Documentation",
"href": "https://compass.vcu.edu/"
}
],
"title": "Components"
}
// Navigation component styles
@import "../../shared/animation";
@import "../../shared/media";
@import "../../shared/style";
@import "../../shared/theme";
.cwf-nav {
line-height: 1;
font-family: theme__font--sans-serif();
@include media__breakpoint {
background-color: var(--cwf-nav--background-color);
}
}
// Nav dark colors
$nav--dark__hamburger--mobile--foreground-color: style__color(white) !default;
$nav--dark--background-color: darken(
style__color(gray-dark),
6.5%
) !default; // #222
$nav--dark--background-color--level-2: style__color(gray-dark) !default;
$nav--dark--background-color--level-3: style__color(gray) !default;
$nav--dark--foreground-color: style__color(white) !default;
$nav--dark--active--foreground-color: style__color(black) !default;
$nav--dark--border-color: style__color(gray-light) !default;
$nav--dark--accent-color: style__color(gold) !default;
$nav--dark__controller--background-color: style__color(black) !default;
// Nav light colors
$nav--light__hamburger--mobile--foreground-color: style__color(black) !default;
$nav--light--background-color: darken(
style__color(white-dark),
3%
) !default; // #f0f0f0
$nav--light--background-color--level-2: style__color(white-dark) !default;
$nav--light--background-color--level-3: style__color(white) !default;
$nav--light--foreground-color: darken(
style__color(gray-lightest),
3%
) !default; // #6d6d6d
$nav--light--active--foreground-color: theme__accent--foreground() !default;
$nav--light--border-color: lighten(
style__color(white-darkest),
6.5%
) !default; // #ddd
$nav--light--accent-color: theme__accent--background() !default;
$nav--light__controller--background-color: style__color(white) !default;
// Nav themes
$nav__themes: (
dark: (
hamburger-mobile-foreground-color:
$nav--dark__hamburger--mobile--foreground-color,
background-color: $nav--dark--background-color,
background-color-level-2: $nav--dark--background-color--level-2,
background-color-level-3: $nav--dark--background-color--level-3,
foreground-color: $nav--dark--foreground-color,
active-foreground-color: $nav--dark--active--foreground-color,
border-color: $nav--dark--border-color,
accent-color: $nav--dark--accent-color,
controller-background-color: $nav--dark__controller--background-color
),
light: (
hamburger-mobile-foreground-color:
$nav--light__hamburger--mobile--foreground-color,
background-color: $nav--light--background-color,
background-color-level-2: $nav--light--background-color--level-2,
background-color-level-3: $nav--light--background-color--level-3,
foreground-color: $nav--light--foreground-color,
active-foreground-color: $nav--light--active--foreground-color,
border-color: $nav--light--border-color,
accent-color: $nav--light--accent-color,
controller-background-color: $nav--light__controller--background-color
)
);
@mixin nav__theme($instructions) {
.cwf-nav--#{map-get($instructions, theme)} {
--cwf-nav__hamburger--mobile-foreground-color: #{map-get(
$instructions,
hamburger-mobile-foreground-color
)};
--cwf-nav--background-color: #{map-get($instructions, background-color)};
--cwf-nav--background-color--level-2: #{map-get(
$instructions,
background-color-level-2
)};
--cwf-nav--background-color--level-3: #{map-get(
$instructions,
background-color-level-3
)};
--cwf-nav--foreground-color: #{map-get($instructions, foreground-color)};
--cwf-nav--active--foreground-color: #{map-get(
$instructions,
active-foreground-color
)};
--cwf-nav--border-color: #{map-get($instructions, border-color)};
--cwf-nav--accent-color: #{map-get($instructions, accent-color)};
--cwf-nav__controller--background-color: #{map-get(
$instructions,
controller-background-color
)};
}
}
@mixin nav__theme--official($theme) {
$colors: map-get($nav__themes, $theme);
$instructions: map-merge(
$colors,
(
theme: $theme
)
);
@include nav__theme($instructions);
}
@each $theme, $colors in $nav__themes {
@include nav__theme--official($theme);
}
$nav__container--background-color: rgba(style__color(black), 0.68) !default;
$nav__container--reduced-transparency--background-color: style__color(
black
) !default;
.cwf-nav__container {
display: none;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--cwf-nav__container--background-color);
@include style__z-index(modal);
--cwf-nav__container--background-color: #{$nav__container--background-color};
@include media__reduced(transparency) {
--cwf-nav__container--background-color: #{$nav__container--reduced-transparency--background-color};
}
@include media__reduced(transparency, no-preference) {
--cwf-nav__container--background-color: #{$nav__container--background-color};
}
@include media__breakpoint {
display: block;
position: static;
background-color: unset;
@include theme__contain;
}
}
.cwf-nav__controller {
height: 100%;
background-color: var(--cwf-nav__controller--background-color);
}
.cwf-nav--main .cwf-nav__controller {
width: 70%;
@include media__breakpoint {
width: 100%;
}
}
.cwf--show-main-nav .cwf-nav__controller {
padding-top: 64px;
}
.cwf-nav--main .cwf--show-main-nav .cwf-nav__controller {
@include media__breakpoint {
padding-top: 0;
}
}
.cwf-nav__item {
display: flex;
flex-wrap: wrap;
position: relative;
}
.cwf-nav__link {
flex: 1;
display: flex;
align-items: center;
font-weight: 500;
overflow: hidden;
padding: 1rem 1.75rem;
position: relative;
background-color: transparent;
text-align: left;
text-decoration: none;
color: var(--cwf-nav--foreground-color);
&:hover,
&:focus {
background-color: var(--cwf-nav--accent-color);
color: var(--cwf-nav--active--foreground-color);
.cwf-nav--main & {
@include media__breakpoint {
& + .cwf-nav__toggle {
color: var(--cwf-nav--active--foreground-color);
}
}
}
}
}
.cwf-nav__list {
display: none;
list-style-type: none;
width: 100%;
margin: 0;
padding: 0;
border: 1px solid var(--cwf-nav--border-color);
& .cwf-nav__list {
border: none;
border-top: 1px solid var(--cwf-nav--border-color);
}
}
$nav__list--scroll--background-fallback: transparent !default;
$nav__list--scroll--background: rgba(style__color(black), 0.25) !default;
@mixin nav__list--scroll-shadow($side) {
background: var(--cwf-nav__list--scroll--background-fallback);
background: radial-gradient(
ellipse farthest-corner at $side center,
var(--cwf-nav__list--scroll--background) 0%,
transparent 75%
);
@include media__reduced(transparency) {
background: var(--cwf-nav__list--scroll--background-fallback);
}
@include media__reduced(transparency, no-preference) {
background: radial-gradient(
ellipse farthest-corner at $side center,
var(--cwf-nav__list--scroll--background) 0%,
transparent 75%
);
}
}
.cwf-nav__list.cwf-scroll--y {
position: relative;
max-height: 551px;
--cwf-nav__list--scroll--background-fallback: #{$nav__list--scroll--background-fallback};
--cwf-nav__list--scroll--background: #{$nav__list--scroll--background};
--cwf-nav__list--scroll-top: 0;
--cwf-nav__list--scroll-bottom: 0;
&:before {
top: 0;
margin-bottom: -0.5rem;
@include nav__list--scroll-shadow(top);
opacity: var(--cwf-nav__list--scroll-top);
}
&:before,
&:after {
content: "";
position: sticky;
display: block;
width: 100%;
height: 0.5rem;
pointer-events: none;
@include style__z-index(content, middle);
}
&:after {
bottom: 0;
margin-top: -0.5rem;
@include nav__list--scroll-shadow(bottom);
opacity: var(--cwf-nav__list--scroll-bottom);
}
}
.cwf-nav--main .cwf-nav__list {
@include style__z-index(menu, middle);
border: none;
}
.cwf-nav__item--is-expanded > .cwf-nav__list,
.cwf-nav__controller > .cwf-nav__list {
display: block;
}
.cwf-nav__list--level-1 {
background-color: var(--cwf-nav--background-color);
margin: 0;
width: 100%;
@include style__z-index(page);
}
.cwf--show-main-nav .cwf-nav--main .cwf-nav__list--level-1 {
height: 100%;
overflow-y: auto;
}
$nav--main__list--level-1__link--border-color: transparent !default;
$nav--main__list--level-1__link--active--border-color: rgba(
style__color(black),
0.15
) !default;
.cwf-nav--main .cwf-nav__list--level-1 {
@include media__breakpoint {
display: flex;
flex-direction: row;
& > .cwf-nav__item > .cwf-nav__link {
padding-top: calc(1rem - 1px);
border-top: 1px solid
var(--cwf-nav--main__list--level-1__link--border-color);
--cwf-nav--main__list--level-1__link--border-color: #{$nav--main__list--level-1__link--border-color};
--cwf-nav--main__list--level-1__link--active--border-color: #{$nav--main__list--level-1__link--active--border-color};
&:hover,
&:focus {
border-color: var(
--cwf-nav--main__list--level-1__link--active--border-color
);
}
}
}
}
.cwf-nav--sub .cwf-nav__list--level-2 {
@include style__z-index(content);
}
// make room for the dropdown arrows on level 1
.cwf-nav__list--level-1 > .cwf-nav__item--has-dropdown > .cwf-nav__link,
.cwf-nav__list--level-2 > .cwf-nav__item--has-dropdown > .cwf-nav__link {
padding-right: 3rem;
}
.cwf-nav--sub .cwf-nav__list--level-1 > .cwf-nav__item > .cwf-nav__link,
.cwf-nav--sub .cwf-nav__list--level-1 > .cwf-nav__item > .cwf-nav__toggle {
display: none;
}
.cwf-nav__item {
border-bottom: 1px solid var(--cwf-nav--border-color);
&:last-child {
border-bottom: none;
}
}
.cwf-nav--main .cwf-nav__list--level-1 > .cwf-nav__item {
@include media__breakpoint {
border-bottom: none;
border-right: 1px solid var(--cwf-nav--border-color);
}
}
.cwf-nav--sub .cwf-nav__list--level-1 > .cwf-nav__item {
@include media__breakpoint {
border-right: none;
}
}
.cwf-nav--sub .cwf-nav__list--level-1 > .cwf-nav__item:first-of-type {
@include media__breakpoint {
border-top: none;
}
}
.cwf-nav--main .cwf-nav__list--level-1 > .cwf-nav__item:last-of-type {
@include media__breakpoint {
border-right: none;
}
}
.cwf-nav__list--level-3 > .cwf-nav__item > .cwf-nav__link {
padding-left: 2.5rem;
}
.cwf-nav--main .cwf-nav__list--level-3 > .cwf-nav__item > .cwf-nav__link {
@include media__breakpoint {
padding-left: 1.75rem;
}
}
.cwf-nav__list--level-2 {
padding: 0;
background-color: var(--cwf-nav--background-color--level-2);
}
.cwf-nav--main .cwf-nav__list--level-2 {
@include media__breakpoint {
top: 100%;
}
}
.cwf-nav--main .cwf-nav__list--level-2,
.cwf-nav--main .cwf-nav__list--level-3 {
@include media__breakpoint {
position: absolute;
min-width: 14rem;
}
}
.cwf-nav__list--level-3 {
padding: 0;
background-color: var(--cwf-nav--background-color--level-3);
}
.cwf-nav--main .cwf-nav__list--level-3 {
@include media__breakpoint {
left: 100%;
top: 0;
}
}
$nav__toggle--background-color: transparent !default;
.cwf-nav__toggle {
width: 3rem;
min-height: 3rem;
padding: 0;
border: none;
background-color: var(--cwf-nav__toggle--background-color);
font-size: 1rem;
color: var(--cwf-nav--foreground-color);
--cwf-nav__toggle--background-color: #{$nav__toggle--background-color};
@include style__cursor;
&:hover,
&:focus {
background-color: var(--cwf-nav--accent-color);
}
@include media__touch {
position: relative;
&:before {
content: "";
position: absolute;
left: 0;
top: 10%;
width: 1px;
height: 80%;
background-color: var(--cwf-nav--border-color);
}
&:focus:before {
display: none;
}
}
}
.cwf-nav--dark .cwf-nav__toggle {
&:hover,
&:focus {
color: var(--cwf-nav--background-color);
}
}
.cwf-nav--light .cwf-nav__toggle {
&:hover,
&:focus {
color: var(--cwf-nav--background-color--level-3);
}
}
.cwf-nav--main .cwf-nav__toggle {
@include media__breakpoint {
position: absolute;
height: 100%;
top: 0;
right: 0;
padding: 0;
pointer-events: none;
@include media__touch {
&:before {
display: none;
}
}
}
}
$nav__icon: "\f078" !default; // Chevron down
.cwf-nav__icon {
display: inline-block;
@include style__icon;
@include animation__transition(transform);
&:before {
content: $nav__icon;
}
}
.cwf-nav__icon--rotate {
@include animation__flip;
}
$nav--main__list--level-2__toggle__icon: "\f054" !default; // Chevron right
.cwf-nav--main .cwf-nav__list--level-2 .cwf-nav__toggle .cwf-nav__icon:before {
@include media__breakpoint {
content: $nav--main__list--level-2__toggle__icon;
}
}
$nav__hamburger--background-color: transparent !default;
$nav__hamburger--foreground-color: darken(
style__color(gray-dark),
0.75%
) !default; // #313131
$nav__hamburger--active--background-color: rgba(
style__color(black),
0.05
) !default;
$nav__hamburger--active--foreground-color: style__color(black) !default;
.cwf-nav__hamburger {
align-items: center;
background-color: var(--cwf-nav__hamburger--background-color);
border: none;
color: var(--cwf-nav__hamburger--foreground-color);
display: flex;
flex-direction: column;
font-size: 0.65rem;
font-weight: 700;
height: 64px;
justify-content: space-evenly;
left: 0;
min-width: 48px;
padding: 0.5rem 0;
position: absolute;
top: 0;
@include style__z-index(modal, middle);
--cwf-nav__hamburger--background-color: #{$nav__hamburger--background-color};
--cwf-nav__hamburger--foreground-color: #{$nav__hamburger--foreground-color};
--cwf-nav__hamburger--active--background-color: #{$nav__hamburger--active--background-color};
--cwf-nav__hamburger--active--foreground-color: #{$nav__hamburger--active--foreground-color};
&:hover,
&:focus {
background-color: var(--cwf-nav__hamburger--active--background-color);
color: var(--cwf-nav__hamburger--active--foreground-color);
}
& .cwf-nav__icon {
font-size: 1.25rem;
}
}
.cwf-nav--main .cwf-nav__hamburger {
@include media__breakpoint {
display: none;
}
}
.cwf--show-main-nav {
overflow: hidden;
}
.cwf--show-main-nav .cwf-nav__hamburger {
left: 70%;
right: 0;
flex-direction: row;
width: 0;
min-width: 128px;
padding: 0;
font-size: 1rem;
color: var(--cwf-nav__hamburger--mobile-foreground-color);
transform: translateX(-100%);
& .cwf-nav__icon {
font-size: 1.5rem;
}
}
$nav__hamburger__icon--expand: "\f0c9" !default; // Bars
$nav__hamburger__icon--collapse: "\f00d" !default; // Times
.cwf-nav__hamburger .cwf-nav__icon:before {
content: $nav__hamburger__icon--expand;
}
.cwf--show-main-nav .cwf-nav__hamburger .cwf-nav__icon:before {
content: $nav__hamburger__icon--collapse;
}
.cwf-nav__hamburger-text--menu {
display: block;
.cwf--show-main-nav & {
display: none;
}
}
.cwf-nav__hamburger-text--close {
display: none;
.cwf--show-main-nav & {
display: block;
}
}
.cwf--show-main-nav .cwf-nav__hamburger-text--menu {
display: none;
}
// Navigation utilities
.cwf--show-main-nav .cwf-nav--main .cwf-nav__container {
display: block;
}
.cwf-nav__item > .cwf-nav__link:before {
content: "";
display: none;
position: absolute;
left: 0;
top: 0;
width: 0.5rem;
height: 100%;
background-color: var(--cwf-nav--accent-color);
@include animation__transition(width, height);
}
.cwf-nav__item.cwf-nav__item--is-current > .cwf-nav__link:before {
display: block;
}
.cwf-nav--main
.cwf-nav__list--level-1
> .cwf-nav__item.cwf-nav__item--is-current
> .cwf-nav__link:before {
@include media__breakpoint {
top: unset;
bottom: 0;
width: 100%;
height: 0.25rem;
}
}
.cwf-nav--main
.cwf-nav__list--level-3
> .cwf-nav__item.cwf-nav__item--is-current
> .cwf-nav__link:before {
@include media__breakpoint {
left: unset;
right: 0;
}
}
.cwf-nav__item.cwf-nav__item--is-parent-of-current > .cwf-nav__link:before {
opacity: 0.32;
}
.cwf-nav--main
.cwf-nav__list--level-1
.cwf-nav__item.cwf-nav__item--is-parent-of-current
> .cwf-nav__link:before {
@include media__breakpoint {
opacity: 0.64;
}
}
// The default component class
import { Component } from '../../shared/component';
// Provide functionality to all navs
export class Nav extends Component {
constructor({
desktopBreakpoint = 1024,
// Selectors
container = 'cwf-nav__container',
controller = 'cwf-nav__controller',
hamburger = 'cwf-nav__hamburger',
iconRotate = 'cwf-nav__icon--rotate',
item = 'cwf-nav__item',
itemHasDropdown = 'cwf-nav__item--has-dropdown',
itemIsCurrent = 'cwf-nav__item--is-current',
itemIsExpanded = 'cwf-nav__item--is-expanded',
itemIsParentOfCurrent = 'cwf-nav__item--is-parent-of-current',
link = 'cwf-nav__link',
list = 'cwf-nav__list',
listLevel = /cwf-nav__list--level-?(\d)/,
nav = 'cwf-nav',
scrollable = 'cwf-scroll--y',
showMainNav = 'cwf--show-main-nav',
subNav = 'cwf-nav--sub',
toggle = 'cwf-nav__toggle',
// Properties
listScrollTop = '--cwf-nav__list--scroll-top',
listScrollBottom = '--cwf-nav__list--scroll-bottom'
} = {}) {
super();
// Initialize a run flag
this.run = false;
// Set the desktop breakpoint,...
this.desktopBreakpoint = desktopBreakpoint;
// ... selectors,...
this.selectors = {
container,
controller,
hamburger,
iconRotate,
item,
itemHasDropdown,
itemIsCurrent,
itemIsExpanded,
itemIsParentOfCurrent,
link,
list,
listLevel,
nav,
scrollable,
showMainNav,
subNav,
toggle
};
// ... and properties
this.properties = {
listScrollTop,
listScrollBottom
};
// Initialize an empty array for all nav references
this.references = [];
// Bind this to all event methods that need the class context
this.normalizeReferences = this.normalizeReferences.bind(this);
this.keyboardNavigation = this.keyboardNavigation.bind(this);
this.toggleDropdownMenu = this.toggleDropdownMenu.bind(this);
this.toggleMainMenu = this.toggleMainMenu.bind(this);
}
// Normalize a nav reference
normalizeReferences(nav) {
// Initialize a reference object
let reference = {};
// Store whether this nav is a sub nav,...
reference.isSubNav = nav.classList.contains(this.selectors.subNav);
// ... its lists,...
reference.lists = Array.from(
nav.getElementsByClassName(this.selectors.list)
);
// ... items,...
reference.items = Array.from(
nav.getElementsByClassName(this.selectors.item)
);
// ... toggles,...
reference.toggles = Array.from(
nav.getElementsByClassName(this.selectors.toggle)
);
// ... and links
reference.links = Array.from(
nav.getElementsByClassName(this.selectors.link)
);
// If the nav is a sub nav, return the reference object as is
if (reference.isSubNav) return reference;
// Otherwise, also store its hamburger button,...
reference.hamburger = nav.querySelector(`.${this.selectors.hamburger}`);
// ... container,...
reference.container = nav.querySelector(`.${this.selectors.container}`);
// ... and controller
reference.controller = nav.querySelector(
`.${this.selectors.controller}`
);
// Finally, return the reference object
return reference;
}
// Get all references to the navs and their lists/items
getReferences() {
// Attempt to grab all navs from the page...
const navs = Array.from(
document.querySelectorAll(`.${this.selectors.nav}`)
);
// ... and if none exist, do nothing else
if (!navs) return;
// Otherwise, normalize/store the references to these navs
this.references = navs.map(this.normalizeReferences);
}
// Get an item's toggle
getToggle(item) {
return Array.from(item.children).find((child) =>
child.matches(`.${this.selectors.toggle}`)
);
}
// Figures out whether to bind or unbind from an event
get listener() {
return this.run ? 'addEventListener' : 'removeEventListener';
}
// Add the `--has-dropdown` item class to any drop-down items without it
indicateHasDropdown(item) {
// If the item already has the `--has-dropdown` item class, return the item
if (item.classList.contains(this.selectors.itemHasDropdown))
return item;
// Attempt to grab the item's toggle...
const toggle = this.getToggle(item);
// ... and if none exist, return the item
if (!toggle) return item;
// Othwerwise, add the `--has-dropdown` item class to the item...
item.classList.add(this.selectors.itemHasDropdown);
// ... and return it
return item;
}
// Attempt to find/indicate the current item of a nav
findCurrentItem(items) {
// Grab the full/relative path of the current page...
const { href, pathname } = window.location,
// ... and try to find an item with a link with an HREF that matches
currentItem = items.find((item) => {
// Attempt to grab the item's link...
const link = item.querySelector(`.${this.selectors.link}`);
// ... and if it doesn't exist...
if (!link) return false;
// ... or its href doesn't match the full/relative path, return false
if (link.href !== href && link.href !== pathname) return false;
// Otherwise, return true
return true;
});
// If no item was found, return undefined
if (!currentItem) return undefined;
// Grab the current item's link
const currentLink = currentItem.querySelector(
`.${this.selectors.link}`
);
// Otherwise, add the `--is-current` class to it,...
currentItem.classList.add(this.selectors.itemIsCurrent);
// ... indicate via ARIA that this its link goes to the current page,...
currentLink.setAttribute('aria-current', 'page');
// ... and return it
return currentItem;
}
// Find the parent items of the current item
findParentsOfCurrent(item) {
// Get the parent item of the current item...
let parent = item.parentNode.parentNode;
// ... and initialize a parents array
let parents = [];
// While there's a parent item that's an <li> and not a nav controller,...
while (
parent &&
parent.tagName === 'LI' &&
!parent.classList.contains(this.selectors.controller)
) {
// ... push it to the parents array...
parents.push(parent);
// ... and recurse up to the next parent item
parent = parent.parentNode.parentNode;
}
// Finally, return all parents
return parents;
}
// Indicate the current item tree
indicateCurrentItemTree(currentItem, isSubNav) {
// Find all parent items of the current item...
const parents = this.findParentsOfCurrent(currentItem);
// ... and for each,...
parents.forEach((parent) => {
// ... add the current and parent-of-current classes to it
parent.classList.add(
this.selectors.itemIsCurrent,
this.selectors.itemIsParentOfCurrent
);
});
// If the current navigation is a main-nav and the viewport is not at desktop size, or it's a sub nav, do nothing else
if (!isSubNav && !(window.innerWidth < this.desktopBreakpoint)) return;
// Create an array of current items from the current item and its parents...
const currentItems = [currentItem, ...parents];
// ... and for each,...
currentItems.forEach((item) => {
// ... expand it
item.classList.add(this.selectors.itemIsExpanded);
// Next, attempt to find its toggle...
const toggle = this.getToggle(item);
// ... and if it doesn't exist...
if (!toggle) return;
// ... or it's not visible, do nothing else
if (window.getComputedStyle(toggle).display !== 'block') return;
// Otherwise, rotate its icon
this.rotateToggleIcon(toggle, true);
});
}
// Indicate the current items
indicateCurrentItems({ items, isSubNav }) {
// Either store or find the current item...
const currentItem =
items.find((item) => {
item.classList.contains(this.selectors.itemIsCurrent);
}) || this.findCurrentItem(items);
// ... and if one was stored, indicate its current tree
if (currentItem) this.indicateCurrentItemTree(currentItem, isSubNav);
}
// Rotate the toogle icon based on the collapse state.
rotateToggleIcon(toggle, expanded) {
// If there's no toggle, do nothing else
if (!toggle) return;
// Attempt to grab the toggle's icon...
const icon = toggle.getElementsByTagName('i')[0];
// ... and if it doesn't exist, do nothing else
if (!icon) return;
// Finally, toggle the icon's rotation class (add it if expanded, and remove it if collapsed)
icon.classList.toggle(this.selectors.iconRotate, expanded);
}
// Collapse/expand dropdown menus
toggleDropdownMenu({ type, currentTarget }, expand) {
// Check whether the current target is an item...
const isItem = currentTarget.classList.contains(this.selectors.item),
// ... and store the item accordingly,...
item = isItem ? currentTarget : currentTarget.parentElement,
// ... and grab the desktop breakpoint
{ desktopBreakpoint } = this;
// ... and if the window isn't at desktop size when triggered by hovering, do nothing else
if (
['mouseenter', 'mouseleave'].includes(type) &&
!(window.innerWidth > desktopBreakpoint)
)
return;
// Initialize a state and focus variable...
let state, focus;
switch (type) {
case 'mouseenter':
// ... and for 'mouseeneter' events, set state to true and focus to false,...
state = true;
focus = false;
break;
case 'mouseleave':
// ... for 'mouseleave' events, set both to false,...
state = false;
focus = false;
break;
default:
// ... otherwise, set both to the optional expand variable
state = expand;
focus = expand;
}
// Check whether the item is expanded or not...
const expanded = item.classList.contains(this.selectors.itemIsExpanded),
// ... and whether to force it open/close or toggle it
toggle = typeof state === 'boolean' ? state : !expanded;
// Toggle the expand class...
item.classList.toggle(this.selectors.itemIsExpanded, toggle);
// ... and rotate the icon accordingly
this.rotateToggleIcon(item, toggle);
// If nothing is to be focused, do nothing else
if (!focus) return;
// Grab the list and its first link from the current target...
const list = item.querySelector(`.${this.selectors.list}`),
firstLink = list.querySelector(`.${this.selectors.link}`);
// ... and focus it
firstLink.focus();
}
// Focus the previous/next link (with options to collapse the to-be-focused link's parent item)
focusLink(direction, links, from, collapse = false) {
// Store the first/last links,...
const firstLink = links[0],
lastLink = links[links.length - 1];
// If the direction is "first", focus the first link...
if (direction === 'first') return firstLink.focus();
// ... and if the direction is "last", focus the last link
if (direction === 'last') return lastLink.focus();
// Check whether the from element is an item...
const isItem = from.classList.contains(this.selectors.item),
// ... and get the current link accordingly,...
current = isItem
? from.querySelector(`.${this.selectors.link}`)
: from,
// ... and get the index of the current link
index = links.indexOf(current);
// Initialize storage for a link to compare the current link to,...
let compare,
// ... a link to wrap to,,,
wrap,
// ... and the index of the sibling link to move to
sibling;
// Dependending on the direction provided...
switch (direction) {
case 'previous':
// Previous = if the current link is the first link, wrap to the last link, otherwise move to the previous sibling link
compare = firstLink;
wrap = lastLink;
sibling = index - 1;
break;
case 'next':
// Next = if the current link is the last link, wrap to the first link, otherwise move to the next sibling link
compare = lastLink;
wrap = firstLink;
sibling = index + 1;
}
// ... change what link should be focused
const focusable = current === compare ? wrap : links[sibling];
// If collapse has been enabled,...
if (collapse) {
// ... get the parent item of the link to be focused...
const item = focusable.parentElement;
// ... and collapse it if open
this.toggleDropdownMenu({ currentTarget: item }, false);
}
// Finally, focus the appropriate link
return focusable.focus();
}
// Focus an item's element
focusItemElement(element, item) {
// Grab the item element...
const target = item.querySelector(`.${this.selectors[element]}`);
// ... and focus it
target.focus();
}
// Provide advanced keyboard navigation
keyboardNavigation(event) {
// Pull the type and current target from the event
const { type, currentTarget } = event;
// Either add/remove a keydown event listener when the current target is focused/blurred respectively...
switch (type) {
case 'focus':
currentTarget.addEventListener(
'keydown',
this.keyboardNavigation
);
break;
case 'blur':
currentTarget.removeEventListener(
'keydown',
this.keyboardNavigation
);
}
// ... and if the event is not a keydown, do nothing else
if (type !== 'keydown') return;
// Pull the key from the event...
const { key } = event;
// ... and if it's tab...
if (key === 'Tab') return;
// ... or it's not an arrow key/home/end/escape,...
if (
![
'ArrowLeft',
'ArrowUp',
'ArrowRight',
'ArrowDown',
'Home',
'End',
'Escape'
].includes(key)
)
return;
// ... or if there's an outstanding active composition, do nothing else
if (event.isComposing) return;
// Prevent the default behavior
event.preventDefault();
// Store whether the current target is a toggle or not
const isToggle = currentTarget.classList.contains(
this.selectors.toggle
);
// Get the links and sub nav check of the nav containing the current target
const { links, isSubNav } = this.references.find((nav) => {
// If the current target is not a toggle, find a nav with links containing it...
if (!isToggle) return nav.links.includes(currentTarget);
// ... otherwise, find a nav with toggles containing it
return nav.toggles.includes(currentTarget);
});
// Store all visible, focusable links,...
const visibleLinks = links.filter((link) => link.offsetParent !== null),
// ... the current target's parent (item),...
item = currentTarget.parentElement,
// ... if the item has a dropdown,...
hasDropdown = item.classList.contains(
this.selectors.itemHasDropdown
),
// ... if the item is expanded,...
isExpanded = item.classList.contains(this.selectors.itemIsExpanded),
// ... the item's parent (list),...
list = item.parentElement,
// ... the list's level,..
levelMatch = list.className.match(this.selectors.listLevel),
level = levelMatch && levelMatch.length ? Number(levelMatch[1]) : 0,
// ... if the list is not the first level within the nav,...
isNotTopLevel = isSubNav ? level !== 2 : level !== 1,
// ... and store focusable links within the same list
sameListLinks = links.filter(
(link) => link.parentElement.parentElement === list
);
// Main navigation on desktop
if (!isSubNav && !(window.innerWidth < this.desktopBreakpoint)) {
// Level 1
if (level === 1) {
// Toggles
if (isToggle) {
switch (key) {
case 'ArrowLeft':
// Level 1, arrow left = focus the current item's link
return this.focusItemElement('link', item);
case 'ArrowUp':
// Level 1, arrow up = collapse the dropdown
return this.toggleDropdownMenu(
{ currentTarget },
false
);
case 'ArrowRight':
// Level 1, arrow right = focus the next same list link from the current item's link
return this.focusLink(
'next',
sameListLinks,
item,
true
);
}
}
switch (key) {
case 'ArrowLeft':
// Level 1, arrow left = focus previous link within the same list
return this.focusLink(
'previous',
sameListLinks,
currentTarget
);
case 'ArrowUp':
// Level 1, arrow up = nothing
return;
case 'ArrowRight':
// Level 1, arrow right = focus next link within the same list
return this.focusLink(
'next',
sameListLinks,
currentTarget
);
}
// Level 1, arrow down
if (key === 'ArrowDown') {
// Level 1, arrow down, has dropdown = expand dropdown and focus first link
if (hasDropdown)
return this.toggleDropdownMenu({ currentTarget }, true);
// Level 1, arrow down = nothing
return;
}
}
// Level 2
if (level === 2) {
// Toggles
if (isToggle) {
switch (key) {
case 'ArrowLeft':
// Level 2, arrow left = focus the current item's link
return this.focusItemElement('link', item);
case 'ArrowUp':
// Level 2, arrow up = focus previous link within the same list from the current item's link
return this.focusLink(
'previous',
sameListLinks,
item
);
case 'ArrowRight':
// Level 2, arrow right = open the dropdown
return this.toggleDropdownMenu(
{ currentTarget },
true
);
case 'ArrowDown':
// Level 2, arrow down = focus next link within the same list from current item's link
return this.focusLink('next', sameListLinks, item);
}
}
// Level 2, arrow right, has dropdown = expand dropdown and focus first link
if (key === 'ArrowRight' && hasDropdown)
return this.toggleDropdownMenu({ currentTarget }, true);
// Level 2, arrow up, is first link of same list = focus previous visible link and collapse dropdown
if (key === 'ArrowUp' && currentTarget === sameListLinks[0])
return this.focusLink(
'previous',
visibleLinks,
currentTarget,
true
);
}
// Level 3, arrow left = focus previous visible link from first link of the same list and collapse dropdown
if (level === 3 && key === 'ArrowLeft')
return this.focusLink(
'previous',
visibleLinks,
sameListLinks[0],
true
);
// Defaults
switch (key) {
case 'ArrowUp':
// Arrow up = focus previous link within the same list
return this.focusLink(
'previous',
sameListLinks,
currentTarget
);
case 'ArrowDown':
// Arrow down = focus next link within the same list
return this.focusLink('next', sameListLinks, currentTarget);
case 'Home':
// Home = focus first link within the same list
return this.focusLink('first', sameListLinks);
case 'End':
// End = focus last link within the same list
return this.focusLink('last', sameListLinks);
}
}
// All toggles
if (isToggle) {
// Arrow up
if (key === 'ArrowUp') {
// Arrow up, item is expanded = collapse the dropdown
if (isExpanded)
return this.toggleDropdownMenu({ currentTarget }, false);
// Arrow up = focus the previous visible link from the current item's link
return this.focusLink('previous', visibleLinks, item, true);
}
// Defaults
switch (key) {
case 'ArrowLeft':
// Arrow left = focus the current item's link
return this.focusItemElement('link', item);
case 'ArrowDown':
// Arrow down = expand the dropdown
return this.toggleDropdownMenu({ currentTarget }, true);
}
}
// All navigation
switch (key) {
case 'ArrowUp':
// Arrow up = focus previous visible link
return this.focusLink('previous', visibleLinks, currentTarget);
case 'ArrowDown':
// Arrow down = focus next visible link
return this.focusLink('next', visibleLinks, currentTarget);
case 'Home':
// Home = focus first visible link
return this.focusLink('first', visibleLinks);
case 'End':
// End = focus last visible link
return this.focusLink('last', visibleLinks);
}
// Not top level list, escape = focus previous visible link from first link of same list and collapse
if (isNotTopLevel && key === 'Escape')
return this.focusLink(
'previous',
visibleLinks,
sameListLinks[0],
true
);
// Arrow right, has dropdown = focus the item's toggle
if (key === 'ArrowRight' && hasDropdown)
return this.focusItemElement('toggle', item);
}
// Bind to every nav's toggle's click event
togglesOnClickFocusBlur({ toggles }) {
// If it has none, do nothing else
if (!toggles.length) return;
// Otherwiser, for each toggle,...
toggles.forEach((toggle) => {
// ... toggle its sub menu when clicked, and provide advanced keyboard navigation when focused/blurred
toggle[this.listener]('click', this.toggleDropdownMenu);
toggle[this.listener]('focus', this.keyboardNavigation);
toggle[this.listener]('blur', this.keyboardNavigation);
});
}
// Bind to every link's focus/blur events
linksOnFocusBlur({ links }) {
// If no links exist, do nothing else
if (!links.length) return;
// Otherwise, for each link,...
links.forEach((link) => {
// ... provide advanced keyboard nevigation when focused (and remove it when blurred)
link[this.listener]('focus', this.keyboardNavigation);
link[this.listener]('blur', this.keyboardNavigation);
});
}
// Bind to every main nav's item's mouse enter/leave event if they have dropdowns
itemsWithDropdownsOnHover({ items }) {
// If no items exist, do nothing else
if (!items.length) return;
// Otherwise, filter the items that have dropdowns...
items
.filter((item) =>
item.classList.contains(this.selectors.itemHasDropdown)
)
.forEach((item) => {
// ... and toggle its dropdown menu on mouse enter/leave
item[this.listener]('mouseenter', this.toggleDropdownMenu);
item[this.listener]('mouseleave', this.toggleDropdownMenu);
});
}
// Start or stop core functionality
allFunctionality() {
// For each nav,...
this.references.forEach((nav) => {
// ... indicate the current items and their trees...
this.indicateCurrentItems(nav);
// ... bind to every nav's toggle's click event,...
this.togglesOnClickFocusBlur(nav);
// ... and bind to every link's focus/blur events
this.linksOnFocusBlur(nav);
});
}
// Collapse/expand the main menu
toggleMainMenu({ currentTarget }) {
// Change the default behavior whether the current target is a link or not
const isLink = currentTarget.classList.contains(this.selectors.link),
operation = isLink ? 'remove' : 'toggle',
listener = isLink ? 'removeEventListener' : 'addEventListener';
// Toggle the show main nav class on the document body
document.body.classList[operation](this.selectors.showMainNav);
// Finally, get the links from the nav of the current target...
const { links } = this.references.find((nav) => {
// If the current target is not a link, find a nav with a hamburger button or container that matches the current target...
if (!isLink)
return (
nav.hamburger === currentTarget ||
nav.container === currentTarget
);
// ... otherwise, find a nav with links that include the current target
return nav.links.includes(currentTarget);
});
// ... and toggle the main menu when a link is clicked
links.forEach((link) => {
link[listener]('click', this.toggleMainMenu);
});
}
// Stop event propagation
stopPropagation(event) {
event.stopPropagation();
}
// Bind to a main nav's hamburger button click event
hamburgerOnClick({ hamburger }) {
// If no hamburger button exists, do nothing else
if (!hamburger) return;
// Otherwise, toggle the main menu when its clicked
hamburger[this.listener]('click', this.toggleMainMenu);
}
// Bind to the nav container's click event
containerOnClick({ container, controller }) {
// If no container or controller exist, do nothing else
if (!container || !controller) return;
// Otherwise, toggle the main menu when the overflow container is clicked...
container[this.listener]('click', this.toggleMainMenu);
// ... and stop event propagation if the nav controller is clicked
controller[this.listener]('click', this.stopPropagation);
}
// Start or stop the main navigation functionality
mainFunctionality() {
// For each main nav...
this.references
.filter(({ isSubNav }) => !isSubNav)
.forEach((nav) => {
// ... bind to its item's mouse enter/leave event if they have dropdowns,...
this.itemsWithDropdownsOnHover(nav);
// ... hamburger's click event,...
this.hamburgerOnClick(nav);
// ... and nav container's click event
this.containerOnClick(nav);
});
}
// Adjusts the opacity of the visual scroll indicators of a nav list based on the scroll position
indicateScrollDirection({ target }) {
// Grab the relavent scroll information from the target,...
const { scrollTop, scrollHeight, clientHeight } = target,
// ... how far it's scrolled from the top,
top = Math.round(scrollTop);
// ... and the maximum distance of scrolling
let max = scrollHeight - clientHeight;
// Normalize the maximum distance to zero (if lower)
if (max < 0) max = 0;
// Figure out the percent scrolled from the top/bottom
const percentFromTop = (top / max || 0).toFixed(2),
percentFromBottom = ((max - top) / max || 0).toFixed(2);
// Set the scroll top...
target.style.setProperty(this.properties.listScrollTop, percentFromTop);
// ... and scroll bottom CSS custom property of the list to correspond with opacity
target.style.setProperty(
this.properties.listScrollBottom,
percentFromBottom
);
}
scrollToCurrentItem(list) {
const current = list.querySelector(`.${this.selectors.itemIsCurrent}`);
if (!current) return;
const { offsetTop } = current;
list.scrollTop = offsetTop;
}
// Adds visual indicators that a nav list is scrollable
indicateScrollable({ lists }) {
// If no lists exists, do nothing else
if (!lists.length) return;
// For each list,...
lists.forEach((list) => {
// If the list isn't scrollable, return it as is
if (!list.classList.contains(this.selectors.scrollable))
return list;
// Otherwise, scroll to the current item...
this.scrollToCurrentItem(list);
// ... and indicate the scroll direction up front...
this.indicateScrollDirection({ target: list });
// ... and whenever the list is scrolled thereafter
list[this.listener](
'scroll',
this.indicateScrollDirection.bind(this)
);
});
}
// Start or stop the sub navigation functionality
subFunctionality() {
// For each sub nav...
this.references
.filter(({ isSubNav }) => isSubNav)
.forEach((nav) => {
this.indicateScrollable(nav);
});
}
// Destroy the navigation functionality
destroy() {
// Set the run flag to false
this.run = false;
// If no container exists,...
if (!this.references.length) {
// ... simply delete the settings/references...
delete this.settings;
delete this.references;
// ... and do nothing else
return;
}
// Otherwse, remove core functionality of all navs...
this.allFunctionality();
// ... and special functionality of main...
this.mainFunctionality();
// ... and sub navs
this.subFunctionality();
// Finally, delete the settings/references
delete this.settings;
delete this.references;
}
// Initialize the navigation functionality
initialize() {
// Set the run flag to true
this.run = true;
// First, get all references to the navs and their lists/items
this.getReferences();
// Next, provide core functionality to all navs
this.allFunctionality();
// Finally, provide special functionality to main...
this.mainFunctionality();
// ... and sub navs
this.subFunctionality();
}
}
export default Nav;
No notes defined.