Scrim
Headless backdrop component for overlay systems with automatic z-index management and dismiss handling.
Installation
The Scrim component uses the global stack context. For SSR applications, install the stack plugin:
import { createApp } from 'vue'
import { createStackPlugin } from '@vuetify/v0'
import App from './App.vue'
const app = createApp(App)
app.use(createStackPlugin())
app.mount('#app') For client-side only apps, you can skip plugin installation. The Scrim will use the default stack singleton automatically.
Usage
The Scrim component renders a backdrop that appears when any overlay is active. It automatically positions itself below the topmost overlay using z-index management from the stack context.
The scrim is rendered with aria-hidden — it is decorative. Don’t place interactive or focusable content in its default slot; keep controls inside the overlay itself.
Anatomy
<script setup lang="ts">
import { Scrim } from '@vuetify/v0'
</script>
<template>
<Scrim />
</template>Recipes
Blocking Mode
When the topmost overlay has blocking: true, the scrim will not dismiss on click. The isBlocking slot prop reflects this state:
<template>
<Scrim v-slot="{ isBlocking }" class="fixed inset-0">
<div :class="isBlocking ? 'bg-black/70' : 'bg-black/50'" />
</Scrim>
</template>Inline Rendering
By default, Scrim teleports to body. Disable teleport for inline rendering:
<template>
<div class="relative">
<Scrim :teleport="false" class="absolute inset-0 bg-black/50" />
<!-- Content -->
</div>
</template>Custom Stack Context
For isolated overlay systems, create a custom stack and provide it via Vue’s injection system:
<script setup lang="ts">
import { provide } from 'vue'
import { createStack, Scrim } from '@vuetify/v0'
// Create isolated stack (doesn't interfere with global stack)
const stack = createStack()
provide('v0:stack', stack)
</script>
<template>
<Scrim class="fixed inset-0 bg-black/50" />
</template>Transitions
The default transition is fade. Customize with the transition prop:
<template>
<Scrim transition="slide-fade" class="fixed inset-0 bg-black/50" />
</template>
<style>
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: opacity 0.3s ease;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
opacity: 0;
}
</style>Accessibility
Scrim renders each backdrop layer with aria-hidden="true", so assistive technology skips it entirely — the scrim is decorative and must never hold focusable or interactive content. Its only behavior is click-to-dismiss (suppressed when the topmost overlay is blocking), which is a pointer convenience, not the accessible dismissal path.
The accessible dismissal contract — Escape-to-close, focus trapping, and focus return — belongs to the overlay the scrim sits behind, not to the scrim itself. Pair Scrim with Dialog, which owns role="dialog", focus management, and keyboard handling; the scrim only paints the backdrop.
FAQ
The topmost overlay has blocking: true, which prevents click-to-dismiss. The isBlocking slot prop reflects this state so you can style the backdrop differently while blocking.
Only for SSR. Client-side-only apps can skip it — Scrim falls back to the default stack singleton automatically.
Pass :teleport="false" to render it inline within its parent, for example inside a relative container for a scoped overlay.
The default transition is fade. Pass a transition prop with your own name (for example slide-fade) and define the matching enter/leave CSS.
Yes. Call createStack() and provide('v0:stack', stack) above it so the scrim manages an isolated overlay system without touching the global stack.