
Building a Responsive Lesson Viewer with HTML, CSS, JavaScript, and Tailwind CSS
In this tutorial, we'll create a responsive Lesson Viewer that can display course content in different formats (text, video, PDF). This component is ideal for e-learning platforms, online courses, or any educational application.
1. Project Overview
Our Lesson Viewer will have:
-
A sidebar with course modules and lessons.
-
A main content area that displays:
-
Text content (with syntax highlighting)
-
Embedded videos (YouTube)
-
PDF documents (using PDF.js)
-
Mobile responsiveness (collapsible sidebar on small screens).
-
Progress tracking and navigation controls.
2. Setting Up the Project
We'll use:
-
HTML5 for structure.
-
Tailwind CSS for styling.
-
JavaScript for interactivity.
-
PDF.js for rendering PDFs.
Basic HTML Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lesson Viewer</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">
<!-- PDF.js for PDF rendering -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>
</head>
<body class="bg-gray-50 font-sans">
<!-- Our Lesson Viewer will go here -->
</body>
</html>
3. Building the HTML Structure
We'll divide the layout into:
-
A sidebar (for course navigation).
-
A main content area (for lesson display).
-
A footer (for navigation controls).
Sidebar Structure
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="sidebar bg-white w-64 border-r border-gray-200 flex flex-col">
<div class="p-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-800">Course Content</h2>
<p class="text-sm text-gray-500">Introduction to Web Development</p>
</div>
<div class="flex-1 overflow-y-auto">
<div class="space-y-1 p-2">
<!-- Modules and lessons will go here -->
</div>
</div>
<div class="p-4 border-t border-gray-200">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-gray-700">Progress</span>
<span class="text-sm text-gray-500">25%</span>
</div>
<div class="progress-bar rounded-full">
<div class="progress-fill rounded-full" style="width: 25%"></div>
</div>
</div>
</div>
<!-- Main Content Area -->
<div class="content-area flex-1 flex flex-col overflow-hidden">
<!-- Header -->
<header class="bg-white border-b border-gray-200 flex items-center justify-between p-4">
<button id="sidebar-toggle" class="md:hidden text-gray-500 focus:outline-none">
<i class="fas fa-bars text-xl"></i>
</button>
<h1 class="text-xl font-semibold text-gray-800">Introduction to HTML</h1>
<div class="flex items-center space-x-4">
<button class="text-gray-500 hover:text-gray-700 focus:outline-none">
<i class="far fa-bookmark text-xl"></i>
</button>
<button class="text-gray-500 hover:text-gray-700 focus:outline-none">
<i class="fas fa-ellipsis-v text-xl"></i>
</button>
</div>
</header>
<!-- Lesson Content -->
<main class="flex-1 overflow-y-auto p-4 md:p-6 bg-white">
<!-- Text, Video, and PDF content will go here -->
</main>
<!-- Footer Navigation -->
<footer class="bg-white border-t border-gray-200 p-4">
<div class="flex justify-between items-center max-w-4xl mx-auto">
<button class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-50 flex items-center disabled:opacity-50" disabled>
<i class="fas fa-arrow-left mr-2"></i> Previous
</button>
<div class="text-sm text-gray-500">
Lesson 1 of 12
</div>
<button class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 flex items-center">
Next <i class="fas fa-arrow-right ml-2"></i>
</button>
</div>
</footer>
</div>
</div>
4. Styling with Tailwind CSS
We'll enhance the UI with:
-
Responsive behavior (collapsible sidebar on mobile).
-
Interactive elements (hover effects, active states).
-
Custom CSS for PDF rendering.
Custom CSS (Inside <style>
Tag)
.content-area {
transition: all 0.3s ease;
}
.sidebar {
transition: transform 0.3s ease;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
position: fixed;
z-index: 50;
height: 100vh;
top: 0;
}
.sidebar.open {
transform: translateX(0);
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 40;
}
.overlay.open {
display: block;
}
}
#pdf-viewer {
width: 100%;
height: 70vh;
border: 1px solid #e2e8f0;
overflow-y: auto;
}
.lesson-item:hover {
background-color: #f8fafc;
}
.lesson-item.active {
background-color: #e2e8f0;
border-left: 4px solid #4f46e5;
}
.progress-bar {
height: 6px;
background-color: #e2e8f0;
}
.progress-fill {
height: 100%;
background-color: #4f46e5;
transition: width 0.3s ease;
}
5. Adding JavaScript Functionality
We'll implement:
-
Mobile sidebar toggle.
-
Lesson switching (text, video, PDF).
-
PDF rendering with PDF.js.
JavaScript Code
// Initialize PDF.js
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
document.addEventListener('DOMContentLoaded', function() {
// Mobile sidebar toggle
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebar = document.querySelector('.sidebar');
const overlay = document.querySelector('.overlay');
sidebarToggle.addEventListener('click', function() {
sidebar.classList.toggle('open');
overlay.classList.toggle('open');
});
overlay.addEventListener('click', function() {
sidebar.classList.remove('open');
overlay.classList.remove('open');
});
// Lesson item click handler
const lessonItems = document.querySelectorAll('.lesson-item');
lessonItems.forEach(item => {
item.addEventListener('click', function() {
// Remove active class from all items
lessonItems.forEach(i => i.classList.remove('active'));
// Add active class to clicked item
this.classList.add('active');
// Update header title
const title = this.querySelector('span').textContent;
document.querySelector('header h1').textContent = title;
// Determine content type and show appropriate content
const icon = this.querySelector('i');
if (icon.classList.contains('fa-file-alt')) {
showTextContent();
} else if (icon.classList.contains('fa-play-circle')) {
showVideoContent();
} else if (icon.classList.contains('fa-file-pdf')) {
showPdfContent();
}
});
});
// PDF navigation
const prevPageBtn = document.getElementById('prev-page');
const nextPageBtn = document.getElementById('next-page');
const pageInfo = document.getElementById('page-info');
let pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null;
function showTextContent() {
document.getElementById('text-content').classList.remove('hidden');
document.getElementById('video-content').classList.add('hidden');
document.getElementById('pdf-content').classList.add('hidden');
}
function showVideoContent() {
document.getElementById('text-content').classList.add('hidden');
document.getElementById('video-content').classList.remove('hidden');
document.getElementById('pdf-content').classList.add('hidden');
}
function showPdfContent() {
document.getElementById('text-content').classList.add('hidden');
document.getElementById('video-content').classList.add('hidden');
document.getElementById('pdf-content').classList.remove('hidden');
// Load a sample PDF (replace with your PDF URL)
const url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf';
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
pageInfo.textContent = `Page 1 of ${pdfDoc.numPages}`;
// Render the first page
renderPage(1);
});
}
function renderPage(num) {
pageRendering = true;
pdfDoc.getPage(num).then(function(page) {
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then(function() {
pageRendering = false;
const pdfViewer = document.getElementById('pdf-viewer');
pdfViewer.innerHTML = '';
pdfViewer.appendChild(canvas);
if (pageNumPending !== null) {
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
pageInfo.textContent = `Page ${num} of ${pdfDoc.numPages}`;
}
function onPrevPage() {
if (pageNum <= 1) return;
pageNum--;
queueRenderPage(pageNum);
}
function onNextPage() {
if (pageNum >= pdfDoc.numPages) return;
pageNum++;
queueRenderPage(pageNum);
}
prevPageBtn.addEventListener('click', onPrevPage);
nextPageBtn.addEventListener('click', onNextPage);
// Initially show text content
showTextContent();
});
6. Testing and Final Touches
-
Test on different devices (mobile, tablet, desktop).
-
Ensure PDF rendering works (adjust scale
if needed).
-
Add real course content (replace sample data).