diff --git a/src/components/App.css b/src/components/App.css index b0749d1..65c5628 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -1,6 +1,6 @@ body { - background-color: #141414; - color: #eee; + background-color: var(--background-color); + color: var(--text-color); font-family: monospace; } @@ -29,10 +29,10 @@ button, select { font-size: inherit; font-family: inherit; - background: #fff2; + background: var(--layer-color); border-radius: 8px; border: none; - color: #eee; + color: var(--text-color); padding-inline: 8px; padding-block: 4px; outline: none; @@ -43,27 +43,27 @@ input[type="file"].non-transparent::file-selector-button, input[type="text"].non-transparent, button.non-transparent, select.non-transparent { - background: #222; - border: 2px solid #444; + background: var(--layer-color-non-transparent); + border: 2px solid var(--border-color); } input[type="file"]::file-selector-button:hover, button:hover, select:hover { - background: #fff3; + background: var(--layer-color-hover); } input[type="file"].non-transparent::file-selector-button:hover, button.non-transparent:hover, select.non-transparent:hover { - background: #444; + background: var(--layer-color-non-transparent-hover); } input[type="file"]:disabled::file-selector-button, button:disabled, select:disabled { - background: #fff1; - color: #666; + background: var(--layer-color-disabled); + color: var(--text-color-disabled); cursor: not-allowed; } @@ -71,8 +71,8 @@ select:disabled { input[type="file"].non-transparent:disabled::file-selector-button, button.non-transparent:disabled, select.non-transparent:disabled { - background: #222; - color: #666; + background: var(--layer-color-non-transparent-disabled); + color: var(--text-color-disabled); cursor: not-allowed; } @@ -83,17 +83,17 @@ button { } select > option { - background: #141414; - color: #eee; + background: var(--background-color); + color: var(--text-color); } input[type="text"] { background-color: unset; - border: 2px solid #fff2; + border: 2px solid var(--layer-color); } input[type="text"]:focus { - border: 2px solid #fff3; + border: 2px solid var(--layer-color-hover); } .invis-button { @@ -111,17 +111,17 @@ input[type="text"]:focus { } ::-webkit-scrollbar-track { - background-color: #141414; + background-color: var(--background-color); border-radius: 5px; } ::-webkit-scrollbar-thumb { - background-color: #fff2; + background-color: var(--layer-color); border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { - background-color: #fff3; + background-color: var(--layer-color-hover); } ::-webkit-scrollbar { diff --git a/src/components/App.js b/src/components/App.js index 19862f9..7bfb534 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,7 +1,6 @@ import { useState } from 'react'; import Map from './map/Map'; import Toolbar from './toolbar/Toolbar'; -import './App.css'; import SideMenu from './sidemenu/SideMenu'; import SettingsManager from './SettingsManager'; import LocList from './loclist/LocList'; @@ -10,23 +9,28 @@ import DistanceMeterContext from '../contexts/DistanceMeterContext'; import {GradientsManager} from "./GradientsManager"; import SideMenuContext from '../contexts/SideMenuContext'; import SceneryManager from './SceneryManager'; +import ThemeManager from './ThemeManager'; +import './Themes.css'; +import './App.css'; function App() { const [sideMenuOpen, setSideMenuOpen] = useState(false); const [distancePoints, setDistancePoints] = useState(null); return ( - - - - - - - - - - - + + + + + + + + + + + + + ); } diff --git a/src/components/ThemeManager.js b/src/components/ThemeManager.js new file mode 100644 index 0000000..2d98164 --- /dev/null +++ b/src/components/ThemeManager.js @@ -0,0 +1,20 @@ +import { useState } from "react"; +import ThemeContext from "../contexts/ThemeContext"; + +export default function ThemeManager(props) { + const [ lightMode, setLightMode ] = useState(false); + + if(lightMode === true) { + document.body.classList.add('light-mode'); + } else { + document.body.classList.remove('light-mode'); + } + + return ( + + { props.children } + + ); +} diff --git a/src/components/Themes.css b/src/components/Themes.css new file mode 100644 index 0000000..6c73490 --- /dev/null +++ b/src/components/Themes.css @@ -0,0 +1,57 @@ +:root { + --background-color: #141414; + --text-color: #eee; + --half-layer-color: #fff1; + --layer-color: #fff2; + --layer-color-hover: #fff3; + --layer-color-disabled: #fff1; + --layer-color-non-transparent: #222; + --layer-color-non-transparent-hover: #444; + --layer-color-non-transparent-disabled: #222; + --text-color-disabled: #888; + --border-color: #444; + --text-color-inverted: #141414; + --icon-color: #aaa; + --icon-color-active: #fff; + --map-background-color: #111; + --map-shadow-color: #000; + --map-icon-color: #eee; + --map-text-color: #eee; + --map-platform-color: #444; + --map-track-color: #aaa; + --map-track-color-invisible: #555; + --map-track-color-electrified: #aaf; + --dialog-background-color: #222; + --parser-log-background-color: #0004; + --parser-log-warn-color: #aa4; + --parser-log-error-color: #f44; +} + +.light-mode { + --background-color: #f8f8f8; + --text-color: #222; + --layer-color: #0002; + --half-layer-color: #0001; + --layer-color-hover: #0003; + --layer-color-disabled: #0001; + --layer-color-non-transparent: #ddd; + --layer-color-non-transparent-hover: #ccc; + --layer-color-non-transparent-disabled: #888; + --text-color-disabled: #666; + --border-color: #888; + --text-color-inverted: #eee; + --icon-color: #222; + --icon-color-active: #888; + --map-background-color: #fff; + --map-shadow-color: #fff8; + --map-icon-color: #111; + --map-text-color: #111; + --map-platform-color: #aaa; + --map-track-color: #555; + --map-track-color-invisible: #999; + --map-track-color-electrified: #33b; + --dialog-background-color: #ddd; + --parser-log-background-color: #0001; + --parser-log-warn-color: #774; + --parser-log-error-color: #f44; +} diff --git a/src/components/dialog/Dialog.css b/src/components/dialog/Dialog.css index 5234a06..a6deb98 100644 --- a/src/components/dialog/Dialog.css +++ b/src/components/dialog/Dialog.css @@ -8,13 +8,13 @@ width: 100vw; height: 100vh; z-index: 100; - background-color: rgba(0, 0, 0, 0.25); + background-color: rgba(0, 0, 0, 0.33); } .dialog { display: block; - background-color: #222; - border: 2px solid #444; + background-color: var(--background-color); + border: 2px solid var(--border-color); border-radius: 8px; margin: 0 auto; padding: 1em; @@ -34,7 +34,7 @@ .dialog-title { margin-bottom: 2em; - color: #888; + color: var(--text-color-disabled); font-weight: bold; } diff --git a/src/components/map/Map.css b/src/components/map/Map.css index eb0c914..f442a23 100644 --- a/src/components/map/Map.css +++ b/src/components/map/Map.css @@ -3,7 +3,7 @@ display: flex; flex-wrap: wrap; flex-direction: column; - background-color: #111; + background-color: var(--map-background-color); } .map svg path.track { @@ -19,9 +19,13 @@ .map text { font-size: 2px; - fill: #eee; + fill: var(--map-text-color); stroke: none; - text-shadow: 0 0 16px #000; + text-shadow: 0 0 16px var(--map-shadow-color); +} + +.map .sign-text { + text-shadow: none; } .map .switch-name-text { @@ -46,11 +50,11 @@ } .map g.track-object circle { - fill: #eee; + fill: var(--map-icon-color); } .map g.track-object text { - fill: #eee; + fill: var(--map-text-color); display: none; } @@ -59,7 +63,7 @@ } .map g.platform rect { - fill: #444; + fill: var(--map-platform-color); } .map g.map-icon-stroked path, @@ -67,25 +71,25 @@ .map g.map-icon-stroked line, .map g.map-icon-stroked rect { fill: none; - stroke: #eee; + stroke: var(--map-icon-color); } .map g.map-icon path, .map g.map-icon circle, .map g.map-icon line, .map g.map-icon rect { - fill: #eee; + fill: var(--map-icon-color); stroke: none; } .map g.map-icon-stroked .background, .map g.map-icon .background { - fill: #111; + fill: var(--map-background-color); stroke: none; } .map g.map-icon-stroked .with-background, .map g.map-icon .with-background { - fill: #111; - stroke: #eee; + fill: var(--map-background-color); + stroke: var(--map-icon-color); } diff --git a/src/components/map/object-renderers/SignRenderer.js b/src/components/map/object-renderers/SignRenderer.js index a9a473b..0882d2d 100644 --- a/src/components/map/object-renderers/SignRenderer.js +++ b/src/components/map/object-renderers/SignRenderer.js @@ -40,7 +40,7 @@ function SignTextRenderer(props) { return ( { + setLightMode((prev) => !prev); + } + + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/toolbar/Toolbar.css b/src/components/toolbar/Toolbar.css index 0deb054..487c9e0 100644 --- a/src/components/toolbar/Toolbar.css +++ b/src/components/toolbar/Toolbar.css @@ -5,7 +5,7 @@ align-items: center; gap: 10px; padding: 10px; - border-bottom: 2px solid #444; + border-bottom: 2px solid var(--border-color); } .toolbar-spacer { @@ -29,11 +29,11 @@ .loading-indicator { height: fit-content; padding: 4px 8px; - color: #141414; + color: var(--text-color-inverted); text-align: center; opacity: 0; transition: opacity 1s ease-in-out; - z-index: 1000; + z-index: 49; position: relative; } @@ -48,16 +48,19 @@ animation: fadeOut 1s ease-in-out 2s forwards; /* Start fading out after 2 seconds */ } +.theme-toggle svg, .side-menu-toggle svg { display: block; height: 2rem; width: 2rem; } +.theme-toggle g, .side-menu-toggle g { - stroke: #aaa; + stroke: var(--icon-color); } +.theme-toggle:hover g, .side-menu-toggle:hover g { - stroke: #fff; + stroke: var(--icon-color-active); } diff --git a/src/components/toolbar/Toolbar.js b/src/components/toolbar/Toolbar.js index b1a346e..b625437 100644 --- a/src/components/toolbar/Toolbar.js +++ b/src/components/toolbar/Toolbar.js @@ -4,6 +4,7 @@ import CustomFileSelect from './CustomFileSelect'; import SideMenuToggle from './SideMenuToggle'; import SceneryList from './SceneryList'; import ElectrificationStepMode from './ElectrificationStepMode'; +import ThemeToggle from './ThemeToggle'; export default function Toolbar() { return ( @@ -14,6 +15,7 @@ export default function Toolbar() {
+
diff --git a/src/contexts/ThemeContext.js b/src/contexts/ThemeContext.js new file mode 100644 index 0000000..1c69777 --- /dev/null +++ b/src/contexts/ThemeContext.js @@ -0,0 +1,4 @@ +import { createContext } from 'react'; + +const ThemeContext = createContext(); +export default ThemeContext; diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 962e6d2..574dd57 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -138,16 +138,16 @@ const Constants = { name: 'Standard', optionDefault: 'default', options: { - 'default': ['#aaa', 'Standard track'], - 'invisible': ['#444', 'Invisible track'], + 'default': ['var(--map-track-color)', 'Standard track'], + 'invisible': ['var(--map-track-color-invisible)', 'Invisible track'], } }, 'electrification': { name: 'Electrification', optionDefault: 'conflict', options: { - 'electrified': ['#aaf', 'Electrified'], - 'non-electrified': ['#aaa', 'Non-electrified'], + 'electrified': ['var(--map-track-color-electrified)', 'Electrified'], + 'non-electrified': ['var(--map-track-color)', 'Non-electrified'], 'not-checked': ['#aa4', 'Unknown'], 'conflict': ['#f44', 'Error / Conflict'], } diff --git a/src/icons/index.js b/src/icons/index.js index a501b68..db5bc9b 100644 --- a/src/icons/index.js +++ b/src/icons/index.js @@ -1,4 +1,6 @@ /* Toolbar Icons */ +export { DarkModeIcon } from "./toolbar/dark-mode"; +export { LightModeIcon } from "./toolbar/light-mode"; export { SideMenuIcon } from "./toolbar/side-menu"; /* TrackHoverInfo Icons */ diff --git a/src/icons/toolbar/dark-mode.js b/src/icons/toolbar/dark-mode.js new file mode 100644 index 0000000..29e2bea --- /dev/null +++ b/src/icons/toolbar/dark-mode.js @@ -0,0 +1,7 @@ +export const DarkModeIcon = ({color = '#aaa', strokeWidth = 9}) => ( + + + + + +); diff --git a/src/icons/toolbar/light-mode.js b/src/icons/toolbar/light-mode.js new file mode 100644 index 0000000..1ab6f8f --- /dev/null +++ b/src/icons/toolbar/light-mode.js @@ -0,0 +1,18 @@ +export const LightModeIcon = ({color = '#aaa', strokeWidth = 10}) => ( + + + + + + + + + + + + + + + + +); \ No newline at end of file