import { LitElement, html, css } from "lit"; import { customElement } from "lit/decorators.js"; export interface NavItem { label: string; href: string; } /** * * Top navigation bar with site title, nav links, and dark/light theme toggle. */ @customElement("starlight-header") export class StarlightHeader extends LitElement { static override properties = { siteTitle: { type: String }, nav: { type: Array }, currentPath: { type: String }, navOpen: { type: Boolean }, hasSidebar: { type: Boolean }, _theme: { type: String, state: true }, }; static override styles = css` :host { display: block; position: sticky; top: 0; z-index: 100; } header { height: var(--sl-nav-height, 3.5rem); background-color: var(--sl-color-bg-nav, #fff); border-bottom: 1px solid var(--sl-color-border, #e8e8e8); display: flex; align-items: center; padding: 0 var(--sl-content-pad-x, 1.5rem); gap: 1rem; } .menu-btn { display: none; appearance: none; background: none; border: 1px solid var(--sl-color-border, #e8e8e8); border-radius: var(--sl-border-radius, 0.375rem); width: 2.25rem; height: 2.25rem; align-items: center; justify-content: center; cursor: pointer; color: var(--sl-color-text, #23262f); transition: background-color 0.15s; flex-shrink: 0; padding: 0; } .menu-btn:hover { background-color: var(--sl-color-gray-2, #e8e8e8); } .menu-btn svg { width: 1.1rem; height: 1.1rem; } @media (max-width: 72rem) { .menu-btn { display: flex; } } .site-title { font-size: var(--sl-text-lg, 1.125rem); font-weight: 700; color: var(--sl-color-text, #23262f); text-decoration: none; white-space: nowrap; } .site-title:hover { opacity: 0.85; } nav { display: flex; align-items: center; gap: 0.25rem; flex: 1; } nav a { padding: 0.35rem 0.75rem; font-size: var(--sl-text-sm, 0.875rem); font-weight: 500; color: var(--sl-color-gray-5, #4b4b4b); text-decoration: none; border-radius: var(--sl-border-radius, 0.375rem); transition: color 0.15s, background-color 0.15s; } nav a:hover { color: var(--sl-color-text, #23262f); background-color: var(--sl-color-gray-2, #e8e8e8); } nav a[aria-current="page"] { color: var(--sl-color-accent, #7c3aed); background-color: var(--sl-color-accent-low, #ede9fe); } .theme-toggle { margin-left: auto; appearance: none; background: none; border: 1px solid var(--sl-color-border, #e8e8e8); border-radius: var(--sl-border-radius, 0.375rem); width: 2.25rem; height: 2.25rem; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 1rem; color: var(--sl-color-text, #23262f); transition: background-color 0.15s; flex-shrink: 0; } .theme-toggle:hover { background-color: var(--sl-color-gray-2, #e8e8e8); } `; siteTitle = ""; nav: NavItem[] = []; currentPath = ""; navOpen = false; hasSidebar = false; _theme = "light"; override firstUpdated() { const stored = (typeof localStorage !== "undefined" ? localStorage.getItem("sl-theme") : null) ?? "light"; this._theme = stored; if (typeof document !== "undefined") { document.documentElement.setAttribute("data-theme", stored); } } private _toggleTheme() { const next = this._theme === "light" ? "dark" : "light"; this._theme = next; if (typeof localStorage !== "undefined") { localStorage.setItem("sl-theme", next); } if (typeof document !== "undefined") { document.documentElement.setAttribute("data-theme", next); } } private _toggleNav() { this.dispatchEvent( new CustomEvent("sl-nav-toggle", { bubbles: true, composed: true }), ); } override render() { const icon = this._theme === "dark" ? "☀️" : "🌙"; const label = this._theme === "dark" ? "Switch to light mode" : "Switch to dark mode"; return html`
${this.hasSidebar ? html` ` : ""} ${this.siteTitle}
`; } } export default StarlightHeader;