Skip to content

Code block copy/download buttons unclickable in nested scroll containers (sticky + pointer-events-none) #494

@hebo-claude-assistant

Description

@hebo-claude-assistant

Description

When streamdown is embedded in a layout with two or more nested scroll containers (e.g., an outer overflow-auto sidebar + an inner overflow-auto chat scroll), the code block copy/download buttons are visible but unclickable.

Root cause

Streamdown renders code block buttons using a sticky + pointer-events-none/auto pattern:

```html

language label
Copy Download
code body
\`\`\`

In a nested scroll-container layout, hit-testing for the `pointer-events-auto` inner div doesn't work correctly — clicks register on the code-block wrapper instead of reaching the buttons.

Using browser DevTools inspect mode on the button area selects the entire code block wrapper (`div[data-streamdown="code-block"]`), not the button, confirming something intercepts the click before it reaches the button element.

Reproduction steps

  1. Embed streamdown in a layout with 2+ nested scroll containers:
    • Parent: `overflow: auto` (e.g., sidebar)
    • Child: `overflow: auto` (e.g., chat scroll area, such as `use-stick-to-bottom`)
  2. Render a code block with streamdown
  3. Try to click the copy or download buttons — they are visible but do not respond to clicks
  4. In DevTools, use "Select element" and click where the button is — it selects the wrapper div, not the button

Workaround

Switching the button wrapper from `position: sticky` to `position: absolute` makes buttons reliably clickable:

```css
[data-streamdown="code-block"] {
position: relative;
}
[data-streamdown="code-block"] > div:has(> [data-streamdown="code-block-actions"]) {
position: absolute !important;
top: 0.5rem;
right: 0.5rem;
z-index: 1;
margin-top: 0 !important;
pointer-events: auto;
}
```

Trade-off: buttons no longer "stick" when scrolling past long code blocks — they stay at the top of the code block.

Suggested fix

Consider one of:

  • Use `position: absolute` as the default positioning strategy for code block action buttons
  • Provide a CSS custom property or prop to let consumers override the positioning strategy (e.g., `--streamdown-code-actions-position: sticky | absolute`)
  • Investigate and fix the hit-testing issue with `sticky` + `pointer-events-none` in nested scroll contexts

Environment

  • streamdown: 2.5.0
  • Browsers tested: Chrome, Firefox (both affected)
  • Layout: nested scroll containers (sidebar + chat scroll via `use-stick-to-bottom`)

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