Popover 
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
  <PopoverRoot>
    <PopoverTrigger
      class="rounded-full w-[35px] h-[35px] inline-flex items-center justify-center text-grass11 bg-white shadow-[0_2px_10px] shadow-blackA7 hover:bg-green3 focus:shadow-[0_0_0_2px] focus:shadow-black cursor-default outline-none"
      aria-label="Update dimensions"
    >
      <Icon icon="radix-icons:mixer-horizontal" />
    </PopoverTrigger>
    <PopoverPortal>
      <PopoverContent
        side="bottom"
        :side-offset="5"
        class="rounded p-5 w-[260px] bg-white shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.green7)] will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
      >
        <div class="flex flex-col gap-2.5">
          <p class="text-mauve12 text-[15px] leading-[19px] font-semibold mb-2.5">
            Dimensions
          </p>
          <fieldset class="flex gap-5 items-center">
            <label class="text-[13px] text-grass11 w-[75px]" for="width"> Width </label>
            <input
              id="width"
              class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
              defaultValue="100%"
            >
          </fieldset>
          <fieldset class="flex gap-5 items-center">
            <label class="text-[13px] text-grass11 w-[75px]" for="maxWidth"> Max. width </label>
            <input
              id="maxWidth"
              class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
              defaultValue="300px"
            >
          </fieldset>
          <fieldset class="flex gap-5 items-center">
            <label class="text-[13px] text-grass11 w-[75px]" for="height"> Height </label>
            <input
              id="height"
              class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
              defaultValue="25px"
            >
          </fieldset>
          <fieldset class="flex gap-5 items-center">
            <label class="text-[13px] text-grass11 w-[75px]" for="maxHeight"> Max. height </label>
            <input
              id="maxHeight"
              class="w-full inline-flex items-center justify-center flex-1 rounded px-2.5 text-[13px] leading-none text-grass11 shadow-[0_0_0_1px] shadow-green7 h-[25px] focus:shadow-[0_0_0_2px] focus:shadow-green8 outline-none"
              defaultValue="none"
            >
          </fieldset>
        </div>
        <PopoverClose
          class="rounded-full h-[25px] w-[25px] inline-flex items-center justify-center text-grass11 absolute top-[5px] right-[5px] hover:bg-green4 focus:shadow-[0_0_0_2px] focus:shadow-green7 outline-none cursor-default"
          aria-label="Close"
        >
          <Icon icon="radix-icons:cross-2" />
        </PopoverClose>
        <PopoverArrow class="fill-white" />
      </PopoverContent>
    </PopoverPortal>
  </PopoverRoot>
</template>Features 
- Can be controlled or uncontrolled.
- Customize side, alignment, offsets, collision handling.
- Optionally render a pointing arrow.
- Focus is fully managed and customizable.
- Supports modal and non-modal modes.
- Dismissing and layering behavior is highly customizable.
Installation 
Install the component from your command line.
npm install radix-vueAnatomy 
Import all parts and piece them together.
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
  <PopoverRoot>
    <PopoverTrigger />
    <PopoverAnchor />
    <PopoverPortal>
      <PopoverContent>
        <PopoverClose />
        <PopoverArrow />
      </PopoverContent>
    </PopoverPortal>
  </PopoverRoot>
</template>API Reference 
Root 
Contains all the parts of a popover.
| Prop | Type | Default | 
|---|---|---|
| defaultOpen | boolean | |
| open | boolean | |
| modal | boolean | false | 
| Emit | Type | 
|---|---|
| @update:open | (open: boolean) => void | 
Trigger 
The button that toggles the popover. By default, the PopoverContent will position itself against the trigger.
| Prop | Type | Default | 
|---|---|---|
| as | string | Component | button | 
| asChild | boolean | false | 
| Data Attribute | Value | 
|---|---|
| [data-state] | "open" | "closed" | 
Anchor 
An optional element to position the PopoverContent against. If this part is not used, the content will position alongside the PopoverTrigger.
| Prop | Type | Default | 
|---|---|---|
| as | string | Component | div | 
| asChild | boolean | false | 
Portal 
When used, portals the content part into the body.
| Prop | Type | Default | 
|---|---|---|
| to | string | HTMLElement | body | 
Content 
The component that pops out when the popover is open.
| Prop | Type | Default | 
|---|---|---|
| forceMount | boolean | |
| side | enum | "bottom" | 
| sideOffset | number | 0 | 
| align | enum | "center" | 
| alignOffset | number | 0 | 
| avoidCollisions | boolean | true | 
| collisionBoundary | Boundary | [] | 
| collisionPadding | number | Padding | 0 | 
| arrowPadding | number | 0 | 
| sticky | enum | "partial" | 
| hideWhenDetached | boolean | false | 
| as | string | Component | div | 
| asChild | boolean | false | 
| Emit | Type | 
|---|---|
| @openAutoFocus | (event: Event) => void | 
| @closeAutoFocus | (event: Event) => void | 
| @escapeKeyDown | (event: KeyboardEvent) => void | 
| @pointerDownOutside | (event: PointerDownOutsideEvent) => void | 
| @interactOutside | (event: FocusEvent | MouseEvent | TouchEvent) => void | 
| Data Attribute | Value | 
|---|---|
| [data-state] | "open" | "closed" | 
| [data-side] | "left" | "right" | "bottom" | "top" | 
| [data-align] | "start" | "end" | "center" | 
| CSS Variable | Description | 
|---|---|
| --radix-popover-content-transform-origin |  The  transform-origincomputed from the content and arrow positions/offsets | 
| --radix-popover-content-available-width | The remaining width between the trigger and the boundary edge | 
| --radix-popover-content-available-height | The remaining height between the trigger and the boundary edge | 
| --radix-popover-trigger-width | The width of the trigger | 
| --radix-popover-trigger-height | The height of the trigger | 
Arrow 
An optional arrow element to render alongside the popover. This can be used to help visually link the anchor with the PopoverContent. Must be rendered inside PopoverContent.
| Prop | Type | Default | 
|---|---|---|
| as | string | Component | svg | 
| asChild | boolean | false | 
| width | number | 10 | 
| height | number | 5 | 
Close 
The button that closes an open popover.
| Prop | Type | Default | 
|---|---|---|
| as | string | Component | button | 
| asChild | boolean | false | 
Examples 
Constrain the content size 
You may want to constrain the width of the content so that it matches the trigger width. You may also want to constrain its height to not exceed the viewport.
We expose several CSS custom properties such as --radix-popover-trigger-width and --radix-popover-content-available-height to support this. Use them to constrain the content dimensions.
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
  <PopoverRoot>
    <PopoverTrigger>…</PopoverTrigger>
    <PopoverPortal>
      <PopoverContent class="PopoverContent" :side-offset="5">
        …
      </PopoverContent>
    </PopoverPortal>
  </PopoverRoot>
</template>/* styles.css */
.PopoverContent {
  width: var(--radix-popover-trigger-width);
  max-height: var(--radix-popover-content-available-height);
}Origin-aware animations 
We expose a CSS custom property --radix-popover-content-transform-origin. Use it to animate the content from its computed origin based on side, sideOffset, align, alignOffset and any collisions.
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
  <PopoverRoot>
    <PopoverTrigger>…</PopoverTrigger>
    <PopoverPortal>
      <PopoverContent class="PopoverContent">
        …
      </PopoverContent>
    </PopoverPortal>
  </PopoverRoot>
</template>/* styles.css */
.PopoverContent {
  transform-origin: var(--radix-popover-content-transform-origin);
  animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
  from {
    opacity: 0;
    transform: scale(0);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}Collision-aware animations 
We expose data-side and data-align attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
  <PopoverRoot>
    <PopoverTrigger>…</PopoverTrigger>
    <PopoverPortal>
      <PopoverContent class="PopoverContent">
        …
      </PopoverContent>
    </PopoverPortal>
  </PopoverRoot>
</template>/* styles.css */
.PopoverContent {
  animation-duration: 0.6s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.PopoverContent[data-side="top"] {
  animation-name: slideUp;
}
.PopoverContent[data-side="bottom"] {
  animation-name: slideDown;
}
@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}With custom anchor 
You can anchor the content to another element if you do not want to use the trigger as the anchor.
// index.vue
<script setup>
import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'radix-vue'
</script>
<template>
  <PopoverRoot>
    <PopoverAnchor as-child>
      <div class="Row">
        Row as anchor <PopoverTrigger>Trigger</PopoverTrigger>
      </div>
    </PopoverAnchor>
    <PopoverPortal>
      <PopoverContent>…</PopoverContent>
    </PopoverPortal>
  </PopoverRoot>
</template>/* styles.css */
.Row {
  background-color: gainsboro;
  padding: 20px;
}Accessibility 
Adheres to the Dialog WAI-ARIA design pattern.
Keyboard Interactions 
| Key | Description | 
|---|---|
| Space | Opens/closes the popover. | 
| Enter | Opens/closes the popover. | 
| Tab | Moves focus to the next focusable element | 
| Shift + Tab | Moves focus to the previous focusable element | 
| Esc |  Closes the popover and moves focus to  PopoverTrigger. | 
Custom APIs 
Create your own API by abstracting the primitive parts into your own component.
Abstract the arrow and set default configuration 
This example abstracts the PopoverArrow part and sets a default sideOffset configuration.
Usage 
<script setup lang="ts">
import { Popover, PopoverContent, PopoverTrigger } from './your-popover'
</script>
<template>
  <Popover>
    <PopoverTrigger>Popover trigger</PopoverTrigger>
    <PopoverContent>Popover content</PopoverContent>
  </Popover>
</template>Implementation 
// your-popover.ts
export { default as PopoverContent } from 'PopoverContent.vue'
export { PopoverRoot as Popover, PopoverTrigger } from 'radix-vue'<!-- PopoverContent.vue -->
<script setup lang="ts">
import { PopoverContent, type PopoverContentEmits, type PopoverContentProps, PopoverPortal, useForwardPropsEmits, } from 'radix-vue'
const props = defineProps<PopoverContentProps>()
const emits = defineEmits<PopoverContentEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
  <PopoverPortal>
    <PopoverContent v-bind="{ ...forwarded, ...$attrs }">
      <slot />
    </PopoverContent>
  </PopoverPortal>
</template>