document.head.append(Object.assign(document.createElement('link'), { rel: 'stylesheet', href: '/elements/menu-app/slot.css' })); { const template = ` `; const css1 = ` :root { --backgroundColor: #f2f2f2; --headerFontColor: #0570B4; } body { background-color: var(--backgroundColor); } h1, h2, h3, h4 { color: var(--headerFontColor); } `; const css2 = ` :host([hidden]), [hidden], .hidden { display: none !important; } :host { --panel-bg: var(--backgroundColor, #fff); --overlay-bg: rgba(5, 112, 180, 0.15); --blur-radius: 8px; --max-width: 600px; --min-width: 220px; --edge-gap: 16px; --transition: 500ms cubic-bezier(0.2, 0.8, 0.2, 1); z-index: 10001; 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; height: 44px; padding: 6px; display: inline-flex; align-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 .icon-icon { display: flex; flex-direction: column; justify-content: center; gap: 6px; padding: 0 3px; } .menu-toggle .icon-icon .bar { display: block; width: 26px; height: 2px; background: var(--headerFontColor); border-radius: 2px; } .menu-toggle .icon-icon .top { transform-origin: left center; margin-bottom: 6px; } .menu-toggle .icon-icon .middle { margin-bottom: 6px; } .menu-toggle .icon-icon .bottom { transform-origin: left center; } .menu-toggle .menu-label { display: inline-block; overflow: hidden; white-space: nowrap; width: 0; opacity: 0; font-size: 1.8em; color: var(--headerFontColor); transition: width 0.8s ease, opacity 0.8s ease; } .menu-toggle .menu-label.show { opacity: 1; } .panel { position: absolute; top: var(--edge-gap); /* bottom: var(--edge-gap); */ right: var(--edge-gap); background: var(--panel-bg); box-shadow: -8px 8px 24px rgba(0, 0, 0, 0.12); border-radius: 12px; overflow: auto; transform: translateX(110%); transition: transform var(--transition); max-width: var(--max-width); /* Set explicit max-width from variable */ width: clamp(var(--min-width), 90vw, var(--max-width)); /* Use clamp for responsive width */ /* 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: 2px solid var(--headerFontColor, #0570B4); background: linear-gradient(135deg, var(--headerFontColor, #0570B4), rgba(5, 112, 180, 0.8)); .title { color: #fff; margin: 0; font-size: 18px; font-weight: 600; } .close-btn { background: rgba(255, 255, 255, 0.2); border: none; color: #fff; font: inherit; cursor: pointer; padding: 8px; border-radius: 6px; font-size: 24px; transition: background 0.2s ease; } .close-btn:hover { background: rgba(255, 255, 255, 0.3); } .close-btn:focus { outline: 2px solid rgba(255, 255, 255, 0.5); } } .content { padding: 12px 16px 24px 16px; nav ul { list-style: none; margin: 0; padding: 8px 0; } nav ul li a { display: block; padding: 12px 8px; color: var(--headerFontColor, #0570B4); text-decoration: none; border-radius: 6px; transition: background 0.2s ease, color 0.2s ease; font-weight: 500; } nav ul li a:hover, nav ul li a:focus { background: rgba(5, 112, 180, 0.1); color: #024f7a; outline: none; } nav ul li a:active { background: rgba(5, 112, 180, 0.2); } } /* Responsive: if viewport is narrow, panel can take more space but still limited */ @media (max-width: 480px) { .panel { right: 8px; width: calc(100% - 16px); max-width: none; border-radius: 10px; } } `; window.customElements.define( "menu-app", 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; this.shadowRoot.adoptedStyleSheets = [css1, css2].map(css => ((sheet => (sheet.replaceSync(css), sheet))(new CSSStyleSheet()))); this.MenuLabel = this.shadowRoot.getElementById("MenuLabel"); this.Close = this.shadowRoot.getElementById("Close"); this.Overlay = this.shadowRoot.getElementById("Overlay"); this.Panel = this.shadowRoot.getElementById("Panel"); this.Close.onclick = () => (this.open = false); this.Overlay.onclick = () => (this.open = false); this._onKey = this._onKey.bind(this); } connectedCallback() { if (this.MenuLabel && currentPath == "") { this.showMenuLabelAnimation(); } } openMenu() { document.body.style.overflow = "hidden"; this.removeAttribute("hidden"); this.Overlay.setAttribute("aria-hidden", "false"); this.Panel.setAttribute("aria-hidden", "false"); document.addEventListener("keydown", this._onKey); } closeMenu() { this.Overlay.setAttribute("aria-hidden", "true"); document.removeEventListener("keydown", this._onKey); document.body.style.overflow = ""; document.querySelector('header-app').shadowRoot.getElementById('LoginIcon').focus(); this.Panel.setAttribute("aria-hidden", "true"); } _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("menu-app")[0]?.removeAttribute("open"); }); }