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
\`\`\`
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
- 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`)
- Render a code block with streamdown
- Try to click the copy or download buttons — they are visible but do not respond to clicks
- 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`)
Description
When streamdown is embedded in a layout with two or more nested scroll containers (e.g., an outer
overflow-autosidebar + an inneroverflow-autochat scroll), the code block copy/download buttons are visible but unclickable.Root cause
Streamdown renders code block buttons using a
sticky+pointer-events-none/autopattern:```html
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
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:
Environment