A comprehensive context menu component library for Vue 3 applications with themes, keyboard navigation, and programmatic control
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Vue3 Context Menu provides a comprehensive theming system with built-in themes and extensive customization options. The library includes multiple pre-designed themes with light and dark variants, plus support for custom themes through CSS variables and class overrides.
The library includes several built-in themes designed to match different design systems and operating system styles.
type ThemeNames =
| 'default'
| 'default dark'
| 'flat'
| 'flat dark'
| 'win10'
| 'win10 dark'
| 'mac'
| 'mac dark';Theme Usage:
// Functional API
ContextMenu.showContextMenu({
x: e.x,
y: e.y,
theme: 'win10 dark',
items: [...]
});
// Component API
const menuOptions = {
theme: 'mac',
x: 100,
y: 100
};<template>
<context-menu :options="menuOptions" v-model:show="show">
<!-- menu items -->
</context-menu>
</template>
<script setup>
const menuOptions = {
theme: 'flat dark',
x: 0,
y: 0
};
</script>Clean, modern appearance suitable for most applications.
Minimalist design with flat elements and subtle borders.
Matches Windows 10 context menu styling.
Follows macOS design guidelines and visual style.
<template>
<div class="theme-showcase">
<!-- Default theme -->
<button @click="showDefaultMenu">Default Theme</button>
<!-- Windows 10 theme -->
<button @click="showWin10Menu">Windows 10 Theme</button>
<!-- macOS theme -->
<button @click="showMacMenu">macOS Theme</button>
<!-- Flat theme -->
<button @click="showFlatMenu">Flat Theme</button>
</div>
</template>
<script setup>
import ContextMenu from '@imengyu/vue3-context-menu';
function showDefaultMenu(e) {
ContextMenu.showContextMenu({
x: e.clientX,
y: e.clientY,
theme: 'default',
items: getThemeMenuItems()
});
}
function showWin10Menu(e) {
ContextMenu.showContextMenu({
x: e.clientX,
y: e.clientY,
theme: 'win10',
items: getThemeMenuItems()
});
}
function showMacMenu(e) {
ContextMenu.showContextMenu({
x: e.clientX,
y: e.clientY,
theme: 'mac',
items: getThemeMenuItems()
});
}
function showFlatMenu(e) {
ContextMenu.showContextMenu({
x: e.clientX,
y: e.clientY,
theme: 'flat',
items: getThemeMenuItems()
});
}
function getThemeMenuItems() {
return [
{ label: 'New File', icon: 'file-icon' },
{ label: 'Open File', icon: 'folder-icon' },
{ divided: true, label: 'Settings', icon: 'settings-icon' }
];
}
</script>Create custom themes by defining CSS classes and overriding CSS variables.
/* Custom theme definition */
.mx-context-menu.my-custom-theme {
--mx-menu-backgroud: #2d3748;
--mx-menu-border-color: #4a5568;
--mx-menu-shadow-color: rgba(0, 0, 0, 0.3);
--mx-menu-hover-backgroud: #4a5568;
--mx-menu-text-color: #e2e8f0;
--mx-menu-hover-text-color: #ffffff;
--mx-menu-disabled-text-color: #718096;
--mx-menu-separator-color: #4a5568;
--mx-menu-placeholder-width: 24px;
/* Menu container styling */
padding: 6px 0;
border-radius: 8px;
box-shadow: 0 4px 12px var(--mx-menu-shadow-color);
border: 1px solid var(--mx-menu-border-color);
background: var(--mx-menu-backgroud);
/* Menu item styling */
.mx-context-menu-item {
padding: 8px 16px;
margin: 2px 4px;
border-radius: 4px;
color: var(--mx-menu-text-color);
&:hover:not(.disabled) {
background: var(--mx-menu-hover-backgroud);
color: var(--mx-menu-hover-text-color);
}
&.disabled {
color: var(--mx-menu-disabled-text-color);
opacity: 0.6;
}
}
/* Separator styling */
.mx-context-menu-separator {
border-color: var(--mx-menu-separator-color);
margin: 4px 8px;
}
}Usage of Custom Theme:
ContextMenu.showContextMenu({
x: e.x,
y: e.y,
theme: 'my-custom-theme',
items: [...]
});All available CSS variables for theme customization:
.mx-context-menu {
/* Background colors */
--mx-menu-backgroud: #ffffff;
--mx-menu-hover-backgroud: #f0f0f0;
--mx-menu-active-backgroud: #e0e0e0;
/* Text colors */
--mx-menu-text-color: #333333;
--mx-menu-hover-text-color: #333333;
--mx-menu-disabled-text-color: #999999;
/* Border and shadow */
--mx-menu-border-color: #e0e0e0;
--mx-menu-shadow-color: rgba(0, 0, 0, 0.15);
/* Separator */
--mx-menu-separator-color: #e0e0e0;
/* Layout */
--mx-menu-placeholder-width: 20px;
--mx-menu-padding: 4px 0;
--mx-menu-item-padding: 6px 12px;
--mx-menu-border-radius: 4px;
/* Icons */
--mx-menu-icon-size: 16px;
--mx-menu-check-size: 14px;
--mx-menu-arrow-size: 12px;
/* Transitions */
--mx-menu-transition-duration: 0.15s;
}/* Futuristic theme with gradients and animations */
.mx-context-menu.futuristic-theme {
--mx-menu-backgroud: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--mx-menu-hover-backgroud: rgba(255, 255, 255, 0.1);
--mx-menu-text-color: #ffffff;
--mx-menu-shadow-color: rgba(102, 126, 234, 0.3);
--mx-menu-border-color: rgba(255, 255, 255, 0.2);
padding: 8px 0;
border-radius: 12px;
backdrop-filter: blur(10px);
border: 1px solid var(--mx-menu-border-color);
box-shadow:
0 8px 32px var(--mx-menu-shadow-color),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
.mx-context-menu-item {
padding: 10px 16px;
margin: 2px 6px;
border-radius: 6px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.1),
transparent
);
transition: left 0.5s;
}
&:hover:not(.disabled) {
background: var(--mx-menu-hover-backgroud);
transform: translateX(4px);
&::before {
left: 100%;
}
}
.label {
font-weight: 500;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
}
}<template>
<div>
<div class="theme-controls">
<select v-model="currentTheme" @change="updateTheme">
<option value="default">Default Light</option>
<option value="default dark">Default Dark</option>
<option value="win10">Windows 10</option>
<option value="win10 dark">Windows 10 Dark</option>
<option value="mac">macOS</option>
<option value="mac dark">macOS Dark</option>
<option value="flat">Flat</option>
<option value="flat dark">Flat Dark</option>
</select>
</div>
<div @contextmenu="showThemedMenu">
Right-click to see themed menu
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ContextMenu from '@imengyu/vue3-context-menu';
const currentTheme = ref('default');
function showThemedMenu(e) {
e.preventDefault();
ContextMenu.showContextMenu({
x: e.clientX,
y: e.clientY,
theme: currentTheme.value,
items: [
{ label: 'New', icon: 'new-icon' },
{ label: 'Open', icon: 'open-icon' },
{ label: 'Save', icon: 'save-icon', shortcut: 'Ctrl+S' },
{ divided: true, label: 'Settings', icon: 'settings-icon' }
]
});
}
function updateTheme() {
// Theme will be applied to next menu
console.log(`Theme changed to: ${currentTheme.value}`);
}
</script><template>
<div @contextmenu="showSystemThemedMenu">
Right-click for system-matched theme
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import ContextMenu from '@imengyu/vue3-context-menu';
const systemTheme = ref('default');
function detectSystemTheme() {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
systemTheme.value = 'default dark';
} else {
systemTheme.value = 'default';
}
}
function showSystemThemedMenu(e) {
e.preventDefault();
ContextMenu.showContextMenu({
x: e.clientX,
y: e.clientY,
theme: systemTheme.value,
items: getMenuItems()
});
}
onMounted(() => {
detectSystemTheme();
// Listen for system theme changes
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', detectSystemTheme);
});
</script>Configure global icon font classes for consistent icon display:
interface IconConfiguration {
/** Global icon font class name */
iconFontClass?: string;
/** Reserve icon width for items without icons */
preserveIconWidth?: boolean;
}Usage Examples:
// Global icon font configuration
ContextMenu.showContextMenu({
x: e.x,
y: e.y,
iconFontClass: 'fa', // Font Awesome
preserveIconWidth: true,
items: [
{ label: 'New', icon: 'fa-file' },
{ label: 'Open', icon: 'fa-folder-open' },
{ label: 'Save', icon: 'fa-save' }
]
});
// Per-item icon font class
ContextMenu.showContextMenu({
x: e.x,
y: e.y,
items: [
{
label: 'Custom Icon',
icon: 'custom-icon',
iconFontClass: 'material-icons'
}
]
});Support for SVG symbols and custom SVG icons:
// SVG symbol icons
ContextMenu.showContextMenu({
x: e.x,
y: e.y,
items: [
{
label: 'Home',
svgIcon: '#icon-home',
svgProps: {
width: 16,
height: 16,
fill: 'currentColor'
}
}
]
});<template>
<context-menu>
<context-menu-item label="Custom Icon">
<template #icon>
<svg class="custom-icon" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
</svg>
</template>
</context-menu-item>
</context-menu>
</template>
<style scoped>
.custom-icon {
width: 16px;
height: 16px;
fill: currentColor;
}
</style>