import { css, html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
interface NavLink {
readonly href: string;
readonly label: string;
}
const LINKS: readonly NavLink[] = [
{ href: '/', label: 'Home' },
{ href: '/about', label: 'About' },
];
/**
* Site navigation.
*
* The router intercepts these clicks, so these are ordinary anchors. Keeping
* them as real `` elements means middle-click, ctrl-click, "open in
* new tab" and screen-reader link navigation all keep working.
*/
@customElement('app-nav')
export class AppNav extends LitElement {
static override styles = css`
:host {
display: block;
margin-bottom: 2rem;
}
nav {
display: flex;
gap: 0.25rem;
border-bottom: 1px solid var(--app-color-border, #e3e3e8);
padding-bottom: 0.75rem;
}
a {
padding: 0.35rem 0.75rem;
border-radius: var(--app-radius, 0.5rem);
color: var(--app-color-muted, #63636b);
text-decoration: none;
font-size: 0.95rem;
}
a:hover {
color: var(--app-color-text, #18181b);
background: color-mix(in srgb, currentColor 8%, transparent);
}
a:focus-visible {
outline: 2px solid var(--app-color-accent, #3b5bdb);
outline-offset: 2px;
}
/* aria-current is the accessible source of truth, so style from it. */
a[aria-current='page'] {
color: var(--app-color-accent, #3b5bdb);
background: color-mix(in srgb, var(--app-color-accent, #3b5bdb) 12%, transparent);
font-weight: 600;
}
`;
@state()
private currentPath = window.location.pathname;
override connectedCallback(): void {
super.connectedCallback();
// The router pushes state on link clicks, which does not fire popstate,
// so listen for both that and real back/forward navigation.
window.addEventListener('popstate', this.#syncPath);
window.addEventListener('click', this.#syncPathSoon);
}
override disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener('popstate', this.#syncPath);
window.removeEventListener('click', this.#syncPathSoon);
}
override render() {
return html`
`;
}
#syncPath = (): void => {
this.currentPath = window.location.pathname;
};
// Runs after the router's own click handler has pushed the new URL.
#syncPathSoon = (): void => {
queueMicrotask(this.#syncPath);
};
}
declare global {
interface HTMLElementTagNameMap {
'app-nav': AppNav;
}
}