Skip to content

Commit 936dbb4

Browse files
authored
feat: add dark/light theme toggle (#26)
1 parent f494c8e commit 936dbb4

File tree

5 files changed

+164
-54
lines changed

5 files changed

+164
-54
lines changed

app/static/script.js

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,25 @@ const SQL_EXAMPLES = {
8282

8383
// Initialize the application
8484
document.addEventListener('DOMContentLoaded', function() {
85+
const themeToggle = document.getElementById('theme-toggle');
86+
const htmlElement = document.documentElement;
87+
88+
// This function applies the theme and saves it to the browser's memory
89+
const applyTheme = (theme) => {
90+
htmlElement.setAttribute('data-theme', theme);
91+
localStorage.setItem('theme', theme);
92+
};
93+
94+
// When the page loads, immediately apply the saved theme or default to 'light'
95+
const savedTheme = localStorage.getItem('theme') || 'light';
96+
applyTheme(savedTheme);
97+
98+
// This is the main click event that switches the theme
99+
themeToggle.addEventListener('click', () => {
100+
const currentTheme = htmlElement.getAttribute('data-theme');
101+
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
102+
applyTheme(newTheme);
103+
});
85104
setupEventListeners();
86105
initializeCommandDropdowns();
87106
showNicknameModal();
@@ -591,38 +610,21 @@ function switchTab(tabName) {
591610
}
592611

593612
function initializeDashboard() {
594-
// Initialize CDC Activity Chart
595613
const ctx = document.getElementById('cdcActivityChart');
596614
if (ctx) {
615+
// Check the current theme to set the correct colors for the chart
616+
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
617+
const gridColor = isDarkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)';
618+
const textColor = isDarkMode ? '#f9fafb' : '#64748b';
619+
597620
cdcActivityChart = new Chart(ctx, {
598621
type: 'line',
599622
data: {
600623
labels: [],
601624
datasets: [
602-
{
603-
label: 'Create',
604-
data: [],
605-
borderColor: '#10b981',
606-
backgroundColor: 'rgba(16, 185, 129, 0.1)',
607-
tension: 0.4,
608-
fill: true
609-
},
610-
{
611-
label: 'Update',
612-
data: [],
613-
borderColor: '#f59e0b',
614-
backgroundColor: 'rgba(245, 158, 11, 0.1)',
615-
tension: 0.4,
616-
fill: true
617-
},
618-
{
619-
label: 'Delete',
620-
data: [],
621-
borderColor: '#ef4444',
622-
backgroundColor: 'rgba(239, 68, 68, 0.1)',
623-
tension: 0.4,
624-
fill: true
625-
}
625+
{ label: 'Create', data: [], borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', tension: 0.4, fill: true },
626+
{ label: 'Update', data: [], borderColor: '#f59e0b', backgroundColor: 'rgba(245, 158, 11, 0.1)', tension: 0.4, fill: true },
627+
{ label: 'Delete', data: [], borderColor: '#ef4444', backgroundColor: 'rgba(239, 68, 68, 0.1)', tension: 0.4, fill: true }
626628
]
627629
},
628630
options: {
@@ -631,14 +633,18 @@ function initializeDashboard() {
631633
plugins: {
632634
legend: {
633635
position: 'bottom',
636+
labels: { color: textColor } // Use theme-aware text color
634637
}
635638
},
636639
scales: {
637640
y: {
638641
beginAtZero: true,
639-
ticks: {
640-
stepSize: 1
641-
}
642+
ticks: { stepSize: 1, color: textColor }, // Use theme-aware text color
643+
grid: { color: gridColor } // Use theme-aware grid color
644+
},
645+
x: {
646+
ticks: { color: textColor }, // Use theme-aware text color
647+
grid: { color: gridColor } // Use theme-aware grid color
642648
}
643649
}
644650
}

app/static/styles.css

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,62 @@
1212
--danger-color: #ef4444;
1313
--warning-color: #f59e0b;
1414
--success-color: #10b981;
15+
16+
/* Light Theme Colors */
1517
--background: #f8fafc;
1618
--surface: #ffffff;
1719
--text-primary: #1e293b;
1820
--text-secondary: #64748b;
1921
--border: #e2e8f0;
22+
--header-bg: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
23+
--header-text: #ffffff;
24+
--welcome-bg: linear-gradient(135deg, #f0f9ff, #e0f2fe);
25+
--welcome-border: #bae6fd;
26+
--welcome-text: #0369a1;
27+
--cdc-bg: linear-gradient(135deg, #f8fafc, #f1f5f9);
28+
--create-label-bg: #dcfce7;
29+
--create-label-text: #166534;
30+
--update-label-bg: #fef3c7;
31+
--update-label-text: #92400e;
32+
--delete-label-bg: #fee2e2;
33+
--delete-label-text: #991b1b;
2034
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
2135
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
2236
--radius: 0.5rem;
2337
--radius-lg: 1rem;
38+
--theme-transition-duration: 0.3s;
39+
}
40+
41+
[data-theme="dark"] {
42+
/* Dark Theme Colors */
43+
--background: #111827;
44+
--surface: #1f2937;
45+
--text-primary: #f9fafb;
46+
--text-secondary: #9ca3af;
47+
--border: #374151;
48+
--header-bg: #1f2937;
49+
--header-text: #f9fafb;
50+
--welcome-bg: linear-gradient(135deg, #1e2937, #111827);
51+
--welcome-border: #374151;
52+
--welcome-text: #9ca3af;
53+
--cdc-bg: linear-gradient(135deg, #111827, #1f2937);
54+
--create-label-bg: #052e16;
55+
--create-label-text: #bbf7d0;
56+
--update-label-bg: #422006;
57+
--update-label-text: #fde68a;
58+
--delete-label-bg: #450a0a;
59+
--delete-label-text: #fecaca;
60+
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.3);
61+
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3);
2462
}
2563

2664
body {
2765
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
28-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
2966
color: var(--text-primary);
3067
min-height: 100vh;
3168
line-height: 1.6;
69+
background: var(--background);
70+
transition: background-color var(--theme-transition-duration) ease, color var(--theme-transition-duration) ease;
3271
}
3372

3473
/* App Container */
@@ -38,13 +77,15 @@ body {
3877
min-height: 100vh;
3978
width: 100%;
4079
background: var(--surface);
80+
transition: background-color var(--theme-transition-duration) ease;
4181
}
4282

4383
/* Header */
4484
.header {
45-
background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
46-
color: white;
85+
color: var(--header-text);
4786
box-shadow: var(--shadow);
87+
background: var(--header-bg);
88+
transition: background var(--theme-transition-duration) ease;
4889
}
4990

5091
.header-content {
@@ -55,6 +96,12 @@ body {
5596
padding: 1rem 2rem;
5697
}
5798

99+
.header-right {
100+
display: flex;
101+
align-items: center;
102+
gap: 1.5rem;
103+
}
104+
58105
.logo {
59106
display: flex;
60107
align-items: center;
@@ -280,7 +327,7 @@ body {
280327

281328
.message-item.cdc {
282329
border-left: 4px solid var(--primary-color);
283-
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
330+
background: var(--cdc-bg);
284331
}
285332

286333
@keyframes slideIn {
@@ -300,6 +347,7 @@ body {
300347
line-height: 1.5;
301348
white-space: pre-wrap;
302349
word-break: break-word;
350+
color: var(--text-primary);
303351
}
304352

305353
.message-meta {
@@ -323,18 +371,18 @@ body {
323371
}
324372

325373
.message-type.create {
326-
background: #dcfce7;
327-
color: #166534;
374+
background: var(--create-label-bg);
375+
color: var(--create-label-text);
328376
}
329377

330378
.message-type.update {
331-
background: #fef3c7;
332-
color: #92400e;
379+
background: var(--update-label-bg);
380+
color: var(--update-label-text);
333381
}
334382

335383
.message-type.delete {
336-
background: #fee2e2;
337-
color: #991b1b;
384+
background: var(--delete-label-bg);
385+
color: var(--delete-label-text);
338386
}
339387

340388
/* Input Container */
@@ -362,6 +410,7 @@ body {
362410
font-size: 0.875rem;
363411
transition: all 0.2s;
364412
background: var(--surface);
413+
color: var(--text-primary);
365414
}
366415

367416
.input-group input:focus {
@@ -566,6 +615,7 @@ body {
566615
cursor: pointer;
567616
transition: all 0.2s ease;
568617
text-align: left;
618+
color: var(--text-primary);
569619
flex: 1;
570620
}
571621

@@ -1184,6 +1234,7 @@ body {
11841234
font-size: 1rem;
11851235
transition: all 0.2s;
11861236
background: var(--surface);
1237+
color: var(--text-primary);
11871238
}
11881239

11891240
.form-group input:focus {
@@ -1571,3 +1622,34 @@ body {
15711622
min-width: 0;
15721623
min-height: 0;
15731624
}
1625+
.header-right {
1626+
display: flex;
1627+
align-items: center;
1628+
gap: 1.5rem;
1629+
}
1630+
1631+
/* Styles for the new button */
1632+
.theme-toggle-button {
1633+
background: none;
1634+
border: none;
1635+
cursor: pointer;
1636+
padding: 5px;
1637+
border-radius: 50%;
1638+
display: flex;
1639+
align-items: center;
1640+
justify-content: center;
1641+
color: var(--header-text);
1642+
transition: background-color 0.2s ease, transform 0.2s ease;
1643+
}
1644+
1645+
.theme-toggle-button:hover {
1646+
background-color: rgba(255, 255, 255, 0.1);
1647+
transform: scale(1.1);
1648+
}
1649+
1650+
.theme-toggle-button:active {
1651+
transform: scale(0.95);
1652+
}
1653+
#messageText, #nickname-input {
1654+
color: var(--text-primary);
1655+
}

app/templates/index.html

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!DOCTYPE html>
2-
<html lang="en">
2+
<html lang="en" data-theme="light">
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -54,18 +54,25 @@ <h2>Choose Your Nickname</h2>
5454
<i class="fas fa-database"></i>
5555
<h1>Debezium Real-Time Chat</h1>
5656
</div>
57-
<div class="user-info">
58-
<div class="connection-status">
59-
<span class="status-indicator" id="connection-status"></span>
60-
<span id="connection-text">Connecting...</span>
61-
</div>
62-
<div class="user-id">
63-
<i class="fas fa-user"></i>
64-
<span id="nickname-display">Guest</span>
65-
<button id="edit-nickname-btn" class="edit-nickname-btn" onclick="editNickname()" title="Edit Nickname">
66-
<i class="fas fa-edit"></i>
67-
</button>
57+
58+
<div class="header-right">
59+
<div class="user-info">
60+
<div class="connection-status">
61+
<span class="status-indicator" id="connection-status"></span>
62+
<span id="connection-text">Connecting...</span>
63+
</div>
64+
<div class="user-id">
65+
<i class="fas fa-user"></i>
66+
<span id="nickname-display">Guest</span>
67+
<button id="edit-nickname-btn" class="edit-nickname-btn" onclick="editNickname()" title="Edit Nickname">
68+
<i class="fas fa-edit"></i>
69+
</button>
70+
</div>
6871
</div>
72+
73+
<button id="theme-toggle" class="theme-toggle-button" aria-label="Toggle dark mode">
74+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
75+
</button>
6976
</div>
7077
</div>
7178

docker-compose.yaml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ services:
1010
networks:
1111
- debezium-compose-network
1212
depends_on:
13-
- connect-debezium
14-
- zookeeper-debezium
15-
- kafka-debezium
16-
- tester_db
13+
kafka-debezium:
14+
condition: service_healthy
15+
connect-debezium:
16+
condition: service_started
17+
zookeeper-debezium:
18+
condition: service_started
19+
tester_db:
20+
condition: service_started
1721

1822
zookeeper-debezium:
1923
image: debezium/zookeeper:1.4
@@ -38,6 +42,11 @@ services:
3842
- ZOOKEEPER_CONNECT=zookeeper-debezium:2181
3943
networks:
4044
- debezium-compose-network
45+
healthcheck:
46+
test: ["CMD-SHELL", "ps aux | grep -v grep | grep Kafka"]
47+
interval: 15s
48+
timeout: 10s
49+
retries: 10
4150

4251
connect-debezium:
4352
image: debezium/connect:1.4

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)