document.head.append(Object.assign(document.createElement('link'), { rel: 'stylesheet', href: '/Home/home-menu/slot.css' })); { const template = ` `; const css2 = ` :root { --backgroundColor: #f2f2f2; --headerFontColor: #0570B4; } body { background-color: var(--backgroundColor); } h1, h2, h3, h4 { color: var(--headerFontColor); } `; const css3 = ` :host([hidden]), [hidden], .hidden { display: none !important; } :host { --panel-bg: #fff; --overlay-bg: rgba(255, 255, 255, 0.4); --blur-radius: 8px; --max-width: 420px; --min-width: 220px; --edge-gap: 16px; --transition: 500ms cubic-bezier(0.2, 0.8, 0.2, 1); z-index: 1000; display: block; position: fixed; inset: 0; pointer-events: none; } :host([open]) { pointer-events: auto; } .overlay { position: absolute; inset: 0; background: var(--overlay-bg); backdrop-filter: blur(var(--blur-radius)); -webkit-backdrop-filter: blur(var(--blur-radius)); transition: opacity var(--transition); opacity: 0; } :host([open]) .overlay { opacity: 1; } .menu-toggle { pointer-events: auto; position: absolute; margin: 1em; width: 44px; height: 44px; padding: 6px; display: inline-grid; place-items: center; background: rgba(250, 250, 250, 0.25); backdrop-filter: blur(var(--blur-radius)); border: 0; border-radius: 8px; cursor: pointer; box-sizing: border-box; } .menu-toggle:focus { outline: 2px solid rgba(5, 112, 180, 0.25); } .menu-toggle .bar { display: block; width: 26px; height: 2px; background: var(--headerFontColor); border-radius: 2px; transition: transform 220ms ease, opacity 180ms ease; } .menu-toggle .top { transform-origin: left center; margin-bottom: 6px; } .menu-toggle .middle { margin-bottom: 6px; } .menu-toggle .bottom { transform-origin: left center; } .panel { position: absolute; top: var(--edge-gap); /* bottom: var(--edge-gap); */ left: var(--edge-gap); background: var(--panel-bg); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); border-radius: 12px; overflow: auto; transform: translateX(-110%); transition: transform var(--transition); max-width: calc(min(var(--max-width), 90vw)); width: min(90vw, max(var(--min-width), 320px)); /* Inhalt bestimmt Höhe automatisch; panel bleibt eingeklappt wenn leer */ } :host([open]) .panel { transform: translateX(0); } .header { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 16px; border-bottom: 1px solid rgba(0, 0, 0, 0.06); .title { color: var(--headerFontColor); } .close-btn { background: none; border: none; font: inherit; cursor: pointer; padding: 8px; border-radius: 6px; } .close-btn:focus { outline: 2px solid Highlight; } } .content { padding: 12px 16px 24px 16px; nav ul { list-style: none; margin: 0; padding: 8px 0; } } /* Responsive: if viewport is narrow, panel can take more space but still limited */ @media (max-width: 480px) { .panel { left: 8px; right: 8px; width: calc(100% - 16px); max-width: none; border-radius: 10px; } } `; window.customElements.define( "home-menu", class extends HTMLElement { static observedAttributes = ["open"]; attributeChangedCallback(name, oldValue, newValue) { if (name === "open") { if (this.open) this.openMenu(); else this.closeMenu(); } } get open() { return this.hasAttribute("open"); } set open(val) { if (val) this.setAttribute("open", ""); else this.removeAttribute("open"); } get MenuItems() { return this.shadowRoot.querySelector("slot").assignedElements(); } constructor() { super(); this.attachShadow({ mode: "open" }); this.shadowRoot.innerHTML = template; const style2 = new CSSStyleSheet(); const style3 = new CSSStyleSheet(); style2.replaceSync(css2); style3.replaceSync(css3); this.shadowRoot.adoptedStyleSheets = [style2, style3]; this.MenuToggle = this.shadowRoot.getElementById("MenuToggle"); this.Close = this.shadowRoot.getElementById("Close"); this.Overlay = this.shadowRoot.getElementById("Overlay"); this.Panel = this.shadowRoot.getElementById("Panel"); this.MenuToggle.onclick = () => { const willOpen = !this.open; this.open = willOpen; this.MenuToggle.setAttribute("aria-expanded", String(willOpen)); this.Overlay.setAttribute("aria-hidden", String(!willOpen)); this.Panel.setAttribute("aria-hidden", String(!willOpen)); }; this.Close.onclick = () => (this.open = false); this.Overlay.onclick = () => (this.open = false); this._onKey = this._onKey.bind(this); } connectedCallback() { if (!this.hasAttribute("hidden") && location.pathname.replace(/\/+$/, "") === "") { window.addEventListener("scroll", () => { this.toggleAttribute("hidden", window.scrollY > window.innerHeight * 0.3); }); } } openMenu() { document.addEventListener("keydown", this._onKey); requestAnimationFrame(() => { const current = location.pathname.replace(/\/+$/, ""); let target = this.Close; for (const li of this.MenuItems) { const a = li.querySelector("a[href]"); if (!a) continue; const path = new URL(a.getAttribute("href"), location.href).pathname.replace(/\/+$/, ""); if (path === current) { target = a; break; } } target.focus(); target.setAttribute("aria-current", "page"); }); document.body.style.overflow = "hidden"; } closeMenu() { this.MenuToggle.focus(); document.removeEventListener("keydown", this._onKey); document.body.style.overflow = ""; } _onKey(e) { if (e.key === "Escape") { this.open = false; return; } if (e.key === "Tab") { let focusable = this.MenuItems.map((li) => li.firstChild); focusable.unshift(this.Close); if (focusable.length === 0) { e.preventDefault(); this.Panel.focus(); return; } const first = focusable[0]; const last = focusable[focusable.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } } } ); document.addEventListener("visibilitychange", () => { document.getElementsByTagName("home-menu")[0].removeAttribute("open"); }); }