<a href="#main-content" class="skip-link">Skip to main content</a>
<nav class="navbar" role="navigation" aria-label="Primary navigation">
<div class="navbar__container container">
<a href="{{ '/' | relative_url }}" class="navbar__brand" aria-label="Caxton home">
<img src="{{ '/assets/img/logo.svg' | relative_url }}"
alt=""
class="navbar__logo"
width="32"
height="32"
loading="eager">
<span class="navbar__title">Caxton</span>
</a>
<button class="navbar__toggle"
id="mobile-menu-toggle"
aria-expanded="false"
aria-controls="navbar-menu"
aria-label="Toggle navigation menu">
<span class="navbar__toggle-icon" aria-hidden="true">
<span></span>
<span></span>
<span></span>
</span>
</button>
<div class="navbar__menu" id="navbar-menu">
<ul class="navbar__list" role="list">
{% for item in site.navigation.main %}
<li class="navbar__item">
{% if item.external %}
<a href="{{ item.url }}"
class="navbar__link"
target="_blank"
rel="noopener noreferrer">
{{ item.title }}
<svg class="navbar__external-icon"
width="12"
height="12"
viewBox="0 0 12 12"
fill="currentColor"
aria-label="Opens in new window">
<path d="M10.5 1.5v3h-1v-2L5 7l-.7-.7L8.8 2h-2v-1h3z"/>
<path d="M9.5 7.5v3h-8v-8h3v-1h-4v10h10v-4h-1z"/>
</svg>
</a>
{% else %}
<a href="{{ item.url | relative_url }}"
class="navbar__link {% if page.url == item.url %}navbar__link--active{% endif %}"
{% if page.url == item.url %}aria-current="page"{% endif %}>
{{ item.title }}
</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
</nav>
<style>
.navbar {
position: sticky;
top: 0;
z-index: var(--z-sticky);
background-color: var(--bg-secondary);
border-bottom: 1px solid var(--color-surface0);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.navbar__container {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-4) var(--space-6);
@media (max-width: 768px) {
padding: var(--space-3) var(--space-4);
}
}
.navbar__brand {
display: flex;
align-items: center;
gap: var(--space-3);
text-decoration: none;
color: var(--text-primary);
font-weight: var(--font-semibold);
font-size: var(--font-size-lg);
transition: opacity var(--transition-fast);
&:hover {
opacity: 0.8;
text-decoration: none;
}
&:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 4px;
border-radius: var(--radius-sm);
}
}
.navbar__logo {
width: 32px;
height: 32px;
display: block;
}
.navbar__title {
@media (max-width: 480px) {
display: none;
}
}
.navbar__toggle {
display: none;
background: transparent;
border: none;
padding: var(--space-2);
cursor: pointer;
color: var(--text-primary);
@media (max-width: 768px) {
display: flex;
align-items: center;
justify-content: center;
}
&:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
}
.navbar__toggle-icon {
display: flex;
flex-direction: column;
gap: 4px;
width: 24px;
height: 24px;
position: relative;
span {
display: block;
width: 100%;
height: 2px;
background-color: currentColor;
transition: all var(--transition-fast);
transform-origin: center;
}
.navbar__toggle[aria-expanded="true"] & {
span:nth-child(1) {
transform: translateY(6px) rotate(45deg);
}
span:nth-child(2) {
opacity: 0;
}
span:nth-child(3) {
transform: translateY(-6px) rotate(-45deg);
}
}
}
.navbar__menu {
display: flex;
align-items: center;
gap: var(--space-8);
@media (max-width: 768px) {
position: fixed;
top: 60px;
left: 0;
right: 0;
bottom: 0;
background-color: var(--bg-secondary);
flex-direction: column;
padding: var(--space-6);
transform: translateX(-100%);
opacity: 0;
visibility: hidden;
transition: all var(--transition-base);
overflow-y: auto;
&.navbar__menu--open {
transform: translateX(0);
opacity: 1;
visibility: visible;
}
}
}
.navbar__list {
display: flex;
align-items: center;
gap: var(--space-6);
list-style: none;
margin: 0;
padding: 0;
@media (max-width: 768px) {
flex-direction: column;
width: 100%;
gap: var(--space-2);
}
}
.navbar__item {
@media (max-width: 768px) {
width: 100%;
}
}
.navbar__link {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-2) var(--space-3);
color: var(--text-secondary);
text-decoration: none;
font-weight: var(--font-medium);
border-radius: var(--radius-md);
transition: all var(--transition-fast);
position: relative;
@media (max-width: 768px) {
width: 100%;
padding: var(--space-3) var(--space-4);
font-size: var(--font-size-md);
}
&:hover {
color: var(--text-primary);
background-color: var(--bg-surface);
text-decoration: none;
}
&:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
&--active {
color: var(--color-primary);
&::after {
content: '';
position: absolute;
bottom: -2px;
left: var(--space-3);
right: var(--space-3);
height: 2px;
background-color: var(--color-primary);
border-radius: var(--radius-full);
@media (max-width: 768px) {
display: none;
}
}
@media (max-width: 768px) {
background-color: var(--bg-surface);
}
}
}
.navbar__external-icon {
opacity: 0.7;
margin-left: var(--space-1);
}
.navbar__overlay {
display: none;
@media (max-width: 768px) {
display: block;
position: fixed;
top: 60px;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
opacity: 0;
visibility: hidden;
transition: all var(--transition-base);
z-index: var(--z-overlay - 1);
&.navbar__overlay--visible {
opacity: 1;
visibility: visible;
}
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const toggle = document.getElementById('mobile-menu-toggle');
const menu = document.getElementById('navbar-menu');
const links = menu.querySelectorAll('.navbar__link');
let isOpen = false;
const overlay = document.createElement('div');
overlay.className = 'navbar__overlay';
document.body.appendChild(overlay);
function toggleMenu() {
isOpen = !isOpen;
toggle.setAttribute('aria-expanded', isOpen);
menu.classList.toggle('navbar__menu--open', isOpen);
overlay.classList.toggle('navbar__overlay--visible', isOpen);
document.body.style.overflow = isOpen ? 'hidden' : '';
if (isOpen) {
menu.querySelector('.navbar__link').focus();
} else {
toggle.focus();
}
}
function closeMenu() {
if (isOpen) {
toggleMenu();
}
}
toggle.addEventListener('click', toggleMenu);
overlay.addEventListener('click', closeMenu);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && isOpen) {
closeMenu();
}
});
links.forEach(link => {
link.addEventListener('click', function() {
if (window.innerWidth <= 768) {
closeMenu();
}
});
});
let resizeTimer;
window.addEventListener('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
if (window.innerWidth > 768 && isOpen) {
closeMenu();
}
}, 250);
});
menu.addEventListener('keydown', function(e) {
if (!isOpen) return;
if (e.key === 'Tab') {
const focusableElements = menu.querySelectorAll(
'a, button, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
});
</script>