Slide-in mobile navigation drawer with bounce animation, optional home link, and contact section (address, phone, email) with icons. Controlled by isOpen, exposes slots for logo/subtitle and link list; uses primary/secondary colors and rounded right corners.
Prop | Type | Default / Req. | Description |
---|---|---|---|
title | String | — | The company or brand name shown at the top of the mobile menu (falls back to a slot if provided). |
subtitle | String | — | A secondary line of text displayed beneath the title (falls back to a slot if provided). |
links | Array | required | An array of navigation link objects with `{ name: String, path: String }` to render in the menu. |
mobileAdditionalHomePage | String | — | Optional text for a dedicated “Home” link shown above the regular links. |
adress | String | — | The physical address to display in the contact section (falls back to a slot if provided). |
phone | String | — | The phone number to display and link via `tel:` (falls back to a slot if provided). |
String | — | The email address to display and link via `mailto:` (falls back to a slot if provided). | |
isOpen | Boolean | false | Controls whether the mobile menu is visible (`true`) or hidden (`false`). |
logo
— Custom markup to replace the default title heading.subtitle
— Custom markup to replace the default subtitle heading.mobileLinks
— Custom list of navigation links. Overrides the default iteration over `links`.adress
— Custom markup to replace the default address block.phone
— Custom markup to replace the default phone block.email
— Custom markup to replace the default email block.@nuxt/icon
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>
const props = defineProps({
title: String,
subtitle: String,
links: {
type: Array,
validate: (value) => {
return value.every(link => link.name && link.path);
}
},
adress: String,
phone: String,
email: String,
mobileAdditionalHomePage: String,
isOpen: {
type: Boolean,
default: false,
},
});
</script>
<template>
<!-- Mobile menu -->
<div class="md:hidden fixed top-0 left-0 h-screen w-64 pl-[40px] flex flex-col shadow py-4 pr-4 bg-iq-primary rounded-r-iq-roundness z-50 transition-transform text-iq-paragraph-color"
:class="isOpen ? 'animate-slide-in-bounce' : '-translate-x-full'" id="mobile-menu">
<!-- Div with company name, wiktorias name and what is it -->
<div class="flex flex-col items-center justify-center text-iq-header-secondary-color">
<div v-if="title || subtitle" class="flex flex-col px-4 py-4 bg-iq-secondary rounded-r-iq-roundness shadow">
<slot name="logo">
<h1
class="text-2xl font-black uppercase font-iq-header leading-iq-header tracking-iq-header ">
{{ title }}
</h1>
</slot>
<slot name="subtitle">
<h2 class="text-xs mt-2 ">
{{ subtitle }}
</h2>
</slot>
</div>
</div>
<!-- Links -->
<div class="mt-4" >
<NuxtLink v-if="mobileAdditionalHomePage" to="/" @click="isOpen = false"
class="px-4 py-2 flex items-center transition justify-start overflow-hidden rounded-r-iq-roundness duration-700 focus:bg-white focus:text-iq-primary"
activeClass=" rounded-r-iq-roundness shadow bg-white text-iq-primary">
{{ mobileAdditionalHomePage }}
</NuxtLink>
<slot name="mobileLinks">
<NuxtLink v-for="link in links" :key="link.path" :to="link.path" @click="isOpen = false"
class="px-4 py-2 flex items-center transition justify-start overflow-hidden rounded-r-iq-roundness duration-700 focus:bg-white focus:text-iq-primary"
activeClass=" rounded-r-iq-roundness shadow bg-white text-iq-primary">
{{ link.name }}
</NuxtLink>
</slot>
</div>
<!-- Basic contact info, phone number, adress -->
<address class="text-sm font-bold rounded-tr-iq-roundness pl-4 not-italic flex flex-col gap-2 mt-auto">
<slot name="adress">
<p class="flex flex-row items-start gap-2 justify-between mt-2 text-xs" v-if="adress">
{{ adress }}
<Icon class="text-xl" name="material-symbols:location-on" />
</p>
</slot>
<slot name="phone">
<p class="flex flex-row items-start gap-2 justify-between mt-2 text-xs" v-if="phone">
<a :href="'tel:' + phone">{{ phone }}</a>
<Icon class="text-xl" name="material-symbols:call-outline-rounded" />
</p>
</slot>
<slot name="email">
<p class="flex flex-row items-start gap-2 justify-between mt-2 text-xs" v-if="email">
<a :href="'mailto:' + email"> {{ email }} </a>
<Icon class="text-xl" name="material-symbols:alternate-email" />
</p>
</slot>
</address>
</div>
</template>
<style scoped>
@keyframes slide-in-bounce {
0% {
transform: translateX(-100%);
}
60% {
transform: translateX(-10px);
}
80% {
transform: translateX(-50px);
}
100% {
transform: translateX(-40px);
}
}
.animate-slide-in-bounce {
animation: slide-in-bounce 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
</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 websiteName = "Website Name"; // Replace with your actual website name
const websiteDescription = "Catching headline for your website"; // Replace with your actual website description
const links = [
{ name: "Get started", path: "/get-started" },
{ name: "Learn more", path: "/learn-more" },
{ name: "Components", path: "/components" },
{ name: "Some page", path: "/" },
{ name: "Kontakt", path: "/" },
];
const adress = "Best street, 00-000 NYC";
const phone = "+66 123 456 789";
const email = "[email protected]";
const isOpen = ref(true);
</script>
<template>
<div>
<OnlyMobile @click="isOpen = !isOpen" />
<NavSpringMobile
:links="links"
:title="websiteName"
:subtitle="websiteDescription"
:adress="adress"
:phone="phone"
:email="email"
:isOpen="isOpen"
mobileAdditionalHomePage="Home"
/>
</div>
</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.