Skip to content

Instantly share code, notes, and snippets.

@naranyala
Created August 10, 2025 03:15
Show Gist options
  • Save naranyala/4c97858b6eb736eae0f99631af85860f to your computer and use it in GitHub Desktop.
Save naranyala/4c97858b6eb736eae0f99631af85860f to your computer and use it in GitHub Desktop.
the only one dashboard layout you need, of course mobile-friendly
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimal Dashboard</title>
<link rel="stylesheet" href="https://unpkg.com/open-props" />
<link rel="stylesheet" href="https://unpkg.com/open-props/normalize.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
[v-cloak] {
display: none;
}
body {
font-family: var(--font-sans);
background: var(--gray-1);
color: var(--gray-9);
}
.dashboard {
display: grid;
grid-template-columns: 240px 1fr 280px;
min-height: 100vh;
gap: 0;
}
.sidebar,
.right-sidebar {
background: white;
padding: var(--size-4);
border-right: 1px solid var(--gray-3);
overflow-y: auto;
}
.right-sidebar {
border-right: none;
border-left: 1px solid var(--gray-3);
}
.sidebar h2,
.right-sidebar h2 {
font-size: var(--font-size-3);
margin-bottom: var(--size-4);
color: var(--gray-9);
font-weight: var(--font-weight-6);
}
.menu-list {
list-style: none;
padding: 0;
margin: 0;
}
.menu-item {
margin-bottom: var(--size-2);
}
.menu-link {
display: flex;
align-items: center;
gap: var(--size-3);
padding: var(--size-3);
text-decoration: none;
color: var(--gray-7);
border-radius: var(--radius-2);
transition: all 0.2s ease;
cursor: pointer;
}
.menu-link:hover {
background: var(--gray-2);
color: var(--gray-9);
}
.menu-link.active {
background: var(--indigo-9);
color: white;
}
.main-content {
padding: var(--size-5);
background: var(--gray-1);
}
.main-content h1 {
font-size: var(--font-size-5);
margin-bottom: var(--size-4);
font-weight: var(--font-weight-6);
}
.card {
background: white;
border-radius: var(--radius-3);
padding: var(--size-4);
box-shadow: var(--shadow-2);
margin-bottom: var(--size-4);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--size-4);
margin-bottom: var(--size-4);
}
.stat-card {
display: flex;
align-items: center;
gap: var(--size-4);
}
.stat-icon {
font-size: var(--font-size-6);
color: var(--indigo-9);
}
.stat-value {
font-size: var(--font-size-5);
font-weight: var(--font-weight-7);
line-height: 1;
}
.stat-label {
color: var(--gray-6);
font-size: var(--font-size-1);
}
.activity-item,
.notification-item {
padding: var(--size-3) 0;
border-bottom: 1px solid var(--gray-2);
display: flex;
gap: var(--size-3);
}
.activity-icon,
.notification-icon {
color: var(--indigo-9);
min-width: 20px;
}
.activity-time {
font-size: var(--font-size-0);
color: var(--gray-6);
margin-top: var(--size-1);
}
/* Mobile Header */
.mobile-header {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
background: white;
border-bottom: 1px solid var(--gray-3);
z-index: 200;
padding: 0 var(--size-4);
align-items: center;
justify-content: space-between;
}
.mobile-header .brand {
font-weight: var(--font-weight-6);
font-size: var(--font-size-3);
}
.mobile-header .actions {
display: flex;
gap: var(--size-2);
}
.btn {
background: var(--gray-2);
border: none;
padding: var(--size-2);
border-radius: var(--radius-2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
}
.btn:hover {
background: var(--gray-3);
}
/* Mobile Backdrop */
.mobile-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 150;
display: none;
}
.mobile-backdrop.active {
display: block;
}
@media (max-width: 1024px) {
.mobile-header {
display: flex;
}
.dashboard {
grid-template-columns: 1fr;
}
.sidebar,
.right-sidebar {
position: fixed;
top: 0;
height: 100vh;
z-index: 180;
transform: translateX(-100%);
transition: transform 0.3s ease;
width: 280px;
}
.right-sidebar {
right: 0;
left: auto;
transform: translateX(100%);
}
.sidebar.active {
transform: translateX(0);
}
.right-sidebar.active {
transform: translateX(0);
}
.main-content {
padding-top: calc(60px + var(--size-4));
}
}
</style>
</head>
<body>
<div id="app" v-cloak>
<!-- Mobile Header -->
<header class="mobile-header">
<div class="brand">Dashboard</div>
<div class="actions">
<button @click="toggleLeftSidebar" class="btn">
<i class="fas fa-bars"></i>
</button>
<button @click="toggleRightSidebar" class="btn">
<i class="fas fa-bell"></i>
</button>
</div>
</header>
<!-- Mobile Backdrop -->
<div class="mobile-backdrop" :class="{ active: showMobileBackdrop }" @click="closeMobileSidebars"></div>
<div class="dashboard">
<!-- Left Sidebar -->
<aside class="sidebar" :class="{ active: leftSidebarActive }">
<h2>Dashboard</h2>
<nav>
<ul class="menu-list">
<li class="menu-item">
<a class="menu-link" :class="{active: activeTab === 'dashboard'}" @click="setActiveTab('dashboard')">
<i class="fas fa-tachometer-alt"></i>
<span>Dashboard</span>
</a>
</li>
<li class="menu-item">
<a class="menu-link" :class="{active: activeTab === 'analytics'}" @click="setActiveTab('analytics')">
<i class="fas fa-chart-line"></i>
<span>Analytics</span>
</a>
</li>
<li class="menu-item">
<a class="menu-link" :class="{active: activeTab === 'users'}" @click="setActiveTab('users')">
<i class="fas fa-users"></i>
<span>Users</span>
</a>
</li>
<li class="menu-item">
<a class="menu-link" :class="{active: activeTab === 'orders'}" @click="setActiveTab('orders')">
<i class="fas fa-shopping-cart"></i>
<span>Orders</span>
</a>
</li>
</ul>
</nav>
</aside>
<!-- Main Content -->
<main class="main-content">
<h1>{{getTabLabel(activeTab)}}</h1>
<!-- Dashboard Content -->
<div v-if="activeTab === 'dashboard'">
<div class="stats-grid">
<div class="card stat-card">
<div class="stat-icon"><i class="fas fa-users"></i></div>
<div>
<div class="stat-value">{{stats.users}}</div>
<div class="stat-label">Total Users</div>
</div>
</div>
<div class="card stat-card">
<div class="stat-icon"><i class="fas fa-shopping-cart"></i></div>
<div>
<div class="stat-value">{{stats.orders}}</div>
<div class="stat-label">New Orders</div>
</div>
</div>
<div class="card stat-card">
<div class="stat-icon"><i class="fas fa-boxes"></i></div>
<div>
<div class="stat-value">{{stats.products}}</div>
<div class="stat-label">Products</div>
</div>
</div>
</div>
<div class="card">
<h3>Recent Activity</h3>
<div v-for="activity in recentActivities" :key="activity.id" class="activity-item">
<div class="activity-icon">
<i :class="activity.icon"></i>
</div>
<div>
<div><strong>{{activity.user}}</strong> {{activity.action}}</div>
<div class="activity-time">{{activity.time}}</div>
</div>
</div>
</div>
</div>
<!-- Other Tabs -->
<div v-else>
<div class="card">
<p>{{getTabLabel(activeTab)}} content goes here</p>
</div>
</div>
</main>
<!-- Right Sidebar -->
<aside class="right-sidebar" :class="{ active: rightSidebarActive }">
<h2>Notifications</h2>
<div v-for="notification in notifications" :key="notification.id" class="notification-item">
<div class="notification-icon">
<i :class="notification.icon"></i>
</div>
<div>
<div>{{notification.message}}</div>
<div class="activity-time">{{notification.time}}</div>
</div>
</div>
<div v-if="notifications.length === 0" style="text-align: center; padding: var(--size-4);">
<p style="color: var(--gray-6);">No new notifications</p>
</div>
</aside>
</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const {createApp, ref, computed} = Vue;
createApp({
setup() {
const activeTab = ref('dashboard');
const leftSidebarActive = ref(false);
const rightSidebarActive = ref(false);
const stats = ref({
users: 1245,
orders: 48,
products: 326
});
const recentActivities = ref([
{
id: 1,
user: 'Jane Smith',
action: 'placed a new order (#1234)',
time: '10 minutes ago',
icon: 'fas fa-shopping-cart'
},
{
id: 2,
user: 'Mike Johnson',
action: 'updated product details',
time: '25 minutes ago',
icon: 'fas fa-box'
},
{
id: 3,
user: 'Sarah Williams',
action: 'registered as a new user',
time: '1 hour ago',
icon: 'fas fa-user-plus'
}
]);
const notifications = ref([
{
id: 1,
message: 'New user registered',
time: '15 minutes ago',
icon: 'fas fa-user-plus'
},
{
id: 2,
message: 'Order #1234 completed',
time: '1 hour ago',
icon: 'fas fa-check-circle'
},
{
id: 3,
message: 'System maintenance scheduled',
time: '3 hours ago',
icon: 'fas fa-tools'
}
]);
const getTabLabel = (tab) => {
const labels = {
dashboard: 'Dashboard Overview',
analytics: 'Analytics',
users: 'User Management',
orders: 'Order Management'
};
return labels[tab] || tab.charAt(0).toUpperCase() + tab.slice(1);
};
const showMobileBackdrop = computed(() => {
return leftSidebarActive.value || rightSidebarActive.value;
});
const toggleLeftSidebar = () => {
leftSidebarActive.value = !leftSidebarActive.value;
if (leftSidebarActive.value) {
rightSidebarActive.value = false;
}
};
const toggleRightSidebar = () => {
rightSidebarActive.value = !rightSidebarActive.value;
if (rightSidebarActive.value) {
leftSidebarActive.value = false;
}
};
const closeMobileSidebars = () => {
leftSidebarActive.value = false;
rightSidebarActive.value = false;
};
const setActiveTab = (tab) => {
activeTab.value = tab;
closeMobileSidebars();
};
return {
activeTab,
leftSidebarActive,
rightSidebarActive,
showMobileBackdrop,
stats,
recentActivities,
notifications,
getTabLabel,
toggleLeftSidebar,
toggleRightSidebar,
closeMobileSidebars,
setActiveTab
};
}
}).mount('#app');
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment