Skip to content

Commit 9fde94d

Browse files
authored
Merge pull request #131 from burinc/improve-dark-mode-ui
feat: Add CSS variable-based dark mode logic
2 parents d8c54a0 + 09872cb commit 9fde94d

8 files changed

Lines changed: 693 additions & 321 deletions

src/scittle/weather/complete_dashboard.cljs

Lines changed: 121 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,52 @@
55
[reagent.dom :as rdom]
66
[clojure.string :as str]))
77

8+
;; ============================================================================
9+
;; Theme Styles
10+
;; ============================================================================
11+
12+
(defn theme-styles
13+
"CSS for light and dark mode support using Quarto's data-bs-theme attribute"
14+
[]
15+
[:style "
16+
/* Light mode (default) */
17+
[data-bs-theme='light'] {
18+
--card-bg: #ffffff;
19+
--card-secondary-bg: #f9fafb;
20+
--input-bg: #ffffff;
21+
--text-primary: #1f2937;
22+
--text-secondary: #6b7280;
23+
--text-tertiary: #9ca3af;
24+
--border-color: #e5e7eb;
25+
--border-color-dark: #d1d5db;
26+
--button-bg: #f3f4f6;
27+
--button-hover: #e5e7eb;
28+
--button-text: #374151;
29+
--shadow-color: rgba(0, 0, 0, 0.1);
30+
}
31+
32+
/* Dark mode */
33+
[data-bs-theme='dark'] {
34+
--card-bg: #1f2937;
35+
--card-secondary-bg: #111827;
36+
--input-bg: #111827;
37+
--text-primary: #f3f4f6;
38+
--text-secondary: #d1d5db;
39+
--text-tertiary: #9ca3af;
40+
--border-color: #374151;
41+
--border-color-dark: #4b5563;
42+
--button-bg: #374151;
43+
--button-hover: #4b5563;
44+
--button-text: #f3f4f6;
45+
--shadow-color: rgba(0, 0, 0, 0.3);
46+
}
47+
48+
/* Hover states - applied universally */
49+
button:not(.btn-primary):hover {
50+
background: var(--button-hover) !important;
51+
}
52+
"])
53+
854
;; ============================================================================
955
;; API Functions (Consolidated)
1056
;; ============================================================================
@@ -82,24 +128,29 @@
82128
;; Utilities (from previous demos)
83129
;; ============================================================================
84130

85-
(defn celsius-to-fahrenheit [c]
131+
(defn celsius-to-fahrenheit
132+
[c]
86133
(when c (+ (* c 1.8) 32)))
87134

88-
(defn fahrenheit-to-celsius [f]
135+
(defn fahrenheit-to-celsius
136+
[f]
89137
(when f (* (- f 32) 0.5556)))
90138

91-
(defn fahrenheit-to-kelvin [f]
139+
(defn fahrenheit-to-kelvin
140+
[f]
92141
(when f (+ (fahrenheit-to-celsius f) 273.15)))
93142

94-
(defn format-temp [fahrenheit unit]
143+
(defn format-temp
144+
[fahrenheit unit]
95145
(when fahrenheit
96146
(case unit
97147
"F" (str fahrenheit "°F")
98148
"C" (str (Math/round (fahrenheit-to-celsius fahrenheit)) "°C")
99149
"K" (str (Math/round (fahrenheit-to-kelvin fahrenheit)) "K")
100150
(str fahrenheit "°F"))))
101151

102-
(defn get-weather-icon [short-forecast]
152+
(defn get-weather-icon
153+
[short-forecast]
103154
(let [forecast-lower (str/lower-case (or short-forecast ""))]
104155
(cond
105156
(re-find #"thunder|tstorm" forecast-lower) "⛈️"
@@ -146,12 +197,13 @@
146197
:opacity "0.8"})
147198

148199
(def card-style
149-
{:background "#ffffff"
150-
:border "1px solid #e0e0e0"
200+
{:background "var(--card-bg, #ffffff)"
201+
:color "var(--text-primary, #1f2937)"
202+
:border "1px solid var(--border-color, #e5e7eb)"
151203
:border-radius "12px"
152204
:padding "24px"
153205
:margin-bottom "20px"
154-
:box-shadow "0 2px 8px rgba(0,0,0,0.1)"})
206+
:box-shadow "0 2px 8px var(--shadow-color, rgba(0,0,0,0.1))"})
155207

156208
(def location-search-style
157209
{:display "grid"
@@ -161,9 +213,12 @@
161213

162214
(def input-style
163215
{:padding "12px 15px"
164-
:border "1px solid #ddd"
216+
:background "var(--input-bg, #ffffff)"
217+
:color "var(--text-primary, #1f2937)"
218+
:border "1px solid var(--border-color-dark, #d1d5db)"
165219
:border-radius "8px"
166-
:font-size "14px"})
220+
:font-size "14px"
221+
:box-shadow "inset 0 1px 2px var(--shadow-color, rgba(0,0,0,0.05))"})
167222

168223
(def button-primary-style
169224
{:padding "12px 24px"
@@ -174,7 +229,8 @@
174229
:cursor "pointer"
175230
:font-size "14px"
176231
:font-weight "600"
177-
:transition "all 0.2s"})
232+
:transition "all 0.2s"
233+
:box-shadow "0 2px 4px rgba(0,0,0,0.3)"})
178234

179235
(def quick-cities-grid-style
180236
{:display "grid"
@@ -184,18 +240,21 @@
184240

185241
(def city-button-style
186242
{:padding "10px"
187-
:background "#f3f4f6"
188-
:border "1px solid #e5e7eb"
243+
:background "var(--button-bg, #f3f4f6)"
244+
:color "var(--button-text, #374151)"
245+
:border "1px solid var(--border-color-dark, #d1d5db)"
189246
:border-radius "8px"
190247
:cursor "pointer"
191248
:font-size "13px"
249+
:font-weight "500"
192250
:text-align "center"
193-
:transition "all 0.2s"})
251+
:transition "all 0.2s"
252+
:box-shadow "0 1px 2px var(--shadow-color, rgba(0,0,0,0.1))"})
194253

195254
(def tabs-container-style
196255
{:display "flex"
197256
:gap "5px"
198-
:border-bottom "2px solid #e5e7eb"
257+
:border-bottom "2px solid #374151"
199258
:margin-bottom "20px"})
200259

201260
(defn tab-style [active?]
@@ -215,7 +274,7 @@
215274
:justify-content "space-between"
216275
:align-items "center"
217276
:padding "15px"
218-
:background "#f9fafb"
277+
:background "var(--card-secondary-bg, #f9fafb)"
219278
:border-radius "8px"
220279
:margin-bottom "20px"
221280
:flex-wrap "wrap"
@@ -229,17 +288,17 @@
229288
(def setting-label-style
230289
{:font-size "14px"
231290
:font-weight "500"
232-
:color "#4b5563"})
291+
:color "var(--text-secondary, #6b7280)"})
233292

234293
(def toggle-buttons-style
235294
{:display "flex"
236295
:gap "5px"})
237296

238297
(defn toggle-button-style [active?]
239298
{:padding "6px 14px"
240-
:background (if active? "#3b82f6" "white")
241-
:color (if active? "white" "#6b7280")
242-
:border "1px solid #d1d5db"
299+
:background (if active? "#3b82f6" "var(--button-bg, #f3f4f6)")
300+
:color (if active? "white" "var(--button-text, #374151)")
301+
:border (if active? "1px solid #3b82f6" "1px solid var(--border-color-dark, #d1d5db)")
243302
:border-radius "6px"
244303
:cursor "pointer"
245304
:font-size "13px"
@@ -257,13 +316,13 @@
257316
(def temp-display-style
258317
{:font-size "72px"
259318
:font-weight "300"
260-
:color "#1a1a1a"
319+
:color "var(--text-primary, #1f2937)"
261320
:line-height "1"
262321
:margin "0"})
263322

264323
(def condition-text-style
265324
{:font-size "18px"
266-
:color "#6b7280"
325+
:color "var(--text-tertiary, #9ca3af)"
267326
:margin "10px 0"})
268327

269328
(def metrics-quick-grid-style
@@ -273,19 +332,20 @@
273332

274333
(def metric-item-style
275334
{:padding "12px"
276-
:background "#f9fafb"
335+
:background "var(--card-secondary-bg, #f9fafb)"
336+
:border "1px solid var(--border-color, #e5e7eb)"
277337
:border-radius "8px"})
278338

279339
(def metric-label-style
280340
{:font-size "12px"
281-
:color "#6b7280"
341+
:color "#9ca3af"
282342
:margin-bottom "5px"
283343
:text-transform "uppercase"
284344
:font-weight "600"})
285345

286346
(def metric-value-style
287347
{:font-size "20px"
288-
:color "#1a1a1a"
348+
:color "var(--text-primary, #1f2937)"
289349
:font-weight "500"})
290350

291351
(def forecast-mini-grid-style
@@ -295,14 +355,15 @@
295355

296356
(def forecast-mini-card-style
297357
{:padding "15px"
298-
:background "#f9fafb"
358+
:background "var(--card-secondary-bg, #f9fafb)"
359+
:border "1px solid var(--border-color, #e5e7eb)"
299360
:border-radius "8px"
300361
:text-align "center"})
301362

302363
(def loading-style
303364
{:text-align "center"
304365
:padding "60px"
305-
:color "#6b7280"})
366+
:color "#9ca3af"})
306367

307368
;; ============================================================================
308369
;; Quick Cities
@@ -354,9 +415,7 @@
354415
^{:key (:name city)}
355416
[:button
356417
{:style city-button-style
357-
:on-click #(on-city-select city)
358-
:on-mouse-over #(set! (.. % -target -style -background) "#e5e7eb")
359-
:on-mouse-out #(set! (.. % -target -style -background) "#f3f4f6")}
418+
:on-click #(on-city-select city)}
360419
(:name city)])]])
361420

362421
(defn settings-bar [{:keys [unit on-unit-change]}]
@@ -371,7 +430,7 @@
371430
:on-click #(on-unit-change u)}
372431
u])]]
373432

374-
[:div {:style {:font-size "13px" :color "#6b7280"}}
433+
[:div {:style {:font-size "13px" :color "#9ca3af"}}
375434
"🔄 Auto-refresh: Off"]])
376435

377436
(defn tabs-navigation [{:keys [active-tab on-tab-change]}]
@@ -432,7 +491,7 @@
432491
(get-weather-icon (:shortForecast period))]
433492
[:div {:style {:font-size "24px" :font-weight "600" :color "#3b82f6"}}
434493
(format-temp (:temperature period) unit)]
435-
[:div {:style {:font-size "13px" :color "#6b7280" :margin-top "8px"}}
494+
[:div {:style {:font-size "13px" :color "#9ca3af" :margin-top "8px"}}
436495
(:shortForecast period)]])]))
437496

438497
(defn hourly-view [{:keys [hourly unit]}]
@@ -459,7 +518,7 @@
459518
[:div {:style {:font-size "64px"}} ""]
460519
[:div {:style {:font-size "18px" :font-weight "500" :margin-top "15px"}}
461520
"No Active Weather Alerts"]
462-
[:div {:style {:font-size "14px" :color "#6b7280" :margin-top "8px"}}
521+
[:div {:style {:font-size "14px" :color "#9ca3af" :margin-top "8px"}}
463522
"All clear! No weather warnings or advisories."]]
464523

465524
[:div
@@ -552,33 +611,35 @@
552611
(fetch-all-weather))]
553612

554613
(fn []
555-
[:div {:style app-container-style}
556-
[:div {:style header-card-style}
557-
[:h1 {:style app-title-style}
558-
[:span "☀️"] [:span "Weather Dashboard"]]
559-
(when @location-name
560-
[:div {:style location-header-style}
561-
"📍 " @location-name])
562-
(when @last-updated
563-
[:div {:style last-updated-style}
564-
"Last updated: " (.toLocaleTimeString @last-updated)])]
565-
566-
[location-search-panel
567-
{:lat @lat
568-
:lon @lon
569-
:on-lat-change #(reset! lat %)
570-
:on-lon-change #(reset! lon %)
571-
:on-fetch fetch-all-weather
572-
:cities quick-cities
573-
:on-city-select select-city}]
574-
575-
(cond
576-
@loading? [loading-spinner]
577-
@weather-data [weather-dashboard
578-
{:weather-data @weather-data
579-
:location-name @location-name
580-
:unit @unit
581-
:on-unit-change #(reset! unit %)}])])))
614+
[:div
615+
[theme-styles]
616+
[:div {:style app-container-style}
617+
[:div {:style header-card-style}
618+
[:h1 {:style app-title-style}
619+
[:span "☀️"] [:span "Weather Dashboard"]]
620+
(when @location-name
621+
[:div {:style location-header-style}
622+
"📍 " @location-name])
623+
(when @last-updated
624+
[:div {:style last-updated-style}
625+
"Last updated: " (.toLocaleTimeString @last-updated)])]
626+
627+
[location-search-panel
628+
{:lat @lat
629+
:lon @lon
630+
:on-lat-change #(reset! lat %)
631+
:on-lon-change #(reset! lon %)
632+
:on-fetch fetch-all-weather
633+
:cities quick-cities
634+
:on-city-select select-city}]
635+
636+
(cond
637+
@loading? [loading-spinner]
638+
@weather-data [weather-dashboard
639+
{:weather-data @weather-data
640+
:location-name @location-name
641+
:unit @unit
642+
:on-unit-change #(reset! unit %)}])]])))
582643

583644
;; ============================================================================
584645
;; Mount

0 commit comments

Comments
 (0)