Skip to content

bug: .tooltip adds to ancestor scrollWidth, producing phantom scrollbars #4534

@DaSchTour

Description

@DaSchTour

What version of daisyUI are you using?

v5.5.19

Which browsers are you seeing the problem on?

Chrome, Safari, Firefox (Blink + WebKit + Gecko all behave the same here).

Describe your issue

A .tooltip placed near the right (or bottom) edge of a scroll container makes the container's scrollWidth larger than its clientWidth, so the user sees a horizontal (or vertical) scrollbar even though nothing in the visible layout is actually overflowing.

This has been reported a number of times before and closed as expected / "no pure-CSS fix possible":

There is a pure-CSS fix that I'd love to propose: contain: layout.

Why it happens

The tooltip text is painted via a ::before (or .tooltip-content child) that lives in the layout permanently — only opacity toggles its visibility. The host is position: relative, so the pseudo's absolutely-positioned box is registered against the host's containing block. Browsers include that box in the scrollWidth/scrollHeight of every scroll ancestor, even though it's not visible and lives outside the host's own border box. Result: a phantom scrollbar whenever a .tooltip sits near the edge of a scroll container.

Proposed fix

/* packages/daisyui/src/components/tooltip.css */
.tooltip {
  @layer daisyui.l1.l2.l3 {
    @apply relative inline-block;
    contain: layout;          /* <-- new */
    /* …existing rules… */
  }
}

contain: layout (MDN) keeps the layout/scroll calculation of the tooltip's descendants local to the host. It does not clip painting — the tooltip still renders outside its host on hover, exactly as it does today. The pseudo simply stops contributing to ancestor scrollWidth/scrollHeight.

This is specifically the concern raised in #326"Clipping the tooltip and hide a part of it? I don't think that's a good solution". contain: layout does not clip. Painting is unaffected; only the scroll-extent calculation changes.

Reproduction

Minimal HTML (v5 markup):

<div style="overflow:auto; width:240px; border:1px solid #ccc; padding:8px;">
  <button class="btn tooltip tooltip-top" data-tip="A reasonably long tooltip text">
    btn
  </button>
</div>

Without the fix: the wrapper shows a horizontal scrollbar at rest.
With the fix (.tooltip { contain: layout }): no scrollbar, hover still shows the full tooltip outside the host.

I verified this on the production app where I hit the bug — both <main> and <mat-sidenav-content> reported scrollWidth > clientWidth purely because of .tooltip pseudos near the right edge of a toolbar. Adding .tooltip { contain: layout; } brought scrollWidth === clientWidth while leaving hover behaviour intact.

Caveats considered

  • contain: layout establishes a new containing block for absolutely-positioned descendants. .tooltip is already position: relative, so it's already that containing block — no positioning change.
  • It doesn't affect transforms or the ::after arrow, both of which paint normally.
  • Browser support is broad: Chromium, Firefox, Safari ≥ 15.4.

Ask

Happy to send a PR with the one-line change + tests in the playground/docs if you're open to it — could you assign me?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions