Skip to content

Commit 337ebff

Browse files
authored
feat: add light mode to dev portal (#2072)
## Summary Adds a light / dark mode toggle to the dev portal. <img width="1549" height="922" alt="Screenshot 2026-04-08 at 18 57 29" src="https://github.com/user-attachments/assets/e118d21b-6840-4db3-8309-a8af43ea698b" /> <img width="1549" height="922" alt="Screenshot 2026-04-08 at 18 57 24" src="https://github.com/user-attachments/assets/ff08c270-2ba8-4514-b46d-4b671957e8bf" />
1 parent 73b746c commit 337ebff

File tree

2 files changed

+130
-12
lines changed

2 files changed

+130
-12
lines changed

scripts/dev-portal/index.html

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,25 @@ <h1>
6666
</svg>
6767
HyperDX Dev Portal
6868
</h1>
69-
<div class="status">
70-
<div class="dot"></div>
71-
<span id="refresh-status">Auto-refreshing every 3s</span>
69+
70+
<div class="header-right">
71+
<div class="status">
72+
<div class="dot"></div>
73+
<span id="refresh-status">Auto-refreshing every 3s</span>
74+
</div>
75+
<button
76+
class="theme-toggle"
77+
id="theme-toggle"
78+
onclick="toggleTheme()"
79+
title="Toggle light/dark mode"
80+
>
81+
<svg id="theme-icon-sun" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
82+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v1m0 16v1m8.66-13.66l-.71.71M4.05 19.95l-.71.71M21 12h-1M4 12H3m16.66 7.66l-.71-.71M4.05 4.05l-.71-.71M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
83+
</svg>
84+
<svg id="theme-icon-moon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="display:none">
85+
<path stroke-linecap="round" stroke-linejoin="round" d="M21 12.79A9 9 0 1111.21 3a7 7 0 009.79 9.79z"/>
86+
</svg>
87+
</button>
7288
</div>
7389
</div>
7490

@@ -120,6 +136,46 @@ <h3>
120136
</div>
121137

122138
<script>
139+
// --- Theme ---
140+
function getPreferredTheme() {
141+
const stored = localStorage.getItem('hdx-dev-portal-theme');
142+
if (stored === 'light' || stored === 'dark') return stored;
143+
return window.matchMedia('(prefers-color-scheme: light)').matches
144+
? 'light'
145+
: 'dark';
146+
}
147+
148+
function applyTheme(theme) {
149+
document.documentElement.setAttribute('data-theme', theme);
150+
const sunIcon = document.getElementById('theme-icon-sun');
151+
const moonIcon = document.getElementById('theme-icon-moon');
152+
if (sunIcon && moonIcon) {
153+
// Sun icon shown in dark mode (click to go light), moon in light mode
154+
sunIcon.style.display = theme === 'dark' ? '' : 'none';
155+
moonIcon.style.display = theme === 'light' ? '' : 'none';
156+
}
157+
}
158+
159+
function toggleTheme() {
160+
const current =
161+
document.documentElement.getAttribute('data-theme') || 'dark';
162+
const next = current === 'dark' ? 'light' : 'dark';
163+
localStorage.setItem('hdx-dev-portal-theme', next);
164+
applyTheme(next);
165+
}
166+
167+
// Apply immediately to avoid flash
168+
applyTheme(getPreferredTheme());
169+
170+
// Listen for OS theme changes (only when no explicit preference saved)
171+
window
172+
.matchMedia('(prefers-color-scheme: light)')
173+
.addEventListener('change', () => {
174+
if (!localStorage.getItem('hdx-dev-portal-theme')) {
175+
applyTheme(getPreferredTheme());
176+
}
177+
});
178+
123179
const contentEl = document.getElementById('content');
124180
const historyEl = document.getElementById('history-content');
125181
const errorEl = document.getElementById('error-banner');

scripts/dev-portal/styles.css

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,36 @@
1515
--orange: #db6d28;
1616
--log-bg: #141517; /* dark-8 */
1717
--hover: #25262b; /* dark-6: color-bg-hover */
18+
--log-text: #c9d1d9;
19+
--row-hover: rgba(88, 166, 255, 0.05);
20+
--row-active: rgba(88, 166, 255, 0.1);
21+
--table-border: rgba(48, 54, 61, 0.5);
22+
--modal-backdrop: rgba(0, 0, 0, 0.6);
23+
--hover-overlay: rgba(255, 255, 255, 0.05);
24+
}
25+
26+
:root[data-theme='light'] {
27+
--bg: #ffffff;
28+
--card-bg: #f8f9fa;
29+
--card-surface: #f1f3f5;
30+
--border: #dee2e6;
31+
--border-emphasis: #ced4da;
32+
--text: #212529;
33+
--text-muted: #868e96;
34+
--accent: #12b886;
35+
--accent-hover: #0ca678;
36+
--green: #12b886;
37+
--red: #e03131;
38+
--yellow: #f08c00;
39+
--orange: #e8590c;
40+
--log-bg: #f8f9fa;
41+
--hover: #e9ecef;
42+
--log-text: #212529;
43+
--row-hover: rgba(0, 0, 0, 0.03);
44+
--row-active: rgba(0, 0, 0, 0.06);
45+
--table-border: rgba(0, 0, 0, 0.08);
46+
--modal-backdrop: rgba(0, 0, 0, 0.3);
47+
--hover-overlay: rgba(0, 0, 0, 0.05);
1848
}
1949

2050
* {
@@ -120,7 +150,7 @@ body {
120150
white-space: pre-wrap;
121151
word-break: break-all;
122152
background: var(--bg);
123-
color: #c9d1d9;
153+
color: var(--log-text);
124154
}
125155

126156
.log-content .log-line {
@@ -166,6 +196,12 @@ body {
166196
flex-shrink: 0;
167197
}
168198

199+
.header .header-right {
200+
display: flex;
201+
align-items: center;
202+
gap: 12px;
203+
}
204+
169205
.header .status {
170206
font-size: 13px;
171207
color: var(--text-muted);
@@ -329,7 +365,7 @@ body {
329365
.services-table td {
330366
padding: 10px 20px;
331367
font-size: 14px;
332-
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
368+
border-bottom: 1px solid var(--table-border);
333369
}
334370

335371
.services-table tr:last-child td {
@@ -342,11 +378,11 @@ body {
342378
}
343379

344380
.services-table tr.clickable:hover {
345-
background: rgba(88, 166, 255, 0.05);
381+
background: var(--row-hover);
346382
}
347383

348384
.services-table tr.active {
349-
background: rgba(88, 166, 255, 0.1);
385+
background: var(--row-active);
350386
}
351387

352388
.service-name {
@@ -517,7 +553,7 @@ body {
517553

518554
.history-toggle-btn:hover {
519555
color: var(--text);
520-
background: rgba(255, 255, 255, 0.05);
556+
background: var(--hover-overlay);
521557
}
522558

523559
.history-card-body {
@@ -543,7 +579,7 @@ body {
543579
justify-content: space-between;
544580
padding: 12px 20px;
545581
background: var(--card-surface);
546-
border-bottom: 1px solid rgba(48, 54, 61, 0.5);
582+
border-bottom: 1px solid var(--table-border);
547583
}
548584

549585
.history-entry-header .history-meta {
@@ -595,11 +631,11 @@ body {
595631
}
596632

597633
.file-item:hover {
598-
background: rgba(88, 166, 255, 0.05);
634+
background: var(--row-hover);
599635
}
600636

601637
.file-item.active {
602-
background: rgba(88, 166, 255, 0.1);
638+
background: var(--row-active);
603639
}
604640

605641
.file-name {
@@ -660,7 +696,7 @@ body {
660696
.modal-overlay {
661697
position: fixed;
662698
inset: 0;
663-
background: rgba(0, 0, 0, 0.6);
699+
background: var(--modal-backdrop);
664700
display: flex;
665701
align-items: center;
666702
justify-content: center;
@@ -735,3 +771,29 @@ body {
735771
.modal-danger:hover {
736772
opacity: 0.85;
737773
}
774+
775+
/* --- Theme toggle --- */
776+
.theme-toggle {
777+
background: none;
778+
border: 1px solid var(--border);
779+
color: var(--text-muted);
780+
width: 32px;
781+
height: 32px;
782+
border-radius: 6px;
783+
cursor: pointer;
784+
display: flex;
785+
align-items: center;
786+
justify-content: center;
787+
transition: all 0.15s;
788+
flex-shrink: 0;
789+
}
790+
791+
.theme-toggle:hover {
792+
color: var(--text);
793+
border-color: var(--text-muted);
794+
}
795+
796+
.theme-toggle svg {
797+
width: 16px;
798+
height: 16px;
799+
}

0 commit comments

Comments
 (0)