Deskwerk· Workshop

Demos

Chat as a sidebar

Try the demo →

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 inert attribute: 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)).

← Back to the demo