|
| 1 | +import * as PopoverStories from './popover.component.stories'; |
| 2 | + |
| 3 | +# Popover |
| 4 | + |
| 5 | +The Mosaic `Popover` — the styled Mosaic component composed from the `@clerk/headless` popover |
| 6 | +primitive and themed with StyleX. It is a reusable floating card anchored to a trigger: drop any |
| 7 | +inner content into `Popover.Content` and an optional `Popover.Footer`. It inherits the primitive's |
| 8 | +positioning, focus management, and ARIA wiring. |
| 9 | + |
| 10 | +## Example |
| 11 | + |
| 12 | +<Story |
| 13 | + name='Default' |
| 14 | + storyModule={PopoverStories} |
| 15 | +/> |
| 16 | + |
| 17 | +## Usage |
| 18 | + |
| 19 | +```tsx |
| 20 | +import { Button } from '@clerk/ui/mosaic/components/button'; |
| 21 | +import { Popover } from '@clerk/ui/mosaic/components/popover'; |
| 22 | + |
| 23 | +<Popover trigger={props => <Button {...props}>Open popover</Button>}> |
| 24 | + <Popover.Content>Flexible inner content.</Popover.Content> |
| 25 | + <Popover.Footer> |
| 26 | + <Button |
| 27 | + intent='destructive' |
| 28 | + fullWidth |
| 29 | + > |
| 30 | + Sign out of all accounts |
| 31 | + </Button> |
| 32 | + </Popover.Footer> |
| 33 | +</Popover>; |
| 34 | +``` |
| 35 | + |
| 36 | +The `trigger` render prop receives the interaction props (ARIA attributes, click handler) from the |
| 37 | +headless layer and should spread them onto whatever element opens the popover. |
| 38 | + |
| 39 | +### Controlled |
| 40 | + |
| 41 | +```tsx |
| 42 | +const [open, setOpen] = useState(false); |
| 43 | + |
| 44 | +<Popover |
| 45 | + open={open} |
| 46 | + onOpenChange={setOpen} |
| 47 | + trigger={props => <Button {...props}>Open</Button>} |
| 48 | +> |
| 49 | + <Popover.Content>Flexible inner content.</Popover.Content> |
| 50 | +</Popover>; |
| 51 | +``` |
| 52 | + |
| 53 | +### Placement |
| 54 | + |
| 55 | +`placement` sets the preferred side and alignment; `sideOffset` sets the gap from the trigger. The |
| 56 | +popup flips and shifts automatically to stay in view. |
| 57 | + |
| 58 | +```tsx |
| 59 | +<Popover |
| 60 | + placement='bottom-end' |
| 61 | + sideOffset={8} |
| 62 | + trigger={props => <Button {...props}>Open</Button>} |
| 63 | +> |
| 64 | + <Popover.Content>Aligned to the trigger's end edge.</Popover.Content> |
| 65 | +</Popover>; |
| 66 | +``` |
| 67 | + |
| 68 | +## Parts |
| 69 | + |
| 70 | +The convenience `Popover` composes the trigger and a portalled, positioned popup. For custom |
| 71 | +layouts, compose the compound parts directly. |
| 72 | + |
| 73 | +| Part | Slot | Description | |
| 74 | +| --------------------- | -------------------- | -------------------------------------------------------------- | |
| 75 | +| `Popover.Root` | — | State provider; owns open/close and placement. | |
| 76 | +| `Popover.Trigger` | — | Anchor element; accepts a `render` prop. | |
| 77 | +| `Popover.Portal` | — | Portals the popup out to the document body. | |
| 78 | +| `Popover.Positioner` | `popover-positioner` | Floating wrapper; owns positioning, stacking, `data-side`. | |
| 79 | +| `Popover.Popup` | `popover-popup` | The card surface; holds content + footer, runs the enter/exit. | |
| 80 | +| `Popover.Content` | `popover-content` | Flexible inner content region; scrolls on overflow. | |
| 81 | +| `Popover.Footer` | `popover-footer` | Footer region, separated from content by a top border. | |
| 82 | +| `Popover.Close` | — | Dismisses the popover; accepts a `render` prop. | |
| 83 | +| `Popover.Title` | — | Heading; wired to the popup's `aria-labelledby`. | |
| 84 | +| `Popover.Description` | — | Description; wired to the popup's `aria-describedby`. | |
| 85 | +| `Popover.Arrow` | — | Optional arrow pointing at the trigger. | |
| 86 | + |
| 87 | +## Styling |
| 88 | + |
| 89 | +Unlike the slot-recipe components, the Mosaic popover is themed with **StyleX**. Each styled part |
| 90 | +carries a stable `.cl-<slot>` class (the slots in the table above) alongside the StyleX atoms. |
| 91 | +Consumers never target the hashed atomic classes — override by targeting the `.cl-*` slot from a |
| 92 | +CSS layer that wins over `@clerk/ui/styles.css`: |
| 93 | + |
| 94 | +```css |
| 95 | +@import '@clerk/ui/styles.css' layer(components); |
| 96 | + |
| 97 | +@layer overrides { |
| 98 | + .cl-popover-popup { |
| 99 | + border-radius: 20px; |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +State attributes from the headless layer are available for CSS targeting: |
| 105 | + |
| 106 | +| Attribute | Applies To | Description | |
| 107 | +| --------------------- | -------------- | --------------------------------------------------- | |
| 108 | +| `data-open` | Trigger, Popup | Present when the popover is open | |
| 109 | +| `data-closed` | Trigger, Popup | Present when closed (during exit) | |
| 110 | +| `data-starting-style` | Popup | Present on the entering frame | |
| 111 | +| `data-ending-style` | Popup | Present during the exit animation | |
| 112 | +| `data-side` | Positioner | Resolved side (`top` / `bottom` / `left` / `right`) | |
| 113 | + |
| 114 | +The popup's default enter/exit transition (opacity + scale) is driven off `data-starting-style` / |
| 115 | +`data-ending-style` and is disabled under `prefers-reduced-motion: reduce`. |
0 commit comments