Chat as a sidebar
The pattern you know from portals and web apps: chat belongs to the interface, but it shouldn’t sit on screen the whole time. A button in the page header opens a panel that slides in from the right, over the page. The embedded widget sits inside it.
Render once, move only the panel
The trick: the panel is in the DOM from the start, just parked outside the viewport (translate-x-full). That way the embedded render finds a container with real dimensions, and the conversation stays loaded as you open and close it. Nothing flickers back in.
<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;
}
Accessibility is part of the blueprint
- The panel is a dialog:
role="dialog"+aria-modal="true". - When closed, it gets the
inertattribute: no tab stops into an invisible panel, and none into the widget iframe inside it either. - Escape and a click on the backdrop close it; focus jumps to the close button on open, and back to the trigger on close.
- The transition only runs under
prefers-reduced-motion: no-preference.
Where the pattern holds up
Help centers, customer portals, internal tools, e-commerce product pages: anywhere a pushy floating button gets in the way but you still want chat one click away. On mobile, the sidebar turns into a near-full-width panel on its own (min(420px, 92vw)).