Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sonner z-index always behind radix-ui dialog #357

Closed
adamleithp-mystic opened this issue Feb 28, 2024 · 7 comments
Closed

Sonner z-index always behind radix-ui dialog #357

adamleithp-mystic opened this issue Feb 28, 2024 · 7 comments

Comments

@adamleithp-mystic
Copy link

adamleithp-mystic commented Feb 28, 2024

Describe the feature / bug 📝:

When i have a radix-ui dialog open, sonner is above visually but is behind the dialog despite setting the appropriate z-index on all concerned elements. Making swiping/close button inaccessible.

Steps to reproduce the bug 🔁:

<Dialog.Root open={open} onOpenChange={setOpen}>
      <Dialog.Trigger asChild>
        <Button {...props}>{children}</Button>
      </Dialog.Trigger>

      <Dialog.Portal>
        <Dialog.Overlay className="dialog-overlay" />
        <Dialog.Content className="dialog-content">
          ...
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
.dialog-overlay {z-index:50 !important}
.dialog-content {z-index:60 !important}

And then

<Toaster
    closeButton
    duration={50000000}
    position="top-center"
    toastOptions={{
        unstyled: true,
        style:{
            zIndex: 1000,
        },
    }}
/>

Nothing i do, even adding classes makes the Toaster go to the front.

I've tried to do this as-well, no luck.

import * as Portal from "@radix-ui/react-portal";

<Portal.Root>
    <Toaster
        closeButton
        duration={50000000}
        position="top-center"
        toastOptions={{
            unstyled: true,
            style:{
                zIndex: 1000,
            },
        }}
    />
</Portal.Root>
@LF112
Copy link

LF112 commented Mar 1, 2024

Try positioning it directly beneath React render, on the same level as the App component.

ReactDOM.createRoot(document.getElementById('app')!).render(
  <>
    <App />
    <Portal.Root>
      <Toaster richColors />
    </Portal.Root>
  </>
);

this worked after I tried

@emilkowalski
Copy link
Owner

@LF112's approach is correct.

@adamleithp-mystic
Copy link
Author

adamleithp-mystic commented Mar 1, 2024

What about Next.js (13+) which doesn't have an <App /> but rather a root layout.tsx?

This doesn't work either.

<html lang="en" suppressHydrationWarning>
    <body className={inter.className}>
        {/* <Portal.Root> */}
        <Toaster closeButton duration={5000000000} position="top-center" />
        {/* </Portal.Root> */}

        {children}
    </body>
</html>

@adamleithp-mystic
Copy link
Author

I found the issue, radix ui's dialog put's pointer-events: none; on the <body> causing this bug.

radix-ui/primitives#2122

@steven-tey
Copy link

steven-tey commented Apr 11, 2024

Edit: Found a fix 👇

<Toaster className="pointer-events-auto" />

@emilkowalski might be good to add this to Toaster itself?


Running into this issue as well 😓

Putting <Toaster /> outside of the body fixes this, but it's illegal (you'll get a Warning: In HTML, <section> cannot be a child of <html>.)

  <html lang="en">
      <Toaster />
      <body>
          {children}
      </body>
    </html>

@emilkowalski emilkowalski reopened this Apr 11, 2024
@adamleithp-mystic
Copy link
Author

Let's close this?

Adding the style to the <Toaster/> (as @steven-tey said) or adding pointer-events: auto !important to body will work as-well.

@freshgiammi
Copy link

I'm not sure this should be closed: yes the snackbars are now clickable, but they cause the Dialog to be closed as they are recognized as outside clicks. To allow click-through we need to consume the internal library @radix-ui/react-dismissable-layer which allows for item for be clicked outside of modality boundaries.

For posterity (and possibly fixing this), I've managed to correctly compose the scope to have both components behave correctly.
Now, my implementation solely uses toast.custom as our toast implementation is completely different from what Sonner is offering, so I'm able to do something like:

return toast.custom(
  t => (
    <DismissableLayer.Branch asChild>
      <Snackbar    
		...
      />
    </DismissableLayer.Branch>
  ), opts
)

considering the current implementation, I'm not quite sure on how this could be achievable using the other methods.

Additionally, we need'd to wrap the Toaster component as well:

{/* Radix Portal */}
<Portal>
  {/* Allow composability within Radix portals. */}
  <DismissableLayer.Root asChild>
    <Toaster
	...
    />
  </DismissableLayer.Root>
</Portal>

But this isn't currently possible, as Toaster isn't capable of ref forwarding.

I have opened #491 to add the ref forwarding ability, but I'm at a loss on how to allow wrapping the Toast element when a custom isn't used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants