Fixed, client-side mobile menu with backdrop and animated slide transitions. Exposes toggleMenu via defineExpose, supports logo slot and optional footer; uses design tokens for typography and radius.
Prop | Type | Default / Req. | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
logo | String | — | Text or branding rendered as the logo/home link. Can be replaced via the `logo` slot. | ||||||||||||
links | Array | required | An array of navigation link objects to display in the panel. Each item must include `name` and `path` as strings. | ||||||||||||
|
logo
— Custom markup to replace the default logo/home link content.footer
— Custom markup for the footer area below the navigation links.Method | Description |
---|---|
toggleMenu | Toggles the open/closed state of the mobile menu panel. |
You may use these UI components in your own personal or commercial projects. You may not resell, redistribute, sublicense, or package them as standalone assets or template/library packs.
Full terms: End-User License Agreement
Below you can expand the main implementation file and any supporting components. Use the “Copy” button to grab a snippet straight to your clipboard.
These are the raw components that are required to run this example. Copy-paste them into your project. Most likely you will not change anything in these files, but you can if you want to. These are the components that are used in the main implementation file.
<script setup>
import { ref } from "vue";
defineProps({
logo: {
type: String,
},
links: {
type: Array,
required: true,
validate: (value) => {
return value.every((item) => {
return typeof item.name === "string" && typeof item.path === "string";
});
},
}
});
const isOpen = defineModel()
/**
* Toggle the mobile menu open/close state
*/
function toggleMenu() {
isOpen.value = !isOpen.value;
}
defineExpose({
toggleMenu,
});
</script>
<template>
<nav class="fixed top-0 left-0 w-full z-50 font-iq-paragraph leading-iq-paragraph tracking-iq-paragraph text-iq-paragraph-color">
<!-- Backdrop -->
<transition name="fade">
<div
v-if="isOpen"
class="fixed inset-0 bg-black/50 backdrop-blur-sm"
@click="toggleMenu"
/>
</transition>
<!-- Menu panel -->
<transition name="slide">
<aside
v-if="isOpen"
class="fixed top-0 left-0 h-full w-64 py-12 bg-white/10 backdrop-blur-lg ring-1 ring-white/20 rounded-r-iq-roundness shadow-xl flex flex-col"
>
<div class="flex flex-row items-center justify-between px-6">
<!-- Name -->
<NuxtLink
to="/"
class="text-2xl font-bold text-white tracking-tight"
@click="toggleMenu"
>
<slot name="logo">{{ logo }}</slot>
</NuxtLink>
<button class="self-end p-2" @click="toggleMenu" aria-label="Close menu">
<!-- X Icon -->
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-white/80"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<ul class="mt-6 space-y-2">
<li v-for="item in links" :key="item.path">
<NuxtLink
:to="item.path"
class="block px-6 py-3 bg-white/5 text-lg font-medium text-white active:bg-white/15 transition duration-150"
active-class="bg-white/15"
@click="toggleMenu"
>
{{ item.name }}
</NuxtLink>
</li>
</ul>
<div class="mt-auto border-t border-white/20 pt-4 px-6">
<slot name="footer">
<a
href="#"
class="block text-sm text-white/70 hover:text-white/90 transition"
>
© 2025 My Company
</a>
</slot>
</div>
</aside>
</transition>
</nav>
</template>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.slide-enter-active {
transition: transform 0.3s ease;
}
.slide-enter-from {
transform: translateX(-100%);
}
.slide-leave-active {
transition: transform 0.2s ease;
}
.slide-leave-to {
transform: translateX(-100%);
}
</style>
This is the main Vue file that uses the component. Copy-paste this into your project. In this code feel free to change anything you like, such as the component name, props, or class. This is the place where you control the main component.
<script setup>
const links = [
{ name: "Home", path: "/" },
{ name: "Learn More", path: "/learn-more" },
{ name: "Get Started", path: "/get-started" },
{ name: "Components", path: "/components", highlight: true },
];
const isOpen = ref(false);
</script>
<template>
<!-- REPLACE with desktop menu -->
<OnlyMobile @click="isOpen = !isOpen" />
<NavSlimMobile
:links="links"
logo="Flex Ui Lib"
v-model="isOpen"
class="md:hidden text-2xl p-2 rounded hover:bg-white/10 transition"
aria-label="Toggle navigation"
/>
</template>
Decide whether you want a global design-system or a one-off inline snippet.
Complete @theme
block – import once and share across every component.
Copy the code below into main.css
file. It is most likely in assets/css/main.css
directory.
:style
binding – paste straight onto any of ours components.
Copy the code below and paste it into the :style
binding of the component.
I wish I could automate every little thing—but for now you’ll need to handle these final steps by hand. Apologies for the extra work!
iq-card-*
style Now that you’ve picked a card preset, copy its CSS into your @layer components
block in main.css
. This ensures every `iq-card
` wrapper will look just right.
iq-cta
iq-cta is the main call to action button class. It’s used in many places across the components. But for now it is only a single class that you can customize. You can copy the code below and paste it into your @layer components
block in main.css
. In future you will be able to fully customize it from our UI and choose from many presets.
I didn't have time to figure out consistency. Although there are no actions required, be mindful that the forms might not be entirely consistent with the design system. A quick once-over will keep everything looking sharp.