v2.7.1

Modal

A popup window for showing messages or gathering user input.

Usage

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

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

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

    <template #content>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

You can also use the #header, #body and #footer slots to customize the Modal's content.

Title

Use the title prop to set the title of the Modal's header.

<template>
  <B24Modal title="Modal with title">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Description

Use the description prop to set the description of the Modal's header.

<template>
  <B24Modal
    title="Modal with description"
    description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Close

Use the close prop to customize or hide the close button (with false value) displayed in the Modal's header.

You can pass any property from the Button component to customize it.

<template>
  <B24Modal
    title="Modal with close button"
    :close="{
      color: 'air-secondary-accent-2',
      class: 'rounded-full'
    }"
  >
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>
The close button is not displayed if the #content slot is used as it's a part of the header.

Close Icon

Use the close-icon prop to customize the close button Icon.

<script setup lang="ts">
import RocketIcon from '@bitrix24/b24icons-vue/main/RocketIcon'
</script>

<template>
  <B24Modal title="Modal with close button" :close-icon="RocketIcon">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Transition

Use the transition prop to control whether the Modal is animated or not. Defaults to true.

<template>
  <B24Modal :transition="false" title="Modal without transition">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Overlay

Use the overlay prop to control whether the Modal has an overlay or not. Defaults to true.

<template>
  <B24Modal :overlay="false" title="Modal without overlay">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Overlay blur

If you want to disable background blur, you should use the overlayBlur prop.

The overlayBlur prop has 3 options:

  • auto: (default) when the user has not requested reduced motion
  • on: always use blur
  • off: do not use blur
<template>
  <B24Modal overlay-blur="auto" title="Overlay blur">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Use the modal prop to control whether the Modal blocks interaction with outside content. Defaults to true.

When modal is set to false, the overlay is automatically disabled and outside content becomes interactive.
<template>
  <B24Modal :modal="false" title="Modal interactive">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Dismissible

Use the dismissible prop to control whether the Modal 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.
You can combine modal: false with dismissible: false to make the Modal's background interactive without closing it.
<template>
  <B24Modal :dismissible="false" title="Modal non-dismissible">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>

Scrollable

Use the scrollable prop to make the Modal's content scrollable within the overlay.

As the overlay is needed for scrolling, modal: false is not compatible and overlay: false only removes the background.
<template>
  <B24Modal scrollable title="Modal scrollable">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-full" />
    </template>
  </B24Modal>
</template>
There's a known issue where clicking on the scrollbar may unintentionally close the dialog on some operating systems.

Fullscreen

Use the fullscreen prop to make the Modal fullscreen.

<template>
  <B24Modal fullscreen title="Modal fullscreen">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-full" />
    </template>
  </B24Modal>
</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>
  <B24Modal v-model:open="open">
    <B24Button label="Open" />

    <template #content>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const open = ref(false)

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

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

    <template #content>
      <Placeholder class="h-48" />
    </template>
  </B24Modal>
</template>
In this example, leveraging defineShortcuts, you can toggle the Modal by pressing O.
This allows you to move the trigger outside of the Modal or remove it entirely.

Programmatic usage

You can use the useOverlay composable to open a Modal programmatically.

Make sure to wrap your app with the App component which uses the OverlayProvider component.

First, create a modal component that will be opened programmatically:

ModalExample.vue
<script setup lang="ts">
defineProps<{
  count: number
}>()

const emit = defineEmits<{ close: [boolean] }>()
</script>

<template>
  <B24Modal
    :close="{ onClick: () => emit('close', false) }"
    :title="`This modal was opened programmatically ${count} times`"
  >
    <template #footer>
      <div class="flex gap-2">
        <B24Button label="Dismiss" @click="emit('close', false)" />
        <B24Button color="air-primary-success" label="Success" @click="emit('close', true)" />
      </div>
    </template>
  </B24Modal>
</template>
We are emitting a close event when the modal is closed or dismissed here. You can emit any data through the close event, however, the event must be emitted in order to capture the return value.

Then, use it in your app:

<script setup lang="ts">
import { LazyModalExample } from '#components'

const count = ref(0)

const toast = useToast()
const overlay = useOverlay()

const modal = overlay.create(LazyModalExample)

async function open() {
  const instance = modal.open({
    count: count.value
  })

  const shouldIncrement = await instance.result

  if (shouldIncrement) {
    count.value++

    toast.add({
      title: `Success: ${shouldIncrement}`,
      color: 'air-primary-success',
      id: 'modal-success'
    })

    // Update the count
    modal.patch({
      count: count.value
    })
    return
  }

  toast.add({
    title: `Dismissed: ${shouldIncrement}`,
    color: 'air-primary-alert',
    id: 'modal-dismiss'
  })
}
</script>

<template>
  <B24Button label="Open" @click="open" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { LazyModalExample } from '#components'

const count = ref(0)

const toast = useToast()
const overlay = useOverlay()

const modal = overlay.create(LazyModalExample)

async function open() {
  const instance = modal.open({
    count: count.value
  })

  const shouldIncrement = await instance.result

  if (shouldIncrement) {
    count.value++

    toast.add({
      title: `Success: ${shouldIncrement}`,
      color: 'air-primary-success',
      id: 'modal-success'
    })

    // Update the count
    modal.patch({
      count: count.value
    })
    return
  }

  toast.add({
    title: `Dismissed: ${shouldIncrement}`,
    color: 'air-primary-alert',
    id: 'modal-dismiss'
  })
}
</script>

<template>
  <B24Button label="Open" @click="open" />
</template>
You can close the modal within the modal component by emitting emit('close').

Nested modals

You can nest modals within each other.

<script setup lang="ts">
const first = ref(false)
const second = ref(false)
</script>

<template>
  <B24Modal v-model:open="first" title="First modal" :b24ui="{ footer: 'justify-end' }">
    <B24Button label="Open" />

    <template #footer>
      <B24Button label="Close" @click="first = false" />

      <B24Modal v-model:open="second" title="Second modal" :b24ui="{ footer: 'justify-end' }">
        <B24Button label="Open second" color="air-primary" />

        <template #footer>
          <B24Button label="Close" @click="second = false" />
        </template>
      </B24Modal>
    </template>
  </B24Modal>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const first = ref(false)
const second = ref(false)
</script>

<template>
  <B24Modal v-model:open="first" title="First modal" :b24ui="{ footer: 'justify-end' }">
    <B24Button label="Open" />

    <template #footer>
      <B24Button label="Close" @click="first = false" />

      <B24Modal v-model:open="second" title="Second modal" :b24ui="{ footer: 'justify-end' }">
        <B24Button label="Open second" color="air-primary" />

        <template #footer>
          <B24Button label="Close" @click="second = false" />
        </template>
      </B24Modal>
    </template>
  </B24Modal>
</template>

Use the #footer slot to add content after the Modal's body.

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

<template>
  <B24Modal v-model:open="open" title="Modal with footer" description="This is useful when you want a form in a Modal." :b24ui="{ footer: 'justify-end' }">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>

    <template #footer="{ close }">
      <B24Button label="Submit" color="air-primary-success" />
      <B24Button label="Cancel" @click="close" />
    </template>
  </B24Modal>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const open = ref(false)
</script>

<template>
  <B24Modal v-model:open="open" title="Modal with footer" description="This is useful when you want a form in a Modal." :b24ui="{ footer: 'justify-end' }">
    <B24Button label="Open" />

    <template #body>
      <Placeholder class="h-48" />
    </template>

    <template #footer="{ close }">
      <B24Button label="Submit" color="air-primary-success" />
      <B24Button label="Cancel" @click="close" />
    </template>
  </B24Modal>
</template>

Marketing / promo composition Soon

Compose a promo/upgrade modal from existing components — no new component needed. The body uses a responsive 2-column flex (heading + description on the left, a feature card on the right), the footer pairs a primary CTA with a tertiary "remind me later" action. The decorative background is a Tailwind gradient utility passed through b24ui.body.

<script setup lang="ts">
import type { NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import ArrowRightLIcon from '@bitrix24/b24icons-vue/outline/ArrowRightLIcon'
import CircleCheckIcon from '@bitrix24/b24icons-vue/main/CircleCheckIcon'

const open = ref(false)

const benefits: NavigationMenuItem[] = [
  { label: 'Unlimited monitored lines', icon: CircleCheckIcon },
  { label: 'AI defect detection on camera streams', icon: CircleCheckIcon }
]
</script>

<template>
  <B24Modal
    v-model:open="open"
    title=""
    description=""
    :close="false"
    scrollable
    :b24ui="{
      content: 'sm:max-w-[788px] bg-[radial-gradient(110.42%_110.42%_at_-10.42%_31.25%,var(--ui-color-design-filled-boost-bg-gradient-1)_0%,var(--ui-color-design-filled-boost-bg-gradient-2)_58.65%,var(--ui-color-design-filled-boost-bg-gradient-3)_100%)]',
      body: 'relative p-6 md:p-8'
    }"
  >
    <B24Button label="Open marketing modal" color="air-secondary-accent" />

    <template #body>
      <div class="flex flex-col md:flex-row gap-6 min-h-full">
        <div class="flex-1 flex flex-col gap-3 text-white">
          <h2 class="text-2xl font-semibold leading-snug">
            Keep your factory floor moving — your Pro trial ends in 6 days
          </h2>
          <p class="text-white/85">
            Pro unlocks real-time OEE across every line, 90 days of historical
            analytics, AI defect detection on camera streams and predictive
            maintenance alerts. Teams on Pro ship 12% more units per shift on
            average.
          </p>
          <B24Link to="#" class="inline-flex text-white underline">
            Compare plans
          </B24Link>

          <div class="flex flex-wrap items-center gap-2 mt-auto pt-4">
            <B24Button
              label="Upgrade to Pro"
              color="air-primary"
              size="lg"
              :trailing-icon="ArrowRightLIcon"
            />
            <B24ModalDialogClose>
              <B24Button label="Remind me later" color="air-tertiary-accent" />
            </B24ModalDialogClose>
          </div>
        </div>

        <div class="md:w-80 shrink-0">
          <B24Card
            variant="tinted-no-accent"
            :b24ui="{
              header: 'flex items-center justify-between gap-2',
              body: 'space-y-4'
            }"
          >
            <template #header>
              <span class="font-semibold whitespace-nowrap">Production Insights · Pro</span>
              <B24Badge label="Recommended" color="air-primary" size="xs" class="shrink-0" />
            </template>

            <div>
              <div class="text-3xl font-semibold leading-none">
                +12%
              </div>
              <div class="text-sm text-description mt-1">
                average throughput uplift after upgrade
              </div>
            </div>

            <div class="space-y-1">
              <B24Progress :model-value="24" :max="30" color="air-primary" size="sm" />
              <div class="text-xs text-description">
                Trial used — 24 of 30 days
              </div>
            </div>

            <B24NavigationMenu
              orientation="vertical"
              :items="benefits"
              :b24ui="{ link: 'px-2 py-1.5', linkLeadingIcon: 'text-primary' }"
            />
          </B24Card>
        </div>
      </div>
    </template>
  </B24Modal>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem } from '@bitrix24/b24ui-nuxt'
import ArrowRightLIcon from '@bitrix24/b24icons-vue/outline/ArrowRightLIcon'
import CircleCheckIcon from '@bitrix24/b24icons-vue/main/CircleCheckIcon'

const open = ref(false)

const benefits: NavigationMenuItem[] = [
  { label: 'Unlimited monitored lines', icon: CircleCheckIcon },
  { label: 'AI defect detection on camera streams', icon: CircleCheckIcon }
]
</script>

<template>
  <B24Modal
    v-model:open="open"
    title=""
    description=""
    :close="false"
    scrollable
    :b24ui="{
      content: 'sm:max-w-[788px] bg-[radial-gradient(110.42%_110.42%_at_-10.42%_31.25%,var(--ui-color-design-filled-boost-bg-gradient-1)_0%,var(--ui-color-design-filled-boost-bg-gradient-2)_58.65%,var(--ui-color-design-filled-boost-bg-gradient-3)_100%)]',
      body: 'relative p-6 md:p-8'
    }"
  >
    <B24Button label="Open marketing modal" color="air-secondary-accent" />

    <template #body>
      <div class="flex flex-col md:flex-row gap-6 min-h-full">
        <div class="flex-1 flex flex-col gap-3 text-white">
          <h2 class="text-2xl font-semibold leading-snug">
            Keep your factory floor moving — your Pro trial ends in 6 days
          </h2>
          <p class="text-white/85">
            Pro unlocks real-time OEE across every line, 90 days of historical
            analytics, AI defect detection on camera streams and predictive
            maintenance alerts. Teams on Pro ship 12% more units per shift on
            average.
          </p>
          <B24Link to="#" class="inline-flex text-white underline">
            Compare plans
          </B24Link>

          <div class="flex flex-wrap items-center gap-2 mt-auto pt-4">
            <B24Button
              label="Upgrade to Pro"
              color="air-primary"
              size="lg"
              :trailing-icon="ArrowRightLIcon"
            />
            <B24ModalDialogClose>
              <B24Button label="Remind me later" color="air-tertiary-accent" />
            </B24ModalDialogClose>
          </div>
        </div>

        <div class="md:w-80 shrink-0">
          <B24Card
            variant="tinted-no-accent"
            :b24ui="{
              header: 'flex items-center justify-between gap-2',
              body: 'space-y-4'
            }"
          >
            <template #header>
              <span class="font-semibold whitespace-nowrap">Production Insights · Pro</span>
              <B24Badge label="Recommended" color="air-primary" size="xs" class="shrink-0" />
            </template>

            <div>
              <div class="text-3xl font-semibold leading-none">
                +12%
              </div>
              <div class="text-sm text-description mt-1">
                average throughput uplift after upgrade
              </div>
            </div>

            <div class="space-y-1">
              <B24Progress :model-value="24" :max="30" color="air-primary" size="sm" />
              <div class="text-xs text-description">
                Trial used — 24 of 30 days
              </div>
            </div>

            <B24NavigationMenu
              orientation="vertical"
              :items="benefits"
              :b24ui="{ link: 'px-2 py-1.5', linkLeadingIcon: 'text-primary' }"
            />
          </B24Card>
        </div>
      </div>
    </template>
  </B24Modal>
</template>

Build a marketing / promo modal for any business or app.

Sales dynamics widget

A real-world example of surfacing a stats card behind a Modal. The widget itself is documented on the Card page; here it sits inside B24Modal so a trigger button opens it as a focused overlay — handy when the page chrome is busy and the dashboard needs a peek surface without a navigation jump.

<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>
  <B24Modal
    title=""
    description=""
    :close="false"
    :b24ui="{
      content: 'sm:max-w-md bg-transparent shadow-none border-0',
      body: 'p-0'
    }"
  >
    <B24Button label="Open sales widget" />

    <template #body>
      <B24Card
        variant="filled-copilot"
        :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>
  </B24Modal>
</template>

Wrap the Sales dynamics widget in a Modal so a trigger button opens it as a focused overlay.

With command palette

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

<script setup lang="ts">
import PersonSearchIcon from '@bitrix24/b24icons-vue/outline/PersonSearchIcon'

const searchTerm = ref('')

const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'modal-command-palette-users',
  params: { q: searchTerm },
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const } })) || []
  },
  immediate: false
})

const groups = computed(() => [{
  id: 'users',
  label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
  items: users.value || [],
  ignoreFilter: true
}])

function onOpen() {
  if (!users.value?.length) {
    execute()
  }
}
</script>

<template>
  <B24Modal
    :b24ui="{ content: 'p-0 pt-0 pb-2.5' }"
    @update:open="onOpen"
  >
    <B24Button
      label="Search users..."
      :icon="PersonSearchIcon"
    />

    <template #content>
      <B24CommandPalette
        v-model:search-term="searchTerm"
        :loading="status === 'pending'"
        :groups="groups"
        placeholder="Search users..."
        class="h-80"
      />
    </template>
  </B24Modal>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import PersonSearchIcon from '@bitrix24/b24icons-vue/outline/PersonSearchIcon'

const searchTerm = ref('')

const { data: users, status, execute } = await useLazyFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'modal-command-palette-users',
  params: { q: searchTerm },
  transform: (data: { id: number, name: string, email: string }[]) => {
    return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}`, loading: 'lazy' as const } })) || []
  },
  immediate: false
})

const groups = computed(() => [{
  id: 'users',
  label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users',
  items: users.value || [],
  ignoreFilter: true
}])

function onOpen() {
  if (!users.value?.length) {
    execute()
  }
}
</script>

<template>
  <B24Modal
    :b24ui="{ content: 'p-0 pt-0 pb-2.5' }"
    @update:open="onOpen"
  >
    <B24Button
      label="Search users..."
      :icon="PersonSearchIcon"
    />

    <template #content>
      <B24CommandPalette
        v-model:search-term="searchTerm"
        :loading="status === 'pending'"
        :groups="groups"
        placeholder="Search users..."
        class="h-80"
      />
    </template>
  </B24Modal>
</template>
This example uses useLazyFetch with immediate: false to only fetch data when the Modal opens.

API

Props

Prop Default Type
title string
description string
content DialogContentProps & Partial<EmitsToProps<DialogContentImplEmits>>

The content of the modal.

overlaytrueboolean

Render an overlay behind the modal.

overlayBlur'auto' "auto" | "on" | "off"

Render an overlay blur behind the modal. auto use motion-safe.

scrollablefalseboolean

When true, enables scrollable overlay mode where content scrolls within the overlay.

transitiontrueboolean

Animate the modal when opening or closing.

fullscreenfalseboolean

When true, the modal will take up the full screen.

portaltrue string | false | true | HTMLElement

Render the modal in a portal.

closetrueboolean | Omit<ButtonProps, LinkPropsKeys>

Display a close button to dismiss the modal. { size: 'xs', color: 'air-tertiary-no-accent' }

closeIconicons.closeIconComponent

The icon displayed in the close button.

dismissibletrueboolean

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

scrollbarThintrueboolean
openboolean

The controlled open state of the dialog. Can be binded as v-model:open.

defaultOpenboolean

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

modaltrueboolean

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

b24ui { overlay?: ClassNameValue; content?: ClassNameValue; contentWrapper?: ClassNameValue; header?: ClassNameValue; wrapper?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; close?: ClassNameValue; }

Slots

Slot Type
default{ open: boolean; }
content{ close: () => void; }
header{ close: () => void; }
title{}
description{}
actions{}
close{ b24ui: object; }
body{ close: () => void; }
footer{ close: () => void; }

Emits

Event Type
after:leave[]
after:enter[]
close:prevent[]
update:open[value: boolean]

Theme

https://github.com/bitrix24/b24ui/tree/main/src/theme/modal.ts
export default {
  slots: {
    overlay: 'fixed inset-0',
    content: 'base-mode bg-(--ui-color-bg-content-primary) flex flex-col gap-0 focus:outline-none',
    contentWrapper: 'flex-1 flex flex-col gap-0',
    header: 'px-4 py-4 sm:px-6 flex items-stretch justify-between gap-1.5 min-h-(--b24ui-header-height)',
    wrapper: 'min-h-full flex-1 flex flex-col items-start justify-center gap-0',
    body: 'px-4 sm:px-6 flex-1 text-(length:--ui-font-size-md) leading-normal text-(--ui-color-base-2) text-(length:--ui-font-size-md)',
    footer: 'px-4 py-4 sm:px-6 sm:py-6 flex items-center justify-between gap-3',
    title: 'font-[family-name:var(--ui-font-family-primary)] text-(--ui-color-base-1) font-(--ui-font-weight-bold) mb-0 text-[calc(var(--ui-font-size-2xl)+1px)]/(--ui-font-size-2xl) wrap-break-word',
    description: 'text-description text-(length:--ui-font-size-sm) wrap-break-word',
    close: '[--ui-btn-height:24px]'
  },
  variants: {
    overlayBlur: {
      auto: {
        overlay: 'motion-safe:backdrop-blur-0.5'
      },
      on: {
        overlay: 'backdrop-blur-0.5'
      },
      off: {
        overlay: ''
      }
    },
    transition: {
      true: {
        overlay: 'motion-safe:data-[state=open]:animate-[fade-in_200ms_ease-out] motion-safe:data-[state=closed]:animate-[fade-out_200ms_ease-in]',
        content: 'motion-safe:data-[state=open]:animate-[scale-in_200ms_ease-out] motion-safe:data-[state=closed]:animate-[scale-out_200ms_ease-in]'
      }
    },
    fullscreen: {
      true: {
        content: 'inset-0',
        body: 'py-4'
      },
      false: {
        content: 'w-[calc(100vw-2rem)] max-w-[32rem] rounded-[calc(var(--ui-border-radius-2xl)+2px)] shadow-lg',
        contentWrapper: ''
      }
    },
    overlay: {
      true: {
        overlay: 'bg-[#003366]/20'
      }
    },
    scrollable: {
      true: {
        overlay: 'overflow-y-auto',
        content: 'relative'
      },
      false: {
        content: 'fixed',
        body: 'overflow-y-auto'
      }
    },
    scrollbarThin: {
      true: {
        body: 'scrollbar-thin'
      }
    }
  },
  compoundVariants: [
    {
      scrollable: true,
      fullscreen: false,
      class: {
        overlay: 'grid place-items-center p-4 sm:py-8'
      }
    },
    {
      scrollable: false,
      fullscreen: false,
      class: {
        content: 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-h-[calc(100dvh-2rem)] sm:max-h-[calc(100dvh-4rem)]',
        contentWrapper: 'overflow-hidden'
      }
    },
    {
      scrollable: false,
      fullscreen: true,
      class: {
        content: 'max-h-[calc(100dvh)]',
        contentWrapper: 'overflow-hidden'
      }
    }
  ],
  defaultVariants: {
    scrollbarThin: true,
    overlayBlur: 'auto'
  }
}