Skip to content

Commit bb954a2

Browse files
jsb2092claude
andcommitted
Add dark mode to quiz-taking interface
- Add Tailwind dark mode config with system preference detection - Style all quiz views (access, start, quiz, results) with dark variants - Update dynamically generated elements (nav buttons, answer options, results) - Fix JS syntax error in sendAllInvitations confirm message 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 602c646 commit bb954a2

2 files changed

Lines changed: 62 additions & 50 deletions

File tree

questionbank/static/js/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4173,7 +4173,7 @@
41734173

41744174
async function sendAllInvitations() {
41754175
if (!currentRosterQuizId) return;
4176-
if (!confirm('Send quiz invitation emails to all students who haven\\'t received one yet?')) return;
4176+
if (!confirm("Send quiz invitation emails to all students who haven't received one yet?")) return;
41774177

41784178
try {
41794179
const data = await api(`quizzes/sessions/${currentRosterQuizId}/invitations/send/`, 'POST', {});

questionbank/templates/quiz_take.html

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
<!DOCTYPE html>
2-
<html lang="en">
2+
<html lang="en" class="dark">
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Quiz</title>
77
<script src="https://cdn.tailwindcss.com"></script>
8+
<script>
9+
tailwind.config = {
10+
darkMode: 'class',
11+
}
12+
// Check for dark mode preference
13+
if (localStorage.getItem('theme') === 'dark' ||
14+
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
15+
document.documentElement.classList.add('dark');
16+
} else {
17+
document.documentElement.classList.remove('dark');
18+
}
19+
</script>
820
<script src="https://unpkg.com/lucide@latest"></script>
921
<style>
1022
.timer-warning { animation: pulse 1s infinite; }
@@ -14,98 +26,98 @@
1426
}
1527
</style>
1628
</head>
17-
<body class="bg-gray-100 min-h-screen">
29+
<body class="bg-gray-100 dark:bg-slate-900 min-h-screen transition-colors">
1830
<!-- Quiz Container -->
1931
<div id="quiz-app" class="max-w-4xl mx-auto p-4">
2032

2133
<!-- Access Code Entry -->
22-
<div id="access-view" class="bg-white rounded-lg shadow-lg p-8 mt-20">
23-
<h1 class="text-2xl font-bold text-center mb-6">Enter Quiz Access Code</h1>
34+
<div id="access-view" class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-8 mt-20">
35+
<h1 class="text-2xl font-bold text-center mb-6 text-gray-900 dark:text-white">Enter Quiz Access Code</h1>
2436
<div class="max-w-sm mx-auto">
2537
<input type="text" id="access-code"
26-
class="w-full text-center text-3xl font-mono tracking-widest border-2 border-gray-300 rounded-lg p-4 uppercase"
38+
class="w-full text-center text-3xl font-mono tracking-widest border-2 border-gray-300 dark:border-slate-600 dark:bg-slate-700 dark:text-white rounded-lg p-4 uppercase"
2739
placeholder="ABC123" maxlength="10" autocomplete="off">
2840
<button onclick="checkAccessCode()"
2941
class="w-full mt-4 bg-sky-600 text-white py-3 rounded-lg font-medium hover:bg-sky-700">
3042
Continue
3143
</button>
32-
<p id="access-error" class="text-red-500 text-center mt-2 hidden"></p>
44+
<p id="access-error" class="text-red-500 dark:text-red-400 text-center mt-2 hidden"></p>
3345
</div>
3446
</div>
3547

3648
<!-- Student Info & Start -->
37-
<div id="start-view" class="bg-white rounded-lg shadow-lg p-8 hidden">
38-
<h1 id="quiz-title" class="text-2xl font-bold mb-2">Quiz</h1>
39-
<p id="quiz-description" class="text-gray-600 mb-6"></p>
49+
<div id="start-view" class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-8 hidden">
50+
<h1 id="quiz-title" class="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Quiz</h1>
51+
<p id="quiz-description" class="text-gray-600 dark:text-slate-400 mb-6"></p>
4052

41-
<div class="bg-gray-50 rounded-lg p-4 mb-6">
42-
<h3 class="font-medium mb-2">Quiz Details</h3>
43-
<ul class="text-sm text-gray-600 space-y-1">
53+
<div class="bg-gray-50 dark:bg-slate-700/50 rounded-lg p-4 mb-6">
54+
<h3 class="font-medium mb-2 text-gray-900 dark:text-white">Quiz Details</h3>
55+
<ul class="text-sm text-gray-600 dark:text-slate-400 space-y-1">
4456
<li><i data-lucide="clock" class="w-4 h-4 inline mr-1"></i> Time Limit: <span id="time-limit">60</span> minutes</li>
4557
<li><i data-lucide="list" class="w-4 h-4 inline mr-1"></i> Questions: <span id="question-count">-</span></li>
4658
</ul>
4759
</div>
4860

49-
<div id="quiz-instructions" class="bg-blue-50 text-blue-800 rounded-lg p-4 mb-6 hidden">
61+
<div id="quiz-instructions" class="bg-blue-50 dark:bg-blue-900/30 text-blue-800 dark:text-blue-300 rounded-lg p-4 mb-6 hidden">
5062
<h3 class="font-medium mb-2">Instructions</h3>
5163
<div id="instructions-text"></div>
5264
</div>
5365

5466
<form id="student-form" class="space-y-4">
5567
<div>
56-
<label class="block text-sm font-medium text-gray-700 mb-1">Your Name *</label>
68+
<label class="block text-sm font-medium text-gray-700 dark:text-slate-300 mb-1">Your Name *</label>
5769
<input type="text" id="student-name" required
58-
class="w-full border border-gray-300 rounded-lg px-4 py-2">
70+
class="w-full border border-gray-300 dark:border-slate-600 dark:bg-slate-700 dark:text-white rounded-lg px-4 py-2">
5971
</div>
6072
<div id="student-id-field">
61-
<label class="block text-sm font-medium text-gray-700 mb-1">Student ID *</label>
73+
<label class="block text-sm font-medium text-gray-700 dark:text-slate-300 mb-1">Student ID *</label>
6274
<input type="text" id="student-id"
63-
class="w-full border border-gray-300 rounded-lg px-4 py-2">
75+
class="w-full border border-gray-300 dark:border-slate-600 dark:bg-slate-700 dark:text-white rounded-lg px-4 py-2">
6476
</div>
6577
<div>
66-
<label class="block text-sm font-medium text-gray-700 mb-1">Email (optional)</label>
78+
<label class="block text-sm font-medium text-gray-700 dark:text-slate-300 mb-1">Email (optional)</label>
6779
<input type="email" id="student-email"
68-
class="w-full border border-gray-300 rounded-lg px-4 py-2">
80+
class="w-full border border-gray-300 dark:border-slate-600 dark:bg-slate-700 dark:text-white rounded-lg px-4 py-2">
6981
</div>
7082
<button type="submit"
7183
class="w-full bg-green-600 text-white py-3 rounded-lg font-medium hover:bg-green-700">
7284
Start Quiz
7385
</button>
7486
</form>
75-
<p id="start-error" class="text-red-500 text-center mt-2 hidden"></p>
87+
<p id="start-error" class="text-red-500 dark:text-red-400 text-center mt-2 hidden"></p>
7688
</div>
7789

7890
<!-- Quiz Taking -->
7991
<div id="quiz-view" class="hidden">
8092
<!-- Timer Header -->
81-
<div class="bg-white rounded-lg shadow-lg p-4 mb-4 sticky top-0 z-10 flex justify-between items-center">
93+
<div class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-4 mb-4 sticky top-0 z-10 flex justify-between items-center">
8294
<div>
83-
<h1 id="quiz-header-title" class="font-bold"></h1>
84-
<p class="text-sm text-gray-500">Question <span id="current-q">1</span> of <span id="total-q">0</span></p>
95+
<h1 id="quiz-header-title" class="font-bold text-gray-900 dark:text-white"></h1>
96+
<p class="text-sm text-gray-500 dark:text-slate-400">Question <span id="current-q">1</span> of <span id="total-q">0</span></p>
8597
</div>
86-
<div id="timer" class="text-2xl font-mono font-bold text-gray-900">
98+
<div id="timer" class="text-2xl font-mono font-bold text-gray-900 dark:text-white">
8799
<i data-lucide="clock" class="w-5 h-5 inline mr-1"></i>
88100
<span id="timer-display">60:00</span>
89101
</div>
90102
</div>
91103

92104
<!-- Question Navigation -->
93-
<div class="bg-white rounded-lg shadow-lg p-4 mb-4">
105+
<div class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-4 mb-4">
94106
<div id="question-nav" class="flex flex-wrap gap-2">
95107
<!-- Question number buttons generated dynamically -->
96108
</div>
97109
</div>
98110

99111
<!-- Question Display -->
100-
<div id="question-container" class="bg-white rounded-lg shadow-lg p-6 mb-4">
101-
<div id="question-text" class="text-lg mb-6"></div>
112+
<div id="question-container" class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-6 mb-4">
113+
<div id="question-text" class="text-lg mb-6 text-gray-900 dark:text-white"></div>
102114
<div id="answer-area"></div>
103115
</div>
104116

105117
<!-- Navigation Buttons -->
106118
<div class="flex justify-between">
107119
<button onclick="prevQuestion()" id="prev-btn"
108-
class="bg-gray-200 text-gray-700 px-6 py-2 rounded-lg hover:bg-gray-300 disabled:opacity-50">
120+
class="bg-gray-200 dark:bg-slate-700 text-gray-700 dark:text-slate-200 px-6 py-2 rounded-lg hover:bg-gray-300 dark:hover:bg-slate-600 disabled:opacity-50">
109121
Previous
110122
</button>
111123
<button onclick="nextQuestion()" id="next-btn"
@@ -120,24 +132,24 @@ <h1 id="quiz-header-title" class="font-bold"></h1>
120132
</div>
121133

122134
<!-- Results View -->
123-
<div id="results-view" class="bg-white rounded-lg shadow-lg p-8 hidden">
135+
<div id="results-view" class="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-8 hidden">
124136
<div class="text-center">
125137
<i data-lucide="check-circle" class="w-16 h-16 mx-auto text-green-500 mb-4"></i>
126-
<h1 class="text-2xl font-bold mb-2">Quiz Submitted!</h1>
127-
<p id="results-name" class="text-gray-600 mb-6"></p>
138+
<h1 class="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Quiz Submitted!</h1>
139+
<p id="results-name" class="text-gray-600 dark:text-slate-400 mb-6"></p>
128140
</div>
129141

130-
<div id="score-display" class="bg-gray-50 rounded-lg p-6 text-center mb-6">
131-
<div class="text-4xl font-bold text-sky-600" id="score-percentage"></div>
132-
<div class="text-gray-500" id="score-points"></div>
133-
<div id="grading-pending-message" class="hidden text-amber-600">
142+
<div id="score-display" class="bg-gray-50 dark:bg-slate-700/50 rounded-lg p-6 text-center mb-6">
143+
<div class="text-4xl font-bold text-sky-600 dark:text-sky-400" id="score-percentage"></div>
144+
<div class="text-gray-500 dark:text-slate-400" id="score-points"></div>
145+
<div id="grading-pending-message" class="hidden text-amber-600 dark:text-amber-400">
134146
<i data-lucide="clock" class="w-6 h-6 inline-block mr-2"></i>
135147
<span>Your submission is being graded. Check back later for your score.</span>
136148
</div>
137149
</div>
138150

139151
<div id="results-details" class="hidden">
140-
<h3 class="font-medium mb-4">Your Answers</h3>
152+
<h3 class="font-medium mb-4 text-gray-900 dark:text-white">Your Answers</h3>
141153
<div id="results-list" class="space-y-4">
142154
<!-- Results will be populated here -->
143155
</div>
@@ -278,7 +290,7 @@ <h3 class="font-medium mb-4">Your Answers</h3>
278290
const nav = document.getElementById('question-nav');
279291
nav.innerHTML = questions.map((q, i) => `
280292
<button onclick="showQuestion(${i})" id="nav-${i}"
281-
class="w-10 h-10 rounded-lg font-medium ${i === 0 ? 'bg-sky-600 text-white' : 'bg-gray-200 text-gray-700'} hover:bg-sky-500 hover:text-white">
293+
class="w-10 h-10 rounded-lg font-medium ${i === 0 ? 'bg-sky-600 text-white' : 'bg-gray-200 dark:bg-slate-600 text-gray-700 dark:text-slate-200'} hover:bg-sky-500 hover:text-white">
282294
${i + 1}
283295
</button>
284296
`).join('');
@@ -296,7 +308,7 @@ <h3 class="font-medium mb-4">Your Answers</h3>
296308
btn.className = `w-10 h-10 rounded-lg font-medium ${
297309
i === index ? 'bg-sky-600 text-white' :
298310
answers[q.id] ? 'bg-green-500 text-white' :
299-
'bg-gray-200 text-gray-700'
311+
'bg-gray-200 dark:bg-slate-600 text-gray-700 dark:text-slate-200'
300312
} hover:bg-sky-500 hover:text-white`;
301313
});
302314

@@ -307,30 +319,30 @@ <h3 class="font-medium mb-4">Your Answers</h3>
307319
if (q.question_type === 'multipleChoice') {
308320
const choices = q.answer_data.choices || [];
309321
area.innerHTML = choices.map((choice, i) => `
310-
<label class="flex items-center p-4 border rounded-lg mb-2 cursor-pointer hover:bg-gray-50 ${savedAnswer.selected === choice ? 'border-sky-500 bg-sky-50' : ''}">
322+
<label class="flex items-center p-4 border dark:border-slate-600 rounded-lg mb-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-slate-700 ${savedAnswer.selected === choice ? 'border-sky-500 dark:border-sky-400 bg-sky-50 dark:bg-sky-900/30' : 'bg-white dark:bg-slate-800'}">
311323
<input type="radio" name="mc" value="${escapeHtml(choice)}"
312324
${savedAnswer.selected === choice ? 'checked' : ''}
313325
onchange="saveAnswer('${q.id}', {selected: this.value})"
314326
class="w-5 h-5 text-sky-600">
315-
<span class="ml-3">${formatText(choice)}</span>
327+
<span class="ml-3 text-gray-900 dark:text-white">${formatText(choice)}</span>
316328
</label>
317329
`).join('');
318330
} else if (q.question_type === 'trueFalse') {
319331
area.innerHTML = ['True', 'False'].map(choice => `
320-
<label class="flex items-center p-4 border rounded-lg mb-2 cursor-pointer hover:bg-gray-50 ${String(savedAnswer.selected) === choice.toLowerCase() ? 'border-sky-500 bg-sky-50' : ''}">
332+
<label class="flex items-center p-4 border dark:border-slate-600 rounded-lg mb-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-slate-700 ${String(savedAnswer.selected) === choice.toLowerCase() ? 'border-sky-500 dark:border-sky-400 bg-sky-50 dark:bg-sky-900/30' : 'bg-white dark:bg-slate-800'}">
321333
<input type="radio" name="tf" value="${choice.toLowerCase()}"
322334
${String(savedAnswer.selected) === choice.toLowerCase() ? 'checked' : ''}
323335
onchange="saveAnswer('${q.id}', {selected: this.value === 'true'})"
324336
class="w-5 h-5 text-sky-600">
325-
<span class="ml-3 font-medium">${choice}</span>
337+
<span class="ml-3 font-medium text-gray-900 dark:text-white">${choice}</span>
326338
</label>
327339
`).join('');
328340
} else {
329341
// Short or long answer
330342
const rows = q.question_type === 'longAnswer' ? 8 : 3;
331343
area.innerHTML = `
332344
<textarea id="text-answer" rows="${rows}"
333-
class="w-full border border-gray-300 rounded-lg p-4 focus:ring-2 focus:ring-sky-500"
345+
class="w-full border border-gray-300 dark:border-slate-600 dark:bg-slate-700 dark:text-white rounded-lg p-4 focus:ring-2 focus:ring-sky-500"
334346
placeholder="Enter your answer..."
335347
onblur="saveAnswer('${q.id}', {text: this.value})">${savedAnswer.text || ''}</textarea>
336348
`;
@@ -477,13 +489,13 @@ <h3 class="font-medium mb-4">Your Answers</h3>
477489
document.getElementById('results-details').classList.remove('hidden');
478490
const list = document.getElementById('results-list');
479491
list.innerHTML = data.responses.map(r => `
480-
<div class="border rounded-lg p-4 ${r.is_correct ? 'border-green-300 bg-green-50' : 'border-red-300 bg-red-50'}">
492+
<div class="border rounded-lg p-4 ${r.is_correct ? 'border-green-300 dark:border-green-700 bg-green-50 dark:bg-green-900/30' : 'border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/30'}">
481493
<div class="flex justify-between items-start mb-2">
482-
<span class="font-medium">Question ${r.question_number}</span>
483-
<span class="${r.is_correct ? 'text-green-600' : 'text-red-600'}">${r.points_earned}/${r.points_possible} pts</span>
494+
<span class="font-medium text-gray-900 dark:text-white">Question ${r.question_number}</span>
495+
<span class="${r.is_correct ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}">${r.points_earned}/${r.points_possible} pts</span>
484496
</div>
485-
<div class="text-sm text-gray-700 mb-2">${formatText(r.question_text)}</div>
486-
${r.feedback ? `<div class="text-sm text-gray-600 mt-2"><strong>Feedback:</strong> ${r.feedback}</div>` : ''}
497+
<div class="text-sm text-gray-700 dark:text-slate-300 mb-2">${formatText(r.question_text)}</div>
498+
${r.feedback ? `<div class="text-sm text-gray-600 dark:text-slate-400 mt-2"><strong>Feedback:</strong> ${r.feedback}</div>` : ''}
487499
</div>
488500
`).join('');
489501
}
@@ -512,7 +524,7 @@ <h3 class="font-medium mb-4">Your Answers</h3>
512524
return text
513525
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
514526
.replace(/\*(.*?)\*/g, '<em>$1</em>')
515-
.replace(/`(.*?)`/g, '<code class="bg-gray-100 px-1 rounded">$1</code>')
527+
.replace(/`(.*?)`/g, '<code class="bg-gray-100 dark:bg-slate-700 px-1 rounded">$1</code>')
516528
.replace(/\n/g, '<br>');
517529
}
518530

0 commit comments

Comments
 (0)