Darkmode implemented
There is now a darkmode button
This commit is contained in:
65
frontend/src/dark_mode.rs
Normal file
65
frontend/src/dark_mode.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use gloo_storage::{LocalStorage, Storage};
|
||||
use web_sys::window;
|
||||
use yew::prelude::*;
|
||||
|
||||
#[function_component(DarkModeToggle)]
|
||||
pub fn dark_mode_toggle() -> Html {
|
||||
// 1. Initialize state from LocalStorage or system preference
|
||||
let is_dark = use_state(|| {
|
||||
LocalStorage::get::<bool>("dark-mode").unwrap_or_else(|_| {
|
||||
// Fallback to system preference if not set
|
||||
window()
|
||||
.and_then(|w| w.match_media("(prefers-color-scheme: dark)").ok().flatten())
|
||||
.map(|m| m.matches())
|
||||
.unwrap_or(false)
|
||||
})
|
||||
});
|
||||
|
||||
// 2. Synchronize the class on the body when the state changes
|
||||
{
|
||||
let is_dark = is_dark.clone();
|
||||
use_effect_with(is_dark, |is_dark| {
|
||||
if let Some(win) = window() {
|
||||
if let Some(doc) = win.document() {
|
||||
if let Some(body) = doc.body() {
|
||||
if **is_dark {
|
||||
let _ = body.class_list().add_1("theme-dark");
|
||||
let _ = body.class_list().remove_1("theme-light");
|
||||
let _ = LocalStorage::set("dark-mode", true);
|
||||
} else {
|
||||
let _ = body.class_list().add_1("theme-light");
|
||||
let _ = body.class_list().remove_1("theme-dark");
|
||||
let _ = LocalStorage::set("dark-mode", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|| ()
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Toggle action
|
||||
let onclick = {
|
||||
let is_dark = is_dark.clone();
|
||||
Callback::from(move |_| {
|
||||
is_dark.set(!*is_dark);
|
||||
})
|
||||
};
|
||||
|
||||
html! {
|
||||
<button class="dark-mode-toggle" {onclick}>
|
||||
if *is_dark {
|
||||
// Sun Icon for switching to light mode
|
||||
<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">
|
||||
<circle cx="12" cy="12" r="4"/>
|
||||
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>
|
||||
</svg>
|
||||
} else {
|
||||
// Moon Icon for switching to dark mode
|
||||
<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="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>
|
||||
</svg>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod auth;
|
||||
mod pages;
|
||||
mod dark_mode;
|
||||
use crate::auth::ProtectedRoute;
|
||||
use crate::pages::*;
|
||||
use gloo_net::http::Request;
|
||||
@@ -271,6 +272,7 @@ fn switch(route: Route) -> Html {
|
||||
pub fn app() -> Html {
|
||||
html! {
|
||||
<BrowserRouter>
|
||||
<dark_mode::DarkModeToggle />
|
||||
<Switch<Route> render={switch} />
|
||||
</BrowserRouter>
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Color Palette (Reference Style)
|
||||
$color-bg: #f0f2f5;
|
||||
$color-bg-dark: #121212;
|
||||
$color-container: #ffffff;
|
||||
$color-container-dark: #333333;
|
||||
$color-bg: var(--color-bg);
|
||||
$color-bg-dark: var(--color-bg-dark);
|
||||
$color-container: var(--color-container);
|
||||
$color-container-dark: var(--color-container-dark);
|
||||
$color-sidebar: #0f172a;
|
||||
$color-primary: #2b79c2;
|
||||
$color-primary-hover: #1d5fa0;
|
||||
$color-accent: #2b79c2;
|
||||
$color-muted: #6b7280;
|
||||
$color-text: #111827;
|
||||
$color-text-dark: #e2e2e2;
|
||||
$color-text: var(--color-text);
|
||||
$color-text-dark: var(--color-text-dark);
|
||||
|
||||
// Status Colors
|
||||
$color-status-todo: #ffcccc;
|
||||
|
||||
75
frontend/src/styles/components/_dark_mode.scss
Normal file
75
frontend/src/styles/components/_dark_mode.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
:root {
|
||||
--color-bg: #f0f2f5;
|
||||
--color-bg-dark: #121212;
|
||||
--color-container: #ffffff;
|
||||
--color-container-dark: #333333;
|
||||
--color-text: #111827;
|
||||
--color-text-dark: #e2e2e2;
|
||||
}
|
||||
|
||||
// Automatically respect standard media query if no override is set
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-bg: #121212;
|
||||
--color-container: #333333;
|
||||
--color-text: #e2e2e2;
|
||||
}
|
||||
}
|
||||
|
||||
// Force Light Mode overrides
|
||||
body.theme-light {
|
||||
--color-bg: #f0f2f5;
|
||||
--color-bg-dark: #f0f2f5;
|
||||
--color-container: #ffffff;
|
||||
--color-container-dark: #ffffff;
|
||||
--color-text: #111827;
|
||||
--color-text-dark: #111827;
|
||||
}
|
||||
|
||||
// Force Dark Mode overrides
|
||||
body.theme-dark {
|
||||
--color-bg: #121212;
|
||||
--color-bg-dark: #121212;
|
||||
--color-container: #333333;
|
||||
--color-container-dark: #333333;
|
||||
--color-text: #e2e2e2;
|
||||
--color-text-dark: #e2e2e2;
|
||||
}
|
||||
|
||||
// Fixed positioning ensures NO other component's layout is ever altered
|
||||
.dark-mode-toggle {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 9999;
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(128, 128, 128, 0.2);
|
||||
background-color: var(--color-container);
|
||||
color: var(--color-text);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
stroke: var(--color-text);
|
||||
transition: stroke 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
@use "components/diagnostics";
|
||||
@use "components/pages";
|
||||
@use "components/setup";
|
||||
@use "components/dark_mode";
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
||||
Reference in New Issue
Block a user