ui icon indicating copy to clipboard operation
ui copied to clipboard

[BUG] Blurry Content on Low-Resolution Screens with Overflow-Auto in AlertDialog/Dialog Components

Open hqw567 opened this issue 1 year ago • 8 comments

Description

The content within AlertDialog/Dialog components becomes blurry on low-resolution screens when the overflow-auto property is in effect. This issue appears to be related to the use of translate on DialogContent. The blurring effect is noticeable when the content exceeds the container size, suggesting that the issue may be linked to subpixel rendering and the rasterization process on low-resolution displays.

Environment

  • Tailwind CSS version: 3.4.1
  • Browser and version: Chrome 119.0.6045.200
  • Screen resolution: 1920*1080

Steps to Reproduce

  1. Create an AlertDialog/Dialog component with overflow-auto on DialogContent.
  2. Add enough content inside the DialogContent to cause overflow.
  3. Observe the content on a low-resolution screen.

Expected Behavior

Content should remain crisp and clear, without any blurring, regardless of screen resolution.

Actual Behavior

Content becomes blurry on low-resolution screens when overflow-auto is applied to DialogContent with a translate transform.

Additional Information

  • The issue seems to be exacerbated by the translate transform, potentially due to subpixel positioning on low-resolution screens.
  • It is observed that the blurring effect is not present on high-resolution screens, which suggests that pixel density plays a role in the problem.

Possible Solutions

  • Avoiding the use of translate on elements within overflow-auto containers.
  • Ensuring that element dimensions and translate values are set to even numbers to align with the pixel grid.
  • Using flexbox or grid layouts for centering instead of translate.

I am looking forward to any suggestions or workarounds for this issue. Thank you for your assistance!

hqw567 avatar Jan 10 '24 06:01 hqw567

Any update on this?

AkshayCloudAnalogy avatar Feb 26 '24 14:02 AkshayCloudAnalogy

Any update on this?

artemshchirov avatar Mar 25 '24 19:03 artemshchirov

Changing translate-y-[-50%] to translate-y-[-53%] in dialog.tsx component file removed the blurriness in most cases. it still happens when i change focus on a TinyMCE input I have within the dialog.

  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <DialogPrimitive.Content
      ref={ref}
      className={cn(
        "[&>div]:p-6 overflow-hidden !p-0 pt-[4.5rem] fixed left-[50%] top-[50%] z-20 grid w-full max-w-2xl translate-x-[-50%] translate-y-[-53%] border border-slate-200 bg-white shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-md md:w-full dark:border-slate-800 dark:bg-slate-950",
        className
      )}
    data-test="dialog-content"
      {...props}
    >
    {children}
      
    
    </DialogPrimitive.Content>
  </DialogPortal>
))```

rugys avatar Apr 27 '24 08:04 rugys

Changing translate-y-[-50%] to translate-y-[-53%] in dialog.tsx component file removed the blurriness in most cases. it still happens when i change focus on a TinyMCE input I have within the dialog.

  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <DialogPrimitive.Content
      ref={ref}
      className={cn(
        "[&>div]:p-6 overflow-hidden !p-0 pt-[4.5rem] fixed left-[50%] top-[50%] z-20 grid w-full max-w-2xl translate-x-[-50%] translate-y-[-53%] border border-slate-200 bg-white shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-md md:w-full dark:border-slate-800 dark:bg-slate-950",
        className
      )}
    data-test="dialog-content"
      {...props}
    >
    {children}
      
    
    </DialogPrimitive.Content>
  </DialogPortal>
))```

This works (also for the Command component https://github.com/shadcn-ui/ui/issues/1465).

But my question is: why does this work? How did you come up with this? Is there a specific reason, or was it just trial and error?

Goodosky avatar May 01 '24 16:05 Goodosky

I am a user of shadcn/svelte which is currently using the same CSS for dialogs. Here is the issue for the svelte version https://github.com/huntabyte/shadcn-svelte/issues/534 I analyzed it and wrote a lengthy comment in that issue.

IMHO it is best to make the DialogPrimitive.Content a child of DialogOverlay and then use "flex" to center the dialog. This way we would rid of the translate function which is the root of the problem.

slinso avatar May 03 '24 18:05 slinso

For those in need, here's the updated original component to use flexbox for centering, instead of translating, which removes the bluriness. The open animation has also been adjusted, so it's the same as before.

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <div className="fixed z-50 inset-0 flex items-center justify-center">
      <DialogPrimitive.Content
        ref={ref}
        className={cn(
          "relative grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[-2%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[-2%] sm:rounded-lg",
          className
        )}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
          <X className="h-4 w-4" />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </div>
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

MorelSerge avatar Jun 17 '24 14:06 MorelSerge

For those in need, here's the updated original component to use flexbox for centering, instead of translating, which removes the bluriness. The open animation has also been adjusted, so it's the same as before.

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <div className="fixed z-50 inset-0 flex items-center justify-center">
      <DialogPrimitive.Content
        ref={ref}
        className={cn(
          "relative grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[-2%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[-2%] sm:rounded-lg",
          className
        )}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
          <X className="h-4 w-4" />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </div>
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

Thanks! But a little trick is to use a grid and place-content-center

LuziferSenpai avatar Jul 07 '24 10:07 LuziferSenpai

For those in need, here's the updated original component to use flexbox for centering, instead of translating, which removes the bluriness. The open animation has also been adjusted, so it's the same as before.

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <div className="fixed z-50 inset-0 flex items-center justify-center">
      <DialogPrimitive.Content
        ref={ref}
        className={cn(
          "relative grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[-2%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[-2%] sm:rounded-lg",
          className
        )}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
          <X className="h-4 w-4" />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </div>
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

While this does resolve the blurriness, I've found that the closing animation no longer works after wrapping DialogPrimitive.Content in a div 😕

chesterhow avatar Jul 22 '24 02:07 chesterhow