# Hint

> A labeled trigger that opens a help bubble, its hit area stretched by a pseudo-element

A React component for projects built on shadcn/ui. The [interactive docs](https://alexandrejoly.me/components/hint) show each example live.

```tsx
import { QuestionIcon } from "@phosphor-icons/react";
import { Hint, HintContent, HintTrigger } from "@/components/ui/hint";

export const HintDemo = () => (
  <Hint>
    <HintTrigger>
      Label
      <QuestionIcon />
    </HintTrigger>
    <HintContent>A short explanation of the label.</HintContent>
  </Hint>
);
```

## Installation

With the shadcn CLI:

```bash
pnpm dlx shadcn@latest add @alexandrejoly/hint
```

Without the `@alexandrejoly` namespace, pass the registry URL:

```bash
pnpm dlx shadcn@latest add https://alexandrejoly.me/r/hint.json
```

Or by hand:

1. Add the shadcn components it builds on:

   ```bash
   pnpm dlx shadcn@latest add popover
   ```
2. Copy the source into `components/ui/hint.tsx`, then update the import paths to match your aliases:

   ```tsx
   import {
     Popover,
     PopoverContent,
     PopoverTrigger,
   } from "@/components/ui/popover";
   import { cn } from "@/lib/utils";

   function Hint({ ...props }: React.ComponentProps<typeof Popover>) {
     return <Popover data-slot="hint" {...props} />;
   }

   function HintTrigger({
     children,
     className,
     ...props
   }: React.ComponentProps<typeof PopoverTrigger>) {
     return (
       <PopoverTrigger
         className={cn(
           "relative cursor-help after:absolute after:-inset-2",
           className
         )}
         data-slot="hint-trigger"
         type="button"
         {...props}
       >
         <span className="inline-flex items-center gap-1.5 [&_svg]:size-4 [&_svg]:shrink-0">
           {children}
         </span>
       </PopoverTrigger>
     );
   }

   function HintContent({
     ...props
   }: React.ComponentProps<typeof PopoverContent>) {
     return <PopoverContent data-slot="hint-content" {...props} />;
   }

   export { Hint, HintContent, HintTrigger };
   ```

## Usage

```tsx
import { Hint, HintContent, HintTrigger } from "@/components/ui/hint";
```

```tsx
<Hint>
  <HintTrigger>
    Label
    <CircleHelpIcon />
  </HintTrigger>
  <HintContent>A short explanation of the label.</HintContent>
</Hint>
```

## Accessibility

The trigger is a `button` that holds its own label, so its name is the text people see and it needs no `aria-label`; the icon beside it is decorative. Its hit area reaches 8px past its edges, so even a short label gives a target of at least 24px.

It opens on press, by click, Enter or Space, rather than on hover, so touch and keyboard users reach it too. Focus moves into the bubble, and Escape closes it and returns focus to the trigger, as in any Base UI popover.

Meets WCAG 2.2: [2.5.8 Target Size (Minimum)](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html)

## API reference

### Hint

Holds the open state; it renders no element.

Takes every prop of [Base UI Popover.Root](https://base-ui.com/react/components/popover#root).

### HintTrigger

The button that toggles the hint. Put the label in it, text then icon, so it needs no `aria-label`; icons are sized to 16px. Its hit area extends 8px past its edges.

Takes every prop of [Base UI Popover.Trigger](https://base-ui.com/react/components/popover#trigger).

### HintContent

The bubble, portaled to the body.

Takes every prop of [shadcn PopoverContent](https://ui.shadcn.com/docs/components/base/popover).

[All components](https://alexandrejoly.me/components.md)
