Overlay
Overlays can be used to reveal larger contents or options that are hidden behind a button or a similar component. The content is animated on top of the document and the rest of the interface is blocked.
Requires FluxRoot
This overlay renders through a parent <FluxRoot>. Without one in your app, nothing appears and no error is thrown.
Props
is-closeable?: boolean
If the overlay is closeable with the escape key.
size?: FluxSize
The size of the overlay.
Default: small
Emits
close: []
Emitted when the overlay is closed.
Slots
default
The contents of the overlay. For the best result, use a with a v-if to control its visibility.
Examples
Basic
A basic overlay.
<template>
<FluxSecondaryButton
icon-leading="copy"
label="Open overlay"
@click="isOpen = true"/>
<FluxOverlay size="medium">
<FluxPane v-if="isOpen">
<FluxPaneHeader title="Overlay"/>
<FluxPaneBody>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam aspernatur, consequuntur debitis eligendi eum magnam necessitatibus nulla perferendis sequi voluptate. Aut consequatur ducimus, quaerat quos ratione sequi veniam? Quis, rem.</p>
<p>Alias dolorum laboriosam pariatur qui ut. Debitis distinctio expedita impedit ipsam voluptatem? Asperiores assumenda dolorem ducimus earum nam placeat ut velit. Ab debitis dicta in itaque quos ut veritatis voluptatum.</p>
<p>Ducimus nemo officiis, quas saepe sequi tempora unde! Debitis dolores eos exercitationem itaque laboriosam magni nam, neque officiis, pariatur quis ratione recusandae reiciendis repudiandae tempore temporibus veritatis vero, voluptates? Earum?</p>
</FluxPaneBody>
<FluxPaneFooter>
<FluxSecondaryButton
label="Close"
@click="isOpen = false"/>
</FluxPaneFooter>
</FluxPane>
</FluxOverlay>
</template>
<script
setup
lang="ts">
import { FluxOverlay, FluxPane, FluxPaneBody, FluxPaneFooter, FluxPaneHeader, FluxSecondaryButton } from '@flux-ui/components';
import { ref } from 'vue';
const isOpen = ref(false);
</script>Tabs
An overlay with a sticky header and tab bar.
<template>
<FluxSecondaryButton
icon-leading="copy"
label="Open overlay"
@click="isOpen = true"/>
<FluxOverlay size="medium">
<FluxPane v-if="isOpen">
<FluxPaneHeader title="Overlay">
<FluxSecondaryButton
icon-before="xmark"
@click="isOpen = false"/>
</FluxPaneHeader>
<FluxTabs>
<template #tabs="{ modelValue, tabs, activate }">
<FluxTabBar>
<template
v-for="(tab, index) in tabs"
:key="index">
<FluxTabBarItem
:icon="tab.icon"
:is-active="modelValue === index"
:label="tab.label"
@click="activate(index)"/>
</template>
</FluxTabBar>
</template>
<FluxTab label="Common">
<FluxPaneBody>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam aspernatur, consequuntur debitis eligendi eum magnam necessitatibus nulla perferendis sequi voluptate. Aut consequatur ducimus, quaerat quos ratione sequi veniam? Quis, rem.</p>
<p>Alias dolorum laboriosam pariatur qui ut. Debitis distinctio expedita impedit ipsam voluptatem? Asperiores assumenda dolorem ducimus earum nam placeat ut velit. Ab debitis dicta in itaque quos ut veritatis voluptatum.</p>
<p>Ducimus nemo officiis, quas saepe sequi tempora unde! Debitis dolores eos exercitationem itaque laboriosam magni nam, neque officiis, pariatur quis ratione recusandae reiciendis repudiandae tempore temporibus veritatis vero, voluptates? Earum?</p>
<p>Accusamus adipisci aliquam aperiam beatae dolore, doloremque ducimus eius eligendi eos illo laudantium magnam maxime nam nemo nulla pariatur perferendis quae quas quos, ratione sequi sint tempora tempore veritatis voluptas voluptatem.</p>
<p>Consectetur cupiditate deleniti dolorum, ducimus eaque et eum facere fugit id impedit ipsa iste laborum magni molestiae necessitatibus nesciunt nihil nulla obcaecati officia perspiciatis quas quibusdam quidem, quos ratione tempora voluptatibus.</p>
</FluxPaneBody>
</FluxTab>
<FluxTab label="Advanced">
<FluxPaneBody>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi consequatur dolorem dolorum ducimus enim, fuga, hic illo labore libero, magnam minima odio pariatur porro quasi quidem quis repellat reprehenderit vero?</p>
<p>Accusamus adipisci aliquam aperiam beatae dolore, doloremque ducimus eius eligendi eos illo laudantium magnam maxime nam nemo nulla pariatur perferendis quae quas quos, ratione sequi sint tempora tempore veritatis voluptas voluptatem.</p>
<p>Consectetur cupiditate deleniti dolorum, ducimus eaque et eum facere fugit id impedit ipsa iste laborum magni molestiae necessitatibus nesciunt nihil nulla obcaecati officia perspiciatis quas quibusdam quidem, quos ratione tempora voluptatibus.</p>
</FluxPaneBody>
</FluxTab>
</FluxTabs>
<FluxPaneFooter>
<FluxSecondaryButton
label="Close"
@click="isOpen = false"/>
<FluxSpacer/>
<FluxPrimaryButton
icon-before="circle-check"
label="Save"
@click="isOpen = false"/>
</FluxPaneFooter>
</FluxPane>
</FluxOverlay>
</template>
<script
setup
lang="ts">
import { FluxOverlay, FluxPane, FluxPaneBody, FluxPaneFooter, FluxPaneHeader, FluxPrimaryButton, FluxSecondaryButton, FluxSpacer, FluxTab, FluxTabBar, FluxTabBarItem, FluxTabs } from '@flux-ui/components';
import { ref } from 'vue';
const isOpen = ref(false);
</script>Re-authenticatie
A re-authentication overlay.
<template>
<FluxDestructiveButton
icon-leading="lock"
label="Re-authenticate"
@click="isOpen = true"/>
<FluxOverlay size="small">
<FluxPane v-if="isOpen">
<FluxForm>
<FluxPaneBody>
<h2>You've been signed out.</h2>
</FluxPaneBody>
<FluxPaneBody>
<p>To continue using your account, please sign in again using your password.</p>
</FluxPaneBody>
<FluxPaneBody>
<FluxFormColumn>
<FluxFormField label="Username">
<FluxFormInput type="email" model-value="bas@flux-ui.dev" disabled is-readonly/>
</FluxFormField>
<FluxFormField label="Password">
<FluxFormInput type="password" model-value="12345678"/>
</FluxFormField>
</FluxFormColumn>
</FluxPaneBody>
<FluxPaneFooter>
<FluxButtonStack
is-filled
direction="vertical">
<FluxPrimaryButton
icon-trailing="user-key"
label="Sign in"/>
<FluxSecondaryButton
label="Cancel"
@click="isOpen = false"/>
</FluxButtonStack>
</FluxPaneFooter>
</FluxForm>
</FluxPane>
</FluxOverlay>
</template>
<script
setup
lang="ts">
import { FluxButtonStack, FluxDestructiveButton, FluxForm, FluxFormColumn, FluxFormField, FluxFormInput, FluxOverlay, FluxPane, FluxPaneBody, FluxPaneFooter, FluxPrimaryButton, FluxSecondaryButton } from '@flux-ui/components';
import { ref } from 'vue';
const isOpen = ref(false);
</script>Router-driven
Pair the overlay with Vue Router to open it through navigation. Define an overlay named view on every route that should appear inside the overlay, and render a single <RouterView name="overlay"/> from your top-level layout.
The layout watches route.matched for the first record that exposes the overlay view. When such a record is matched the <RouterView> renders inside the overlay; otherwise the overlay stays empty and the transition closes it.
<template>
<RouterView/>
<FluxOverlay
:key="overlayRoute?.fullPath"
size="medium">
<RouterView
v-if="overlayRoute"
name="overlay"
:route="overlayRoute"/>
</FluxOverlay>
</template>
<script
lang="ts"
setup>
import { FluxOverlay } from '@flux-ui/components';
import { computed } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const overlayRoute = computed(() => {
const match = route.matched.find(record => 'overlay' in (record.components ?? {}));
if (!match) {
return null;
}
return route;
});
</script>import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/projects',
component: () => import('./views/Projects.vue'),
children: [
{
name: 'projects-create',
path: 'create',
components: {
overlay: () => import('./views/ProjectsCreate.vue')
}
},
{
name: 'projects-edit',
path: 'edit/:id',
components: {
overlay: () => import('./views/ProjectsEdit.vue')
}
}
]
}
];
export const router = createRouter({
history: createWebHistory(),
routes
});TIP
Push to one of the named routes (e.g. router.push({name: 'projects-create'})) to open the overlay, and call router.back() from inside the overlay to close it. This keeps the URL in sync with what's on screen and makes the overlay deep-linkable.