Skip to content

Commit eafbad1

Browse files
Merge pull request #70 from TechMaestro-k/feat/quiz-ui-theme
feat(quiz): improve UI, add dark/light theme toggle, and better answe…
2 parents 8d4ded5 + f16067f commit eafbad1

3 files changed

Lines changed: 119 additions & 42 deletions

File tree

projects/quiz/index.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
<meta charset="utf-8">
66
<meta name="viewport" content="width=device-width,initial-scale=1">
77
<title>Quiz</title>
8-
<link rel="stylesheet" href="./styles.css">i
8+
<link rel="stylesheet" href="./styles.css">
99
</head>
1010

1111
<body>
1212
<main>
1313
<div class="quiz-header">
1414
<h1>Quiz</h1>
15-
<div class="timer">
16-
Time Left: <span id="time">15</span>s
15+
<div class="controls">
16+
<button id="theme-toggle" aria-pressed="false" title="Toggle theme">🌙</button>
17+
<div class="timer">
18+
Time Left: <span id="time">15</span>s
19+
</div>
1720
</div>
1821
</div>
1922
<div id="q"></div>

projects/quiz/main.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@ let i = 0, score = 0;
1010
const q = document.getElementById('q'),
1111
answers = document.getElementById('answers'),
1212
result = document.getElementById('result');
13+
const themeToggle = document.getElementById('theme-toggle');
14+
const root = document.documentElement;
15+
16+
// Safe localStorage helpers
17+
function safeGet(k){ try{ return localStorage.getItem(k) }catch(e){return null}}
18+
function safeSet(k,v){ try{ localStorage.setItem(k,v) }catch(e){}}
19+
20+
function applyTheme(t){
21+
const theme = t === 'light' ? 'light' : 'dark';
22+
root.setAttribute('data-theme', theme);
23+
if(themeToggle) themeToggle.textContent = theme === 'light' ? '☀️' : '🌙';
24+
}
25+
26+
themeToggle?.addEventListener('click', ()=>{
27+
const current = root.getAttribute('data-theme') || (safeGet('quiz-theme') || 'dark');
28+
const next = current === 'light' ? 'dark' : 'light';
29+
safeSet('quiz-theme', next);
30+
applyTheme(next);
31+
});
32+
33+
// initialize theme from storage or system preference
34+
const stored = safeGet('quiz-theme');
35+
const prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
36+
applyTheme(stored ? stored : (prefersLight ? 'light' : 'dark'));
1337

1438
/** Decode HTML entities from API */
1539
function decodeHTML(str) {
@@ -97,10 +121,30 @@ function render() {
97121
cur.a.forEach((ans, idx) => {
98122
const b = document.createElement('button');
99123
b.textContent = ans;
124+
b.className = 'answer-btn';
100125
b.addEventListener('click', () => {
126+
// prevent double clicks
127+
if (b.disabled) return;
101128
clearInterval(timerInterval);
102-
if (idx === cur.c) score++;
103-
handleNextQuestion();
129+
// mark selected
130+
Array.from(answers.children).forEach(x=>x.classList.remove('selected'));
131+
b.classList.add('selected');
132+
// mark correct/incorrect
133+
if (idx === cur.c){
134+
b.classList.add('correct');
135+
score++;
136+
} else {
137+
b.classList.add('incorrect');
138+
// reveal the correct one
139+
const correctBtn = answers.children[cur.c];
140+
if (correctBtn) correctBtn.classList.add('correct');
141+
}
142+
// disable all to avoid extra clicks
143+
Array.from(answers.children).forEach(x=>x.disabled=true);
144+
// short delay to show feedback
145+
setTimeout(()=>{
146+
handleNextQuestion();
147+
}, 700);
104148
});
105149
answers.appendChild(b);
106150
});

projects/quiz/styles.css

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,79 @@
1-
body {
2-
font-family: system-ui;
3-
background: #0f0f12;
4-
color: #eef1f8;
5-
margin: 0;
6-
padding: 2rem;
7-
display: grid;
8-
place-items: center
1+
:root{
2+
--bg: #0e1220; /* page background (dark) */
3+
--card: #0f1724; /* panel */
4+
--text: #e6eef8; /* main text */
5+
--muted: #98a0b3; /* secondary text */
6+
--primary: #60a5fa; /* primary accent */
7+
--accent: #7c3aed; /* accent */
8+
--success: #34d399;
9+
--danger: #fb7185;
10+
--btn-text-dark: #061320;
911
}
1012

11-
main {
12-
max-width: 560px;
13-
width: 100%
13+
/* Light theme overrides applied only when data-theme="light" is set on <html> */
14+
[data-theme="light"]{
15+
--bg: #f6f3ea; /* soft cream */
16+
--card: #ffffff;
17+
--text: #0b1a2b;
18+
--muted: #6b7280;
19+
--primary: #3b82f6;
20+
--accent: #7c3aed;
21+
--success: #10b981;
22+
--danger: #ef4444;
23+
--btn-text-dark: #06283a;
1424
}
1525

16-
.quiz-header {
17-
display: flex;
18-
justify-content: space-between; /* Pushes items to opposite ends */
19-
align-items: center; /* Vertically aligns them in the middle */
20-
margin-bottom: 1rem; /* Adds some space below the header */
26+
*{box-sizing:border-box}
27+
body{
28+
margin:0;
29+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
30+
background: radial-gradient(1200px 600px at 10% 10%, rgba(124,58,237,0.08), transparent), var(--bg);
31+
color:var(--text);
32+
padding:2rem;
33+
display:grid;
34+
place-items:center;
35+
min-height:100vh;
2136
}
2237

23-
button {
24-
background: #6ee7b7;
25-
color: #0b1020;
26-
border: none;
27-
padding: .5rem .75rem;
28-
border-radius: .5rem;
29-
font-weight: 600;
30-
cursor: pointer;
31-
margin: .25rem
38+
/* Light-mode only page background (kept separate so dark mode won't change) */
39+
[data-theme="light"] body{
40+
background: linear-gradient(180deg, rgba(246,243,234,1) 0%, rgba(237,245,255,1) 100%);
3241
}
3342

34-
.notes {
35-
color: #a6adbb;
36-
font-size: .9rem
43+
main{
44+
width:100%;
45+
max-width:720px;
46+
background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
47+
border-radius:14px;
48+
padding:1.5rem;
49+
box-shadow: 0 10px 30px rgba(2,6,23,0.6), inset 0 1px 0 rgba(255,255,255,0.02);
50+
border: 1px solid rgba(255,255,255,0.03);
3751
}
3852

39-
.timer {
40-
font-size: 1rem;
41-
font-weight: bold;
42-
text-align: center;
43-
color: #545454;
44-
}
53+
.quiz-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem}
54+
.controls{display:flex;gap:.5rem;align-items:center}
55+
56+
#q{font-size:1.125rem;line-height:1.4;margin:0 0 1rem 0}
57+
58+
#answers{display:flex;flex-direction:column;gap:.5rem}
59+
#answers button{display:block;text-align:left;padding:.85rem 1rem;border-radius:10px;border:1px solid rgba(255,255,255,0.04);background:linear-gradient(180deg, rgba(255,255,255,0.02), transparent);color:var(--text);font-weight:600;cursor:pointer}
60+
#answers button:hover{transform:translateY(-1px);box-shadow:0 6px 20px rgba(2,6,23,0.4)}
61+
#answers button:active{transform:translateY(0)}
62+
#answers button:disabled{opacity:.6;cursor:not-allowed}
63+
64+
/* Selected and feedback states */
65+
#answers button.selected{outline:3px solid rgba(96,165,250,0.14)}
66+
#answers button.correct{background:linear-gradient(90deg, rgba(34,197,94,0.12), rgba(16,185,129,0.06));border-color:rgba(16,185,129,0.28);}
67+
#answers button.incorrect{background:linear-gradient(90deg, rgba(251,113,133,0.06), rgba(239,68,68,0.03));border-color:rgba(239,68,68,0.18)}
68+
69+
.notes{color:var(--muted);font-size:.9rem;margin-top:1rem}
70+
71+
button#theme-toggle{background:transparent;border:1px solid rgba(255,255,255,0.04);padding:.45rem .6rem;border-radius:.5rem;font-size:1rem}
72+
73+
.timer{font-weight:700;color:var(--muted);font-size:.95rem}
74+
.timer.warning{color:var(--danger)}
4575

46-
/* Optional: Add a class for when time is running out */
47-
.timer.warning {
48-
color: #e86460; /* A reddish color */
76+
@media (max-width:520px){
77+
main{padding:1rem;border-radius:12px}
78+
#q{font-size:1rem}
4979
}

0 commit comments

Comments
 (0)