Skip to content

feat(mermaid): configurable portal target for Mermaid fullscreen (micro-frontends & prefixed CSS) #499

@slavco86

Description

@slavco86

Problem

Streamdown is often embedded in micro-frontend setups: the markdown renderer runs inside a child app or isolated subtree of a larger shell, not as the only root on the page.

Those hosts frequently rely on:

  • Scoped CSS — e.g. a wrapper class around the sub-app so design tokens and utilities apply only there.
  • Prefixed Tailwind — e.g. Tailwind v4 prefix() or similar, so generated classes are tw:flex, tw:fixed, etc., and only match elements under the scanned scope.

Mermaid fullscreen is implemented with createPortal, typically to document.body. The fullscreen layer is then outside the micro-frontend’s DOM subtree. As a result:

  • Prefixed / scoped utility classes from the host may not apply to nodes under document.body, so layout, positioning, and fixed / z-index behavior can be wrong or missing.
  • Stacking can break: the overlay may render under shell chrome (headers, footers, sidebars) that live in a different part of the tree with higher z-index.

This is the same portal vs in-tree class of issue as elsewhere in Streamdown (e.g. table fullscreen and DOM context — see #468 / PR #470).

Separately, #490 proposes host callbacks to replace download/fullscreen (e.g. desktop webviews). That addresses hosts that want to own the action. Many embedded apps instead need to keep the default fullscreen UI but have it mount inside their subtree so prefix + scope still apply.

Proposed solution

Add an optional, backward-compatible API to control where the Mermaid fullscreen portal mounts:

  • Name (example): fullscreenPortalContainer or mermaid.fullscreen.portalContainer
  • Type: HTMLElement | null or () => HTMLElement | null (match idioms you use elsewhere).
  • Behavior: When set, Mermaid fullscreen (and any overlay that should share the same stacking context) portals into this element instead of document.body.
  • Default: document.body — unchanged for existing consumers.

Docs: Short note for micro-frontends and prefixed / scoped Tailwind: provide a portal root element inside the same wrapper as <Streamdown /> and pass it here so fullscreen stays in the same DOM and CSS scope as the rest of the sub-app.

Alternatives considered

  • Relying only on global CSS / safelist: Duplicates tokens, fights prefixing, and drifts from the shell’s theme.
  • Replacing the entire Mermaid fence via a custom renderer: Too heavy for fixing portal placement alone.

Related issues

Contribution

Happy to help with a PR (API surface + tests) once maintainers agree on naming and placement on Streamdown vs the Mermaid plugin.


Pull request: #500

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions