shadcn-ui / ui

Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.
https://ui.shadcn.com
MIT License
70.45k stars 4.23k forks source link

Improvement: Tooltip and HoverCard Mobile Support #2402

Closed chukwumaokere closed 3 months ago

chukwumaokere commented 8 months ago

This is part of radix-ui but I noticed that there is no mobile support for HoverCard or Tooltip components. I have a web app where I use these for the desktop experience but a few users have said that it doesnt work on mobile, which is understandable given the library.

Just wanted to flag this as a possible future improvement. I'm thinking of implementing it myself, so I might open a PR to add tap support to Tooltips for mobile. Let me know if this is something we SHOULDNT pursue for any reason.

chukwumaokere commented 8 months ago

Might hold off on this: https://github.com/shadcn-ui/ui/issues/86

In case you "need a tooltip" use a popover instead, as they do in their documentation.

alamenai commented 8 months ago

Yes, tooltips are designed for hover interaction while this interaction is not available on the mobile devices. However, I have noticed that some popular products use hybrid interactions based on the size on the screen.

  1. Desktop: Hover.
  2. Mobile: Click.
0xbid commented 8 months ago

I'm confused on the situation in terms of triggering tooltips with mobile touch.

It seems like a really obvious thing that desktop hover and mobile click are 1:1, and I'm sure most people who start using shadcn and radix are going to assume this is how it works.

It seems like it's possible the authors disagree / ignore (e.g. #86), but I am not sure where that puts us. Does anyone have a solution for converting existing tooltips to work for mobile?

My app is basically unusable on mobile without tooltip support, so really desperate for a solution 🙏

0xbid commented 8 months ago

But yeah @chukwumaokere I would totally use this change, it would be a lifesaver. Even if it didn't get merged I'd use the fork.

luisdralves commented 7 months ago

Here's a component following the logic described by @alamenai

'use client';

import { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react';
import { Tooltip, TooltipTrigger, TooltipContent } from './tooltip';
import { Popover, PopoverTrigger, PopoverContent } from './popover';
import { TooltipContentProps, TooltipProps, TooltipTriggerProps } from '@radix-ui/react-tooltip';
import { PopoverContentProps, PopoverProps, PopoverTriggerProps } from '@radix-ui/react-popover';

const TouchContext = createContext<boolean | undefined>(undefined);
const useTouch = () => useContext(TouchContext);

export const TouchProvider = (props: PropsWithChildren) => {
  const [isTouch, setTouch] = useState<boolean>();

  useEffect(() => {
    setTouch(window.matchMedia('(pointer: coarse)').matches);
  }, []);

  return <TouchContext.Provider value={isTouch} {...props} />;
};

export const HybridTooltip = (props: TooltipProps & PopoverProps) => {
  const isTouch = useTouch();

  return isTouch ? <Popover {...props} /> : <Tooltip {...props} />;
};

export const HybridTooltipTrigger = (props: TooltipTriggerProps & PopoverTriggerProps) => {
  const isTouch = useTouch();

  return isTouch ? <PopoverTrigger {...props} /> : <TooltipTrigger {...props} />;
};

export const HybridTooltipContent = (props: TooltipContentProps & PopoverContentProps) => {
  const isTouch = useTouch();

  return isTouch ? <PopoverContent {...props} /> : <TooltipContent {...props} />;
};

It mounts popover on touch devices and a tooltip by default.

0xbid commented 7 months ago

thanks @luisdralves!

Zwyx commented 6 months ago

To summarise, they are three components:

Tooltip and Hover Card are only meant to be used on desktop. Popover is meant to be used on desktop and mobile.

This is a design choice by the Radix people. I think it wouldn't make sense to change it here at Shadcn-UI :thinking: so I reckon this issue can be closed.

maatouk-j1 commented 6 months ago

@luisdralves can you please show an example of how to use the hybrid tooltip component?

luisdralves commented 6 months ago

Sure, it's very similar to the regular tooltip. Just wrap your layout with the touch provider so that the isTouch state is calculated only once.


// Layout

import { TouchProvider } from '@/components/ui/hybrid-tooltip';
import './globals.css';

export default function RootLayout({ children }: React.PropsWithChildren) {
  return (
    <html lang='en'>
      <body>
        <TouchProvider>
          <main>{children}</main>
        </TouchProvider>
      </body>
    </html>
  );
}

// Page
import { HybridTooltip, HybridTooltipContent, HybridTooltipTrigger } from '@/components/ui/hybrid-tooltip';
import { TooltipProvider } from '@/components/ui/tooltip';

//...

export default function Page() {
  return (
    <div className='flex items-center gap-2'>
      <span>Tooltip example</span>

      <TooltipProvider delayDuration={0}>
        <HybridTooltip>
          <HybridTooltipTrigger asChild>
            <Button
              type='button'
              variant={'ghost'}
              className='h-6 w-6 rounded-full p-1 text-muted-foreground'
            >
              <Info className='h-4 w-4' />
            </Button>
          </HybridTooltipTrigger>

          <HybridTooltipContent>
            <p>Content</p>
          </HybridTooltipContent>
        </HybridTooltip>
      </TooltipProvider>
    </div>
  );
}
oxuk85 commented 5 months ago

I propose a different alternative, take a look at this gist below, it uses @floating-ui/react

https://gist.github.com/oxuk85/34e6969d6ec3971f4561d69f26571bb4

Here is the demo I found and used as the template for my own re-usable component. It works great on touch devices. https://codesandbox.io/p/sandbox/xenodochial-grass-js3bo9?file=%2Fsrc%2FTooltip.tsx

I found the suggestion using popover does not look great on mobile devices.

shadcn commented 3 months ago

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.