Create Restaurant Dashboard using HTML, TailwindCSS, and JavaScript

Faraz

By Faraz -

Learn to create a restaurant dashboard using HTML, TailwindCSS, and JavaScript. Step-by-step guide for beginners to design a responsive UI.


create-restaurant-dashboard-using-html-tailwind-css-and-javascript.webp

Table of Contents

  1. Project Introduction
  2. HTML Code
  3. CSS Code
  4. JavaScript Code
  5. Conclusion
  6. Preview

A restaurant dashboard helps manage menus, orders, customers, and more. In this blog, we will show you how to create a restaurant dashboard UI using HTML, TailwindCSS, and JavaScript. This guide is perfect for beginners and developers who want to design clean and responsive dashboards for restaurants or cafes.

What You Need Before Starting:

  • Basic understanding of HTML structure
  • Familiarity with TailwindCSS classes
  • Some experience in JavaScript (for interactivity)
  • A code editor like VS Code
  • A browser (like Chrome) for preview

Source Code

Step 1 (HTML Code):

To get started, we will first need to create a basic HTML file. In this file, we will include the main structure for our dashboard. Below is a section-wise breakdown of the code:

<head> Section:

Contains metadata, styles, and scripts:

  • <!DOCTYPE html> – Defines the document as HTML5.
  • <meta charset="UTF-8"> – Sets the character encoding to UTF-8.
  • <meta name="viewport"...> – Makes the site responsive.
  • <title> – Sets the browser tab title.
  • Tailwind CSS (via CDN) – For utility-first styling.
  • Google Fonts – Loads the "Inter" font.
  • Chart.js – For charts and analytics.
  • Lucide.js – For clean, customizable SVG icons.
  • styles.css – External stylesheet for custom styles.

<body class="bg-slate-100 text-slate-800">:

  • Sets the page background and default text color using Tailwind classes.

Sidebar <aside>:

A vertical sidebar navigation menu:

  • Fixed to the left (fixed inset-y-0 left-0).
  • Contains:
    • Logo area (with a chef hat icon and title).
    • Navigation menu with links like Dashboard, Orders, Reports, Menu, Reservations, Customers, Delivery, Billing.
    • Settings link.
    • Collapse sidebar button (only shown on large screens).
  • Each menu item includes:
    • A Lucide icon (data-lucide="...").
    • A label text.
    • Hover effects using Tailwind.

Sidebar Overlay <div id="sidebarOverlay">:

A semi-transparent black background that appears when the sidebar is open on small screens.

Main Content <div id="mainContent">:

Contains the header and main dashboard content.

Header:

A sticky top bar with:

  • Hamburger menu (for mobile toggle).
  • Search bar.
  • System status badge (e.g., "System Online").
  • Notification bell with alert dot.
  • User profile button with dropdown options:
    • View profile
    • Settings
    • Logout

Main Section <main>:

  • Title: "Dashboard".
  • Responsive grid layout using Tailwind:
  • First card example: “Today's Orders” showing 156 with icon and styling.
  • More cards follow, like Sales, Customers, Revenue, etc.

Technologies Used:

  • Tailwind CSS: For responsive, utility-based styling.
  • Lucide: SVG icons.
  • Chart.js: (though not yet implemented here) for visualizing data.
  • Responsive design: Mobile-friendly sidebar and topbar.

Step 2 (CSS Code):

Once the basic HTML structure of the dashboard is in place, the next step is to add styling to the dashboard using CSS. Here's a breakdown of what each part does:

1. Global Styling

body {
    font-family: 'Inter', sans-serif;
    overflow-x: hidden;
}
  • font-family: Sets the main font of the site to Inter, with a fallback to sans-serif.
  • overflow-x: hidden: Prevents the page from scrolling horizontally. This avoids unwanted horizontal scrollbars, especially during modal animations.

2. Custom Scrollbar Styling (for WebKit-based browsers like Chrome, Safari)

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}
  • Sets the scrollbar’s width and height to 8px (thin scrollbar).
::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 10px;
}
  • Styles the scrollbar track (background of the scrollbar) with a light gray color and rounded corners.
::-webkit-scrollbar-thumb {
    background: #cbd5e1;
    border-radius: 10px;
}
  • Styles the scrollbar handle (the draggable part) with a medium gray and rounded corners.
::-webkit-scrollbar-thumb:hover {
    background: #94a3b8;
}
  • Changes the scrollbar handle color to darker gray when hovering.

3. Sidebar Animation

#sidebar {
    transition: width 0.3s ease-in-out, transform 0.3s ease-in-out;
}
  • Adds smooth animation to the sidebar’s width and transform properties.
#mainContent {
    transition: margin-left 0.3s ease-in-out;
}
  • Animates the main content's left margin, allowing smooth shifts when the sidebar is toggled.

4. Sidebar Navigation Scrollbar Hidden

.sidebar-nav-items::-webkit-scrollbar {
    display: none;
}
  • Hides the scrollbar specifically for .sidebar-nav-items in WebKit browsers.
.sidebar-nav-items {
    -ms-overflow-style: none; /* IE */
    scrollbar-width: none;     /* Firefox */
}
  • Hides the scrollbar in Internet Explorer and Firefox too.

5. Profile Dropdown Transition

#profileDropdown {
    transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}
  • Animates fade and movement of the profile dropdown for a smooth dropdown effect.

6. Icon Styling

.menu-item-icon,
.modal-icon {
    width: 1.5rem;
    height: 1.5rem;
    stroke-width: 1.5;
}
  • Applies size and stroke thickness to icons used in menus and modals.
.stat-card-icon-container {
    padding: 0.75rem;
    border-radius: 9999px; /* Fully rounded */
}
  • Adds padding and circular shape to the stat icon container.
.stat-card-icon {
    width: 1.75rem;
    height: 1.75rem;
}
  • Sets a larger size for stat card icons.
.activity-icon-container {
    padding: 0.5rem;
    border-radius: 9999px;
}

.activity-icon {
    width: 1.25rem;
    height: 1.25rem;
}
  • Styles for activity icons — smaller than stat icons, but also fully rounded.

7. Modal Styling

.modal-backdrop {
    transition: opacity 0.3s ease-in-out;
}
  • Smooth transition when modal backdrop fades in or out.
.modal-content {
    transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
}
  • Smooth fade and slide animation for modal content.
body {
    font-family: 'Inter', sans-serif;
    overflow-x: hidden;
    /* Prevent horizontal scrollbar if modals cause overflow during transition */
}

/* Custom scrollbar */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 10px;
}

::-webkit-scrollbar-thumb {
    background: #cbd5e1;
    border-radius: 10px;
}

::-webkit-scrollbar-thumb:hover {
    background: #94a3b8;
}

/* Sidebar transitions */
#sidebar {
    transition: width 0.3s ease-in-out, transform 0.3s ease-in-out;
}

#mainContent {
    transition: margin-left 0.3s ease-in-out;
}

.sidebar-nav-items::-webkit-scrollbar {
    display: none;
}

.sidebar-nav-items {
    -ms-overflow-style: none;
    scrollbar-width: none;
}

/* Profile dropdown */
#profileDropdown {
    transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}

/* Icon styling */
.menu-item-icon,
.modal-icon {
    width: 1.5rem;
    height: 1.5rem;
    stroke-width: 1.5;
}

.stat-card-icon-container {
    padding: 0.75rem;
    border-radius: 9999px;
}

.stat-card-icon {
    width: 1.75rem;
    height: 1.75rem;
}

.activity-icon-container {
    padding: 0.5rem;
    border-radius: 9999px;
}

.activity-icon {
    width: 1.25rem;
    height: 1.25rem;
}

/* Modal Styling */
.modal-backdrop {
    transition: opacity 0.3s ease-in-out;
}

.modal-content {
    transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
} 

Step 3 (JavaScript Code):

Finally, we need to create a function in JavaScript. Here's a breakdown of what each part does:

1. DOM Element Selection

At the top, the code uses document.getElementById() and querySelectorAll() to select various HTML elements:

  • Sidebar-related elements (sidebar, mainContent, sidebarToggle, etc.)
  • Modal-related elements (like newOrderModal, addMenuItemModal)
  • Profile dropdown and logout button
  • Forms within modals

2. Sidebar Constants

These are CSS class names used to expand/collapse the sidebar and shift main content accordingly:

  • w-64: wide sidebar
  • w-20: narrow sidebar
  • lg:ml-64: push main content right when sidebar is wide
  • -translate-x-full, translate-x-0: used for hiding/showing mobile sidebar with animations

3. Sidebar State Management

  • Uses localStorage to remember sidebar state (expanded or collapsed) even after page reload.
  • applySidebarState() sets the sidebar to expanded or collapsed:
    • Shows/hides text/icons accordingly.
    • Updates mainContent's left margin.
    • Re-renders icons using lucide.createIcons().
  • toggleDesktopSidebar() toggles sidebar state on desktop and saves it to localStorage.

4. Mobile Sidebar Functions

  • openMobileSidebar(): slides in the sidebar, shows overlay.
  • closeMobileSidebar(): slides out sidebar, hides overlay.

5. Profile Dropdown Toggle

  • toggleProfileDropdown() toggles visibility and animation classes for a user profile dropdown menu.

6. Modal Open/Close Functions

  • openModal(modalElement): fades in modal with scale animation.
  • closeModal(modalElement): fades it out and then hides it.

Used for:

  • New Order
  • Add Menu Item
  • New Reservation

7. Event Listeners

Attached to buttons and actions:

  • Sidebar toggle, mobile menu, overlay click
  • Profile button click
  • Logout (placeholder – just shows an alert)
  • Click outside the profile menu closes it
  • Modal open/close buttons for each modal
  • Form submit handlers print form data to the console and close the modal
  • Escape key closes open modals

8. Responsive Initialization

  • initializeLayout() checks the screen size:
    • On mobile, hides sidebar and removes large margins
    • On desktop, applies sidebar state from localStorage
    • Calls lucide.createIcons() to render icons correctly
// DOM Elements
const sidebar = document.getElementById('sidebar');
const mainContent = document.getElementById('mainContent');
const sidebarToggle = document.getElementById('sidebarToggle');
const mobileMenuButton = document.getElementById('mobileMenuButton');
const sidebarOverlay = document.getElementById('sidebarOverlay');
const menuTexts = document.querySelectorAll('.menu-text');
const sidebarLogoText = document.getElementById('sidebarLogoText');
const toggleIconOpen = document.getElementById('toggleIconOpen');
const toggleIconClosed = document.getElementById('toggleIconClosed');
const profileButton = document.getElementById('profileButton');
const profileDropdown = document.getElementById('profileDropdown');
const logoutButton = document.getElementById('logoutButton');

// Modal Elements
const newOrderModal = document.getElementById('newOrderModal');
const addMenuItemModal = document.getElementById('addMenuItemModal');
const newReservationModal = document.getElementById('newReservationModal');

const openNewOrderModalButton = document.getElementById('openNewOrderModal');
const openAddMenuItemModalButton = document.getElementById('openAddMenuItemModal');
const openNewReservationModalButton = document.getElementById('openNewReservationModal');

const closeNewOrderModalButton = document.getElementById('closeNewOrderModal');
const closeAddMenuItemModalButton = document.getElementById('closeAddMenuItemModal');
const closeNewReservationModalButton = document.getElementById('closeNewReservationModal');

const cancelNewOrderButton = document.getElementById('cancelNewOrder');
const cancelAddMenuItemButton = document.getElementById('cancelAddMenuItem');
const cancelNewReservationButton = document.getElementById('cancelNewReservation');

const newOrderForm = document.getElementById('newOrderForm');
const addMenuItemForm = document.getElementById('addMenuItemForm');
const newReservationForm = document.getElementById('newReservationForm');


// Sidebar Constants
const EXPANDED_WIDTH_CLASS = 'w-64';
const COLLAPSED_WIDTH_CLASS = 'w-20';
const MAIN_CONTENT_MARGIN_EXPANDED_CLASS = 'lg:ml-64';
const MAIN_CONTENT_MARGIN_COLLAPSED_CLASS = 'lg:ml-20';
const SIDEBAR_MOBILE_HIDDEN_CLASS = '-translate-x-full';
const SIDEBAR_MOBILE_VISIBLE_CLASS = 'translate-x-0';

let isSidebarExpanded = localStorage.getItem('sidebarState') !== 'collapsed';

function applySidebarState(expanded, isMobile = false) {
    if (expanded) {
        sidebar.classList.remove(COLLAPSED_WIDTH_CLASS);
        sidebar.classList.add(EXPANDED_WIDTH_CLASS);
        if (!isMobile) {
            mainContent.classList.remove(MAIN_CONTENT_MARGIN_COLLAPSED_CLASS);
            mainContent.classList.add(MAIN_CONTENT_MARGIN_EXPANDED_CLASS);
        }
        menuTexts.forEach(text => {
            text.classList.remove('hidden');
            if (text.parentElement.id === 'sidebarToggle') text.textContent = "Collapse";
        });
        if (sidebarLogoText) sidebarLogoText.classList.remove('hidden');
        if (toggleIconOpen) toggleIconOpen.classList.remove('hidden');
        if (toggleIconClosed) toggleIconClosed.classList.add('hidden');
    } else {
        sidebar.classList.remove(EXPANDED_WIDTH_CLASS);
        sidebar.classList.add(COLLAPSED_WIDTH_CLASS);
        if (!isMobile) {
            mainContent.classList.remove(MAIN_CONTENT_MARGIN_EXPANDED_CLASS);
            mainContent.classList.add(MAIN_CONTENT_MARGIN_COLLAPSED_CLASS);
        }
        menuTexts.forEach(text => {
            text.classList.add('hidden');
            if (text.parentElement.id === 'sidebarToggle') {
                text.classList.remove('hidden'); text.textContent = "";
            }
        });
        if (sidebarLogoText) sidebarLogoText.classList.add('hidden');
        if (toggleIconOpen) toggleIconOpen.classList.add('hidden');
        if (toggleIconClosed) toggleIconClosed.classList.remove('hidden');
    }
    lucide.createIcons();
}

function toggleDesktopSidebar() {
    isSidebarExpanded = !isSidebarExpanded;
    applySidebarState(isSidebarExpanded);
    localStorage.setItem('sidebarState', isSidebarExpanded ? 'expanded' : 'collapsed');
}

function openMobileSidebar() {
    sidebar.classList.remove(SIDEBAR_MOBILE_HIDDEN_CLASS);
    sidebar.classList.add(SIDEBAR_MOBILE_VISIBLE_CLASS);
    sidebarOverlay.classList.remove('hidden');
    applySidebarState(true, true);
}

function closeMobileSidebar() {
    sidebar.classList.remove(SIDEBAR_MOBILE_VISIBLE_CLASS);
    sidebar.classList.add(SIDEBAR_MOBILE_HIDDEN_CLASS);
    sidebarOverlay.classList.add('hidden');
}

let isProfileDropdownOpen = false;
function toggleProfileDropdown() {
    isProfileDropdownOpen = !isProfileDropdownOpen;
    if (isProfileDropdownOpen) {
        profileDropdown.classList.remove('opacity-0', 'scale-95', '-translate-y-2', 'pointer-events-none');
        profileDropdown.classList.add('opacity-100', 'scale-100', 'translate-y-0', 'pointer-events-auto');
    } else {
        profileDropdown.classList.add('opacity-0', 'scale-95', '-translate-y-2', 'pointer-events-none');
        profileDropdown.classList.remove('opacity-100', 'scale-100', 'translate-y-0', 'pointer-events-auto');
    }
}

// --- Modal Control Functions ---
function openModal(modalElement) {
    if (!modalElement) return;
    modalElement.classList.remove('hidden');
    // Timeout to allow display:flex to apply before starting transition
    setTimeout(() => {
        modalElement.classList.remove('opacity-0');
        modalElement.querySelector('.modal-content').classList.remove('scale-95', 'opacity-0');
        modalElement.querySelector('.modal-content').classList.add('scale-100', 'opacity-100');
        lucide.createIcons(); // Re-render icons if any are in the modal
    }, 10);
}

function closeModal(modalElement) {
    if (!modalElement) return;
    modalElement.classList.add('opacity-0');
    modalElement.querySelector('.modal-content').classList.add('scale-95', 'opacity-0');
    modalElement.querySelector('.modal-content').classList.remove('scale-100', 'opacity-100');
    // Wait for transition to finish before hiding
    setTimeout(() => {
        modalElement.classList.add('hidden');
    }, 300); // Match transition duration
}

// --- Event Listeners ---
if (sidebarToggle) sidebarToggle.addEventListener('click', (e) => { e.preventDefault(); toggleDesktopSidebar(); });
if (mobileMenuButton) mobileMenuButton.addEventListener('click', (e) => { e.preventDefault(); openMobileSidebar(); });
if (sidebarOverlay) sidebarOverlay.addEventListener('click', closeMobileSidebar);
if (profileButton) profileButton.addEventListener('click', (e) => { e.stopPropagation(); toggleProfileDropdown(); });

if (logoutButton) {
    logoutButton.addEventListener('click', (e) => {
        e.preventDefault();
        console.log('Logout clicked');
        alert('Logout functionality to be implemented.');
        isProfileDropdownOpen = false;
        profileDropdown.classList.add('opacity-0', 'scale-95', '-translate-y-2', 'pointer-events-none');
        profileDropdown.classList.remove('opacity-100', 'scale-100', 'translate-y-0', 'pointer-events-auto');
    });
}

document.addEventListener('click', (e) => {
    if (isProfileDropdownOpen && !profileDropdown.contains(e.target) && !profileButton.contains(e.target)) {
        toggleProfileDropdown();
    }
});

// Modal Event Listeners
if (openNewOrderModalButton) openNewOrderModalButton.addEventListener('click', () => openModal(newOrderModal));
if (openAddMenuItemModalButton) openAddMenuItemModalButton.addEventListener('click', () => openModal(addMenuItemModal));
if (openNewReservationModalButton) openNewReservationModalButton.addEventListener('click', () => openModal(newReservationModal));

if (closeNewOrderModalButton) closeNewOrderModalButton.addEventListener('click', () => closeModal(newOrderModal));
if (closeAddMenuItemModalButton) closeAddMenuItemModalButton.addEventListener('click', () => closeModal(addMenuItemModal));
if (closeNewReservationModalButton) closeNewReservationModalButton.addEventListener('click', () => closeModal(newReservationModal));

if (cancelNewOrderButton) cancelNewOrderButton.addEventListener('click', () => closeModal(newOrderModal));
if (cancelAddMenuItemButton) cancelAddMenuItemButton.addEventListener('click', () => closeModal(addMenuItemModal));
if (cancelNewReservationButton) cancelNewReservationButton.addEventListener('click', () => closeModal(newReservationModal));

// Modal Form Submissions (Placeholder)
if (newOrderForm) newOrderForm.addEventListener('submit', (e) => {
    e.preventDefault();
    console.log('New Order Form Submitted:', Object.fromEntries(new FormData(e.target)));
    // Add actual save logic here
    closeModal(newOrderModal);
    e.target.reset(); // Reset form fields
});
if (addMenuItemForm) addMenuItemForm.addEventListener('submit', (e) => {
    e.preventDefault();
    console.log('Add Menu Item Form Submitted:', Object.fromEntries(new FormData(e.target)));
    closeModal(addMenuItemModal);
    e.target.reset();
});
if (newReservationForm) newReservationForm.addEventListener('submit', (e) => {
    e.preventDefault();
    console.log('New Reservation Form Submitted:', Object.fromEntries(new FormData(e.target)));
    closeModal(newReservationModal);
    e.target.reset();
});

// Close modals with Escape key
document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') {
        if (!newOrderModal.classList.contains('hidden')) closeModal(newOrderModal);
        if (!addMenuItemModal.classList.contains('hidden')) closeModal(addMenuItemModal);
        if (!newReservationModal.classList.contains('hidden')) closeModal(newReservationModal);
    }
});


function initializeLayout() {
    const isMobile = window.innerWidth < 1024;
    if (isMobile) {
        sidebar.classList.add(SIDEBAR_MOBILE_HIDDEN_CLASS);
        sidebar.classList.remove(SIDEBAR_MOBILE_VISIBLE_CLASS);
        mainContent.classList.remove(MAIN_CONTENT_MARGIN_EXPANDED_CLASS, MAIN_CONTENT_MARGIN_COLLAPSED_CLASS);
        mainContent.classList.add('ml-0');
        applySidebarState(true, true);
        sidebar.classList.add(SIDEBAR_MOBILE_HIDDEN_CLASS);
        if (sidebarToggle) sidebarToggle.classList.add('hidden');
    } else {
        sidebar.classList.remove(SIDEBAR_MOBILE_HIDDEN_CLASS, SIDEBAR_MOBILE_VISIBLE_CLASS);
        applySidebarState(isSidebarExpanded, false);
        if (sidebarToggle) sidebarToggle.classList.remove('hidden');
    }
    lucide.createIcons();
}

window.addEventListener('resize', initializeLayout);
document.addEventListener('DOMContentLoaded', () => {
    initializeLayout();

    const salesCtx = document.getElementById('salesChart')?.getContext('2d');
    if (salesCtx) {
        new Chart(salesCtx, {
            type: 'bar',
            data: {
                labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
                datasets: [{
                    label: 'Sales ($)',
                    data: [1250, 1980, 3100, 4800, 2400, 3200, 4650],
                    backgroundColor: 'rgba(59, 130, 246, 0.6)',
                    borderColor: 'rgba(59, 130, 246, 1)',
                    borderWidth: 1, borderRadius: 6, barThickness: 'flex', maxBarThickness: 30
                }]
            },
            options: {
                responsive: true, maintainAspectRatio: false,
                scales: { y: { beginAtZero: true, grid: { color: '#e5e7eb' } }, x: { grid: { display: false } } },
                plugins: { legend: { display: false } }
            }
        });
    }

    const popularDishesCtx = document.getElementById('popularDishesChart')?.getContext('2d');
    if (popularDishesCtx) {
        new Chart(popularDishesCtx, {
            type: 'doughnut',
            data: {
                labels: ['Margherita Pizza', 'Caesar Salad', 'Pasta Carbonara', 'Steak Frites', 'Tiramisu', 'Other'],
                datasets: [{
                    label: 'Orders', data: [120, 95, 88, 70, 65, 40],
                    backgroundColor: [
                        'rgba(239, 68, 68, 0.7)', 'rgba(59, 130, 246, 0.7)', 'rgba(245, 158, 11, 0.7)',
                        'rgba(16, 185, 129, 0.7)', 'rgba(139, 92, 246, 0.7)', 'rgba(107, 114, 128, 0.7)'
                    ],
                    borderColor: [
                        'rgba(239, 68, 68, 1)', 'rgba(59, 130, 246, 1)', 'rgba(245, 158, 11, 1)',
                        'rgba(16, 185, 129, 1)', 'rgba(139, 92, 246, 1)', 'rgba(107, 114, 128, 1)'
                    ],
                    borderWidth: 1
                }]
            },
            options: {
                responsive: true, maintainAspectRatio: false,
                plugins: { legend: { position: 'bottom', labels: { padding: 15, font: { size: 10 } } } }
            }
        });
    }
});

Final Output:

create-restaurant-dashboard-using-html-tailwind-css-and-javascript.gif

Conclusion:

Creating a restaurant dashboard using HTML, TailwindCSS, and JavaScript is simple when you follow a step-by-step method. This project helps improve your frontend skills and is great for portfolios or real-world projects. You can keep adding features like search, filters, tables, and even connect it to a database later.

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Thanks!
Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox


Latest Post

Please allow ads on our site🥺