A modern, drag-and-drop-enabled, keyboard-accessible Tabbar component inspired by browser-style tabs — with overflow handling and contextual icons.
🎓 Project by Aaron Joel B C
📅 Year: 2025
🌐 Purpose: Build an accessible and customizable Tabbar component in Vue 3, emulating browser behavior with enhanced interaction and animation.
The Dynamic Tabbar component provides advanced tab management capabilities:
- 🔀 Drag-and-drop reordering with ghost preview
- ⌨️ Full keyboard navigation support
- 💾 Overflow handling with smart focus restoration
- 🎨 Animated transitions and visual effects
- 🧠 Property-based tab typing (icons for chart, excel, alerts)
3D ghost styling for enhanced feedback when dragging:
dragTabAction(event, id) {
const ghost = dragElement.cloneNode(true);
ghost.id = 'drag-ghost';
ghost.style.transform = 'translate(-50%, -50%) perspective(800px)';
const moveGhost = (e) => {
const rotateX = -deltaY * 0.4;
const rotateY = deltaX * 0.3;
ghost.style.transform = `
translate(-50%, -50%)
perspective(800px)
rotateX(${rotateX * 5}deg)
rotateY(${rotateY * 5}deg)
`;
};
}
Property-based rendering with Font Awesome icons:
// In Tab.vue
propertyIcon: ["fa-pie-chart","fa-table","fa-exclamation-circle"],
computed: {
propertyNumber() {
return this.property === 'chart' ? 0 :
this.property === 'excel' ? 1 : 2;
}
}
When tabs exceed visible space, the component pushes extras into an overflow dialog:
toggleTabOverflowDialog() {
if (this.isOverFlowDialogOpen) {
document.getElementById("tabOverflowDialog").close();
this.isOverFlowDialogOpen = false;
document.querySelector(`.tab-wrapper[data-id="${this.lastFocusedTabId}"]`).focus();
} else {
document.getElementById("tabOverflowDialog").show();
this.isOverFlowDialogOpen = true;
}
}
Supports:
- Left/Right arrows to switch tabs
- Home/End to jump
- Delete to remove
- Focus restoration
handleKeydown(e, tabId) {
switch (e.key) {
case 'ArrowLeft':
this.handleShiftTabKey(e); break;
case 'ArrowRight':
this.handleTabKey(e); break;
case 'Home':
this.focusTab(this.tabs[0].uid); break;
case 'Delete':
this.closeTabAction(tabId); break;
}
}
Focus is returned intelligently after tab removal or dialog closure:
cancelDialogBox() {
if(this.tabs.length) {
document.querySelector(`.tab-wrapper[data-id="${this.lastFocusedTabId}"]`).focus();
} else {
document.getElementById('addTab').focus();
}
}
#drag-ghost {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.2));
}
#drag-ghost .tab :nth-child(1) {
color: dodgerblue;
}
Smooth transitions for dialog and tab moves:
@keyframes addDialogueAnimation {
from { transform: scale(0.5); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
.tab-wrapper {
transition: transform 0.2s ease;
}
- 💾 Tab persistence using
localStorage
- 📌 Custom tab types with extended icons
- 📱 Touch-friendly UX and gesture support
src/
├── components/
│ ├── Tab.vue
│ ├── Tabbar.vue
│ └── OverflowDialog.vue
├── App.vue
└── main.js
This project is licensed under the MIT License.
Let me know if you'd like a downloadable README.md
file or want to add diagrams/screenshots.