Der Chat als Seitenleiste
Das Muster aus Portalen und Web-Apps: Der Chat gehört zur Oberfläche, soll aber nicht dauernd sichtbar sein. Ein Button im Seitenkopf öffnet ein Panel, das sich von rechts über die Seite schiebt. Darin das eingebettete Widget.
Einmal rendern, nur das Panel bewegen
Der Trick: Das Panel steht von Anfang an im DOM, nur eben ausserhalb des Viewports geparkt (translate-x-full). So findet der Embedded-Render einen Container mit echten Massen vor, und die Konversation bleibt beim Öffnen und Schliessen geladen. Nichts flackert nach.
<aside id="hilfe-panel" class="panel" role="dialog" aria-modal="true" aria-label="Hilfe & Support">
<div id="hilfe-panel-widget"></div>
</aside>
zE('messenger', 'render', {
mode: 'embedded',
widget: { targetElement: '#hilfe-panel-widget' },
});
hilfeKnopf.addEventListener('click', () => panel.classList.add('offen'));
.panel {
position: fixed;
inset-block: 0;
right: 0;
width: min(420px, 92vw);
transform: translateX(100%);
transition: transform 0.3s ease;
}
.panel.offen {
transform: none;
}
Barrierefreiheit gehört zum Bauplan
- Das Panel ist ein Dialog:
role="dialog"+aria-modal="true". - Geschlossen bekommt es das
inert-Attribut: keine Tab-Stopps in ein unsichtbares Panel, auch nicht ins Widget-iframe darin. - Escape und Klick auf den Backdrop schliessen; der Fokus springt beim Öffnen auf den Schliessen-Knopf und beim Schliessen zurück auf den Auslöser.
- Die Transition läuft nur bei
prefers-reduced-motion: no-preference.
Wo das Muster trägt
Hilfe-Center, Kundenportale, interne Tools, E-Commerce-Produktseiten: überall dort, wo ein aufdringlicher Floating-Button stört, der Chat aber einen Klick entfernt sein soll. Auf Mobilgeräten wird aus der Seitenleiste automatisch ein fast vollflächiges Panel (min(420px, 92vw)).