Guide

Overlay

Customize the dimming layer — slot content, pointer behavior, per-group overlays, and the data attributes used for styling and animations.

Overlay

<ModalOverlay> is the optional dimming layer rendered inside <ModalTarget>. It mounts whenever the group has at least one active modal and unmounts after the last one closes — driven by <Presence> from reka-ui, so enter/exit animations are respected.

The overlay is purely visual by default: position: absolute; inset: 0; pointer-events: none. Clicks pass through to [data-modal-region], which is where the package detects "click outside content" to close the topmost modal.

Minimal usage

<script setup lang="ts">
import { ModalOverlay, ModalTarget } from '@kolirt/vue-modal'
</script>

<template>
  <ModalTarget group="default">
    <ModalOverlay class="bg-black/50" />
  </ModalTarget>
</template>

You can omit <ModalOverlay> entirely — modals will render without a backdrop.

Customizing with the default slot

The overlay exposes a default slot. Use it to layer decorative elements on top of the base dimming color: gradients, noise textures, blur layers, branding watermarks, animated backgrounds.

<ModalOverlay class="overlay">
  <div class="overlay__noise" />
  <div class="overlay__gradient" />
</ModalOverlay>
.overlay {
  background: rgba(0, 0, 0, 0.55);
}

.overlay__noise {
  position: absolute;
  inset: 0;
  background: url('/noise.png') repeat;
  opacity: 0.08;
  mix-blend-mode: overlay;
}

.overlay__gradient {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, transparent 0%, rgba(0, 0, 0, 0.4) 100%);
}

Pointer events

The root [data-modal-overlay] is pointer-events: none. Slotted children inherit that — they're decorative and won't intercept clicks, which preserves the "click overlay to close" behavior driven by [data-modal-region].

If a slotted element must receive interaction (e.g. a logo link, a watermark with a tooltip), enable pointer events on that specific element:

.overlay__logo {
  pointer-events: auto;
}

Keep this rare. Interactive controls (close buttons, action bars) usually belong inside <ModalContent> so they participate in focus management and don't compete with the click-outside-to-close behavior.

Data attributes for styling

AttributeWhen present
[data-modal-overlay]Always — the anchor for CSS rules
data-state="open"Group has at least one active modal
data-state="closed"Last modal in the group is exiting
data-instantTopmost modal was opened with instantEnter — overlay enter animation should be suppressed
[data-modal-overlay] {
  background: rgba(0, 0, 0, 0.5);
  transition: opacity 200ms ease;
}

[data-modal-overlay][data-state='open'] {
  opacity: 1;
}

[data-modal-overlay][data-state='closed'] {
  opacity: 0;
}

The package ships a built-in rule that disables the overlay enter animation while data-instant is set on data-state="open", so instantEnter modals don't get a fading backdrop. Exit animations still run normally.

Per-group overlays

Each <ModalTarget> mounts its own <ModalOverlay>. Different groups can have different backdrops, opacities, or animations without conflicting with each other:

<ModalTarget group="default">
  <ModalOverlay class="bg-black/40" />
</ModalTarget>

<ModalTarget group="confirm">
  <ModalOverlay class="bg-black/70 backdrop-blur-sm" />
</ModalTarget>

See Multiple targets for stacking, z-index, and per-target overrides.

Errors

<ModalOverlay> must be a descendant of <ModalTarget>. Mounting it standalone throws:

[@kolirt/vue-modal] <ModalOverlay> must be used inside <ModalTarget>.
Copyright © 2026