v2.7.1

Popover

A non-modal popup window for showing messages or gathering user input.

Usage

Use a Button or any other component in the default slot of the Popover.

Then, use the #content slot to add the content displayed when the Popover is open.

<template>
  <B24Popover>
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>

Mode

Use the mode prop to change the mode of the Popover. Defaults to click.

<template>
  <B24Popover mode="hover">
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>
When using the hover mode, the Reka UI HoverCard component is used instead of the Popover.

Delay

When using the hover mode, you can use the open-delay and close-delay props to control the delay before the Popover is opened or closed.

<template>
  <B24Popover mode="hover" :open-delay="500" :close-delay="300">
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>

Content

Use the content prop to control how the Popover content is rendered, like its align or side for example.

<template>
  <B24Popover
    :content="{
      align: 'center',
      side: 'bottom',
      sideOffset: 8
    }"
  >
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>

Arrow

Use the arrow prop to display an arrow on the Popover.

<template>
  <B24Popover arrow>
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>

Use the modal prop to control whether the Popover blocks interaction with outside content. Defaults to false.

<template>
  <B24Popover modal>
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>

Dismissible

Use the dismissible prop to control whether the Popover is dismissible when clicking outside of it or pressing escape. Defaults to true.

A close:prevent event will be emitted when the user tries to close it.
<script setup lang="ts">
import Cross50Icon from '@bitrix24/b24icons-vue/actions/Cross50Icon'
</script>

<template>
  <B24Popover :dismissible="false" :b24ui="{ content: 'p-4' }">
    <B24Button label="Open" />

    <template #content="{ close }">
      <div class="flex items-center gap-4 mb-4">
        <h2 class="text-legend font-(--ui-font-weight-semi-bold)">
          Popover non-dismissible
        </h2>

        <B24Button color="air-tertiary" :icon="Cross50Icon" @click="close" />
      </div>

      <Placeholder class="size-full min-h-48" />
    </template>
  </B24Popover>
</template>

Examples

Control open state

You can control the open state by using the default-open prop or the v-model:open directive.

<script setup lang="ts">
const open = ref(false)

defineShortcuts({
  o: () => open.value = !open.value
})
</script>

<template>
  <B24Popover v-model:open="open">
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const open = ref(false)

defineShortcuts({
  o: () => open.value = !open.value
})
</script>

<template>
  <B24Popover v-model:open="open">
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="size-48 m-4 inline-flex" />
    </template>
  </B24Popover>
</template>
In this example, leveraging defineShortcuts, you can toggle the Popover by pressing O.

With command palette

You can use a CommandPalette component inside the Popover's content.

<script setup lang="ts">
import type { CommandPaletteItem } from '@bitrix24/b24ui-nuxt'
import TagIcon from '@bitrix24/b24icons-vue/outline/TagIcon'

const items = ref([
  {
    label: 'bug',
    value: 'bug',
    chip: {
      color: 'air-primary-alert'
    }
  },
  {
    label: 'feature',
    value: 'feature',
    chip: {
      color: 'air-primary-success'
    }
  },
  {
    label: 'enhancement',
    value: 'enhancement',
    chip: {
      color: 'air-primary'
    }
  }
] satisfies CommandPaletteItem[])

const label = ref([])
</script>

<template>
  <B24Popover :content="{ side: 'right', align: 'start' }">
    <B24Button
      :icon="TagIcon"
      label="Select labels"
    />

    <template #content>
      <B24CommandPalette
        v-model="label"
        :multiple="true"
        placeholder="Search labels..."
        :groups="[{ id: 'labels', items }]"
        :b24ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }"
      />
    </template>
  </B24Popover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { CommandPaletteItem } from '@bitrix24/b24ui-nuxt'
import TagIcon from '@bitrix24/b24icons-vue/outline/TagIcon'

const items = ref([
  {
    label: 'bug',
    value: 'bug',
    chip: {
      color: 'air-primary-alert'
    }
  },
  {
    label: 'feature',
    value: 'feature',
    chip: {
      color: 'air-primary-success'
    }
  },
  {
    label: 'enhancement',
    value: 'enhancement',
    chip: {
      color: 'air-primary'
    }
  }
] satisfies CommandPaletteItem[])

const label = ref([])
</script>

<template>
  <B24Popover :content="{ side: 'right', align: 'start' }">
    <B24Button
      :icon="TagIcon"
      label="Select labels"
    />

    <template #content>
      <B24CommandPalette
        v-model="label"
        :multiple="true"
        placeholder="Search labels..."
        :groups="[{ id: 'labels', items }]"
        :b24ui="{ input: '[&>input]:h-8 [&>input]:text-sm' }"
      />
    </template>
  </B24Popover>
</template>

With following cursor

You can make the Popover follow the cursor when hovering over an element using the reference prop:

<script setup lang="ts">
const open = ref(false)
const anchor = ref({ x: 0, y: 0 })

const reference = computed(() => ({
  getBoundingClientRect: () =>
    ({
      width: 0,
      height: 0,
      left: anchor.value.x,
      right: anchor.value.x,
      top: anchor.value.y,
      bottom: anchor.value.y,
      ...anchor.value
    } as DOMRect)
}))
</script>

<template>
  <B24Popover
    :open="open"
    :reference="reference"
    :content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
  >
    <div
      class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-accent-main-primary-alt) text-sm aspect-video w-72"
      @pointerenter="open = true"
      @pointerleave="open = false"
      @pointermove="(ev: PointerEvent) => {
        anchor.x = ev.clientX
        anchor.y = ev.clientY
      }"
    >
      Hover me
    </div>

    <template #content>
      <div class="p-4 text-description">
        {{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
      </div>
    </template>
  </B24Popover>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'

const open = ref(false)
const anchor = ref({ x: 0, y: 0 })

const reference = computed(() => ({
  getBoundingClientRect: () =>
    ({
      width: 0,
      height: 0,
      left: anchor.value.x,
      right: anchor.value.x,
      top: anchor.value.y,
      bottom: anchor.value.y,
      ...anchor.value
    } as DOMRect)
}))
</script>

<template>
  <B24Popover
    :open="open"
    :reference="reference"
    :content="{ side: 'top', sideOffset: 16, updatePositionStrategy: 'always' }"
  >
    <div
      class="flex items-center justify-center rounded-md border border-dashed border-(--ui-color-accent-main-primary-alt) text-sm aspect-video w-72"
      @pointerenter="open = true"
      @pointerleave="open = false"
      @pointermove="(ev: PointerEvent) => {
        anchor.x = ev.clientX
        anchor.y = ev.clientY
      }"
    >
      Hover me
    </div>

    <template #content>
      <div class="p-4 text-description">
        {{ anchor.x.toFixed(0) }} - {{ anchor.y.toFixed(0) }}
      </div>
    </template>
  </B24Popover>
</template>

With anchor slot

You can use the #anchor slot to position the Popover against a custom element.

This slot only works when mode is click.
<script lang="ts" setup>
const open = ref(false)
</script>

<template>
  <B24Popover
    v-model:open="open"
    :dismissible="false"
    :b24ui="{ content: 'w-(--reka-popper-anchor-width) p-4' }"
  >
    <template #anchor>
      <B24Input placeholder="Focus to open" @focus="open = true" @blur="open = false" />
    </template>

    <template #content>
      <Placeholder class="w-full aspect-square" />
    </template>
  </B24Popover>
</template>

Entity info card

Real-world example: an account or deal summary shown in a hover popover, assembled from Card, Avatar, Separator, Button, Link and DescriptionList — no custom styling beyond the avatar accent.

<script setup lang="ts">
import UserGroupIcon from '@bitrix24/b24icons-vue/common-b24/UserGroupIcon'
import type { DescriptionListItem } from '@bitrix24/b24ui-nuxt'

const items = [
  { label: 'Account manager', description: 'Sample owner', slot: 'owner' as const },
  { label: 'Created', description: 'Oct 6, 2024 08:37' },
  { label: 'Segment', description: 'Enterprise' }
] satisfies DescriptionListItem[]
</script>

<template>
  <B24Popover mode="hover" :content="{ side: 'bottom', sideOffset: 8 }" :b24ui="{ content: 'p-0' }">
    <B24Link is-action>
      ACME Corp.
    </B24Link>

    <template #content>
      <div class="w-65 p-6 flex flex-col gap-4.5">
        <div class="flex items-center gap-3">
          <B24Avatar
            size="lg"
            color="air-secondary-alert"
            :icon="UserGroupIcon"
          />
          <div class="min-w-0">
            <div class="text-label font-(--ui-font-weight-semi-bold) truncate">
              ACME Corp.
            </div>
            <div class="text-sm text-description truncate">
              12 contacts
            </div>
          </div>
        </div>

        <B24Button block size="sm" color="air-secondary-accent-2" label="Open account" />

        <B24Separator />

        <B24DescriptionList
          size="sm"
          :items="items"
          :b24ui="{
            container: 'mt-0 sm:grid-cols-1',
            labelWrapper: 'border-t-0 sm:border-t-0 py-0 sm:py-0',
            descriptionWrapper: 'sm:border-t-0 py-0 pb-2.5 sm:py-0 sm:pb-2.5 last:pb-0 sm:last:pb-0'
          }"
        >
          <template #description="{ item }">
            <B24Link v-if="item.slot === 'owner'">{{ item.description }}</B24Link>
            <template v-else>{{ item.description }}</template>
          </template>
        </B24DescriptionList>
      </div>
    </template>
  </B24Popover>
</template>

Build an entity-info popover for any record or object.

Sales dynamics widget

A real-world example of pairing a Popover with a stats card. The widget itself is documented on the Card page; here it sits inside B24Popover so a trigger button reveals it on demand.

<script setup lang="ts">
import RepeatIcon from '@bitrix24/b24icons-vue/outline/RepeatIcon'
import SettingsIcon from '@bitrix24/b24icons-vue/outline/SettingsIcon'
import FeedbackIcon from '@bitrix24/b24icons-vue/outline/FeedbackIcon'
import Info1Icon from '@bitrix24/b24icons-vue/main/Info1Icon'

const rows = [
  { label: 'Active deals', count: 10, amount: '$46,500' },
  { label: 'Won deals', count: 1, amount: '$10,000' }
]

const highlight = {
  label: 'Conversion',
  count: '10%',
  amount: '17.7%',
  info: 'Today vs. last 30 days'
}
</script>

<template>
  <B24Popover :b24ui="{ content: 'p-0 bg-transparent border-0 shadow-none' }">
    <B24Button label="Open sales widget" />

    <template #content>
      <B24Card
        variant="filled-copilot"
        class="max-w-md"
        :b24ui="{
          root: 'edge-dark rounded-2xl bg-[radial-gradient(110.42%_110.42%_at_-10.42%_31.25%,var(--ui-color-copilot-bg-content-3)_0%,var(--ui-color-copilot-bg-content-2)_58.65%,var(--ui-color-copilot-bg-content-1)_100%)]'
        }"
      >
        <template #header>
          <div class="flex items-start justify-between gap-4">
            <div class="min-w-0">
              <div class="text-(length:--ui-font-size-2xl)/[normal] font-(--ui-font-weight-semi-bold)">
                Repeat sales dynamics
              </div>
              <div class="text-(length:--ui-font-size-md) opacity-90">
                Total deals created: 10
              </div>
              <div class="text-(length:--ui-font-size-md) opacity-90">
                Today: 10 deals
              </div>
            </div>

            <B24Button
              rounded
              color="air-secondary-accent"
              label="Last 30 days"
              :trailing-icon="RepeatIcon"
            />
          </div>
        </template>

        <div class="flex flex-col gap-2">
          <div class="grid grid-cols-[1fr_auto_auto] gap-x-6 px-3 py-1 text-(length:--ui-font-size-sm) opacity-80">
            <span />
            <span class="text-right min-w-20">Count</span>
            <span class="text-right min-w-24">Amount</span>
          </div>

          <div
            v-for="row in rows"
            :key="row.label"
            class="grid grid-cols-[1fr_auto_auto] gap-x-6 items-center px-3 py-3 rounded-xl bg-white/5"
          >
            <span class="font-(--ui-font-weight-medium)">{{ row.label }}</span>
            <span class="text-right min-w-20">{{ row.count }}</span>
            <span class="text-right min-w-24">{{ row.amount }}</span>
          </div>

          <div class="style-filled-boost grid grid-cols-[1fr_auto_auto] gap-x-6 items-center px-3 py-3 rounded-xl text-(--ui-color-design-filled-boost-content)">
            <span class="font-(--ui-font-weight-medium) inline-flex items-center gap-1">
              {{ highlight.label }}
              <B24Tooltip :text="highlight.info">
                <Info1Icon class="size-4 opacity-80" />
              </B24Tooltip>
            </span>
            <span class="text-right min-w-20">{{ highlight.count }}</span>
            <span class="text-right min-w-24 font-(--ui-font-weight-semi-bold)">{{ highlight.amount }}</span>
          </div>
        </div>

        <template #footer>
          <div class="flex items-center justify-between gap-4">
            <B24Button color="air-secondary-accent" :icon="SettingsIcon" label="Configure" />
            <B24Button color="air-secondary-accent" :icon="FeedbackIcon" label="Feedback" />
          </div>
        </template>
      </B24Card>
    </template>
  </B24Popover>
</template>

Wrap the Sales dynamics widget in a Popover so a trigger button or link reveals it on demand.

API

Props

Prop Default Type
mode'click' M

The display mode of the popover.

content{ side: 'bottom', sideOffset: 8, collisionPadding: 8 } PopoverContentProps & Partial<EmitsToProps<PopoverContentImplEmits>>

The content of the popover.

arrowfalseboolean | PopoverArrowProps

Display an arrow alongside the popover.

portaltrue string | false | true | HTMLElement

Render the popover in a portal.

reference Element | VirtualElement

The reference (or anchor) element that is being referred to for positioning.

If not provided will use the current component as anchor.

dismissibletrueboolean

When false, the popover will not close when clicking outside or pressing escape.

defaultOpenboolean

The open state of the popover when it is initially rendered. Use when you do not need to control its open state.

openboolean

The controlled open state of the popover.

modalfalseboolean

The modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers.

openDelay0 number

The duration from when the mouse enters the trigger until the hover card opens.

closeDelay0 number

The duration from when the mouse leaves the trigger or content until the hover card closes.

b24ui { content?: ClassNameValue; arrow?: ClassNameValue; }

Slots

Slot Type
default{ open: boolean; }
contentSlotProps<M>
anchorSlotProps<M>
The close function is only available when mode is set to click because Reka UI exposes this for Popover but not for HoverCard.

Emits

Event Type
close:prevent[]
update:open[value: boolean]

Theme

https://github.com/bitrix24/b24ui/tree/main/src/theme/popover.ts
export default {
  slots: {
    content: 'base-mode bg-(--ui-color-bg-content-primary) shadow-(--popup-window-box-shadow) rounded-(--ui-border-radius-xl) will-change-[opacity] motion-safe:data-[state=open]:animate-[scale-in_100ms_ease-out] motion-safe:data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-(--reka-popover-content-transform-origin) focus:outline-none pointer-events-auto',
    arrow: 'fill-(--ui-color-bg-content-primary)'
  }
}