How to Build a Beautiful Course Dashboard UI with Progress Bars
Live Preview |
In this tutorial, I'll walk you through creating an elegant, responsive course dashboard UI that displays enrolled courses with progress bars. This interface is perfect for e-learning platforms, online course systems, or any educational application where users need to track their learning progress.
What We're Building
Our course dashboard will include:
-
A statistics overview showing enrolled courses, completed courses, and average progress
-
A grid of course cards with visual progress indicators
-
Filtering and search functionality
-
Responsive design that works on all devices
-
Smooth animations and hover effects
Step 1: Set Up the HTML Structure
First, let's create the basic HTML structure with Tailwind CSS:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Course Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Content will go here -->
</div>
</body>
</html>
We're including:
-
Tailwind CSS via CDN
-
Font Awesome for icons
-
A basic responsive container
Step 2: Add the Header Section
<!-- Header -->
<header class="mb-8">
<h1 class="text-3xl font-bold text-gray-800">My Learning Dashboard</h1>
<p class="text-gray-600">Track your course progress and continue learning</p>
</header>
This creates a simple header with a title and subtitle.
Step 3: Create Statistics Cards
Let's add three statistics cards showing key metrics:
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<!-- Enrolled Courses Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
<i class="fas fa-book-open text-xl"></i>
</div>
<div>
<p class="text-gray-500">Enrolled Courses</p>
<h3 class="text-2xl font-bold" id="enrolled-count">0</h3>
</div>
</div>
</div>
<!-- Completed Courses Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-green-100 text-green-600 mr-4">
<i class="fas fa-check-circle text-xl"></i>
</div>
<div>
<p class="text-gray-500">Completed</p>
<h3 class="text-2xl font-bold" id="completed-count">0</h3>
</div>
</div>
</div>
<!-- Average Progress Card -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-purple-100 text-purple-600 mr-4">
<i class="fas fa-chart-line text-xl"></i>
</div>
<div>
<p class="text-gray-500">Avg. Progress</p>
<h3 class="text-2xl font-bold" id="avg-progress">0%</h3>
</div>
</div>
</div>
</div>
Key features:
-
Responsive grid (1 column on mobile, 3 on desktop)
-
Each card has an icon, label, and value
-
We'll update the values dynamically with JavaScript later
Step 4: Add Filter Controls
<!-- Filter Controls -->
<div class="flex flex-col sm:flex-row justify-between items-center mb-6 gap-4">
<!-- Search Input -->
<div class="relative w-full sm:w-64">
<input type="text" placeholder="Search courses..."
class="w-full pl-10 pr-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
<!-- Filter Dropdowns -->
<div class="flex gap-2 w-full sm:w-auto">
<select class="px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option>All Categories</option>
<option>Web Development</option>
<option>Data Science</option>
<option>Design</option>
<option>Business</option>
</select>
<select class="px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option>Sort by</option>
<option>Progress</option>
<option>Recently Enrolled</option>
<option>Course Name</option>
</select>
</div>
</div>
This adds:
-
A search input with icon
-
Category filter dropdown
-
Sort options dropdown
-
Responsive layout that stacks on mobile
Step 5: Create the Courses Grid
<!-- Courses Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" id="courses-container">
<!-- Course cards will be dynamically inserted here -->
</div>
<!-- Empty State -->
<div id="empty-state" class="hidden text-center py-12">
<div class="mx-auto w-24 h-24 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<i class="fas fa-book text-3xl text-gray-400"></i>
</div>
<h3 class="text-xl font-medium text-gray-700 mb-2">No courses enrolled yet</h3>
<p class="text-gray-500 mb-6">Browse our catalog and start your learning journey today!</p>
<button class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">Explore Courses</button>
</div>
The grid will:
-
Show 1 column on mobile, 2 on tablet, 3 on desktop
-
Include an empty state that shows when no courses exist
Step 6: Add Custom CSS for Animations
Add this inside the <head>
section:
<style>
.progress-bar {
transition: width 0.5s ease-in-out;
}
.course-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
}
.course-card {
transition: all 0.3s ease;
}
</style>
These styles will:
-
Animate the progress bars when they load
-
Add hover effects to course cards
Step 7: JavaScript Implementation
Now let's add the JavaScript to make it dynamic:
<script>
// Sample course data
const courses = [
{
id: 1,
title: "Advanced JavaScript Concepts",
category: "Web Development",
instructor: "Sarah Johnson",
thumbnail: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&q=80",
progress: 75,
lastAccessed: "2 days ago",
totalLessons: 24,
completedLessons: 18
},
// Add more courses as needed...
];
// DOM elements
const coursesContainer = document.getElementById('courses-container');
const enrolledCount = document.getElementById('enrolled-count');
const completedCount = document.getElementById('completed-count');
const avgProgress = document.getElementById('avg-progress');
const emptyState = document.getElementById('empty-state');
// Render courses
function renderCourses() {
if (courses.length === 0) {
emptyState.classList.remove('hidden');
coursesContainer.classList.add('hidden');
} else {
emptyState.classList.add('hidden');
coursesContainer.classList.remove('hidden');
coursesContainer.innerHTML = '';
courses.forEach(course => {
const progressColor = getProgressColor(course.progress);
const isCompleted = course.progress === 100;
const courseCard = document.createElement('div');
courseCard.className = 'course-card bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg';
courseCard.innerHTML = `
<div class="relative">
<img src="${course.thumbnail}" alt="${course.title}" class="w-full h-48 object-cover">
${isCompleted ?
`<div class="absolute top-2 right-2 bg-green-500 text-white text-xs font-bold px-2 py-1 rounded-full flex items-center">
<i class="fas fa-check mr-1"></i> Completed
</div>` : ''}
</div>
<div class="p-6">
<div class="flex justify-between items-start mb-2">
<span class="inline-block bg-${progressColor}-100 text-${progressColor}-800 text-xs px-2 py-1 rounded-full">
${course.category}
</span>
<span class="text-sm text-gray-500">${course.lastAccessed}</span>
</div>
<h3 class="text-xl font-bold text-gray-800 mb-2">${course.title}</h3>
<p class="text-gray-600 text-sm mb-4">Instructor: ${course.instructor}</p>
<div class="mb-3">
<div class="flex justify-between text-sm text-gray-600 mb-1">
<span>Progress: ${course.progress}%</span>
<span>${course.completedLessons}/${course.totalLessons} lessons</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="progress-bar h-2.5 rounded-full bg-${progressColor}-600" style="width: ${course.progress}%"></div>
</div>
</div>
<div class="flex justify-between mt-4">
<button class="text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center">
<i class="fas fa-play-circle mr-1"></i> Continue
</button>
<button class="text-sm text-gray-600 hover:text-gray-800 font-medium flex items-center">
<i class="fas fa-info-circle mr-1"></i> Details
</button>
</div>
</div>
`;
coursesContainer.appendChild(courseCard);
});
updateStats();
}
}
// Get appropriate color based on progress
function getProgressColor(progress) {
if (progress === 100) return 'green';
if (progress >= 70) return 'blue';
if (progress >= 40) return 'purple';
return 'yellow';
}
// Update statistics
function updateStats() {
enrolledCount.textContent = courses.length;
const completedCourses = courses.filter(course => course.progress === 100).length;
completedCount.textContent = completedCourses;
const totalProgress = courses.reduce((sum, course) => sum + course.progress, 0);
const averageProgress = Math.round(totalProgress / courses.length);
avgProgress.textContent = `${averageProgress}%`;
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderCourses();
// Simulate loading animation for progress bars
setTimeout(() => {
document.querySelectorAll('.progress-bar').forEach(bar => {
bar.style.transition = 'width 0.7s ease-in-out';
});
}, 100);
});
</script>
Key JavaScript Functions
-
renderCourses():
-
Checks if there are courses to display
-
Creates HTML for each course card
-
Applies appropriate styling based on progress
-
Handles the empty state
-
-
getProgressColor():
-
Returns different Tailwind color classes based on progress percentage
-
100% = green, 70-99% = blue, 40-69% = purple, <40% = yellow
-
-
updateStats():
-
Calculates and displays:
-
Total enrolled courses
-
Number of completed courses
-
Average progress across all courses
-
-
Step 8: Final Touches
The complete implementation includes smooth animations:
-
Progress bars animate when the page loads
-
Course cards have hover effects
-
All transitions are CSS-based for performance