diff --git a/src/components/ColorSchemeToggle.tsx b/src/components/ColorSchemeToggle.tsx new file mode 100644 index 0000000..cf0123b --- /dev/null +++ b/src/components/ColorSchemeToggle.tsx @@ -0,0 +1,21 @@ +import { useState, useEffect } from 'react'; +import Moon from '../icons/Moon'; +import Sun from '../icons/Sun'; +import { detectColorScheme, switchTheme } from '../util/colorScheme'; + +const ColorSchemeToggle = () => { + const [dark, setDark] = useState(); + + useEffect(() => { + setDark(detectColorScheme()); + }, []); + + const handleClick = (): void => { + setDark((_prev) => !_prev); + switchTheme(dark!); + }; + + return ; +}; + +export default ColorSchemeToggle; diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 363035b..f883458 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -8,6 +8,8 @@ import { useSearch } from '../context/searchContext'; import FilterModal from './FilterModal'; import Search from '../icons/Search'; import Clear from '../icons/Clear'; +import { detectColorScheme, switchTheme } from '../util/colorScheme'; +import ColorSchemeToggle from './ColorSchemeToggle'; export default function Navbar(): JSX.Element { const location = useLocation(); @@ -74,6 +76,7 @@ export default function Navbar(): JSX.Element {

Songbook

+ diff --git a/src/icons/Moon.tsx b/src/icons/Moon.tsx new file mode 100644 index 0000000..42de60f --- /dev/null +++ b/src/icons/Moon.tsx @@ -0,0 +1,14 @@ +import Icon, { IconProps } from './Icon'; + +export default function Moon({ size, className }: IconProps): JSX.Element { + return ( + + + + + + ); +} diff --git a/src/icons/Sun.tsx b/src/icons/Sun.tsx new file mode 100644 index 0000000..17fd538 --- /dev/null +++ b/src/icons/Sun.tsx @@ -0,0 +1,14 @@ +import Icon, { IconProps } from './Icon'; + +export default function Sun({ size, className }: IconProps): JSX.Element { + return ( + + + + + + ); +} diff --git a/src/index.scss b/src/index.scss index 024fe42..b35fc80 100644 --- a/src/index.scss +++ b/src/index.scss @@ -19,6 +19,7 @@ $main-list-padding: 0.5rem 0 0.75rem; --background: 255, 255, 255; --foreground: 0, 0, 0; + --primary: 64, 104, 125; --primary-medium: 46, 81, 99; --primary-dark: 28, 42, 51; @@ -28,13 +29,18 @@ $main-list-padding: 0.5rem 0 0.75rem; max-width: 100vw; } +[data-theme='dark'] { + --background: 28, 42, 51; + --foreground: 255, 255, 255; +} + html, body, #root { margin: 0; padding: 0; color: rgba(var(--foreground), 0.85); - background-color: var(--background); + background-color: rgba(var(--background)); height: 100vh; overflow: hidden; @@ -81,7 +87,7 @@ nav { div.menu { height: $menu-height; background-color: rgb(var(--primary-medium)); - color: rgba(var(--background), 1); + color: #fff; @extend .flex-row; @extend .space-between; @@ -127,6 +133,7 @@ nav { flex: 1; height: $searchbar-height; background-color: rgb(var(--background)); + color: rgb(var(--foreground)); border: none; padding: 0.25rem 0.3rem; font-size: 1.1rem; @@ -217,12 +224,16 @@ main.BookmarksList { .SongListItem { @extend .list-item; - > li > div { - p { - margin: 0.2rem 0 0 0; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; + > li { + background-color: rgba(var(--background)); + + > div { + p { + margin: 0.2rem 0 0 0; + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + } } } } diff --git a/src/util/colorScheme.ts b/src/util/colorScheme.ts new file mode 100644 index 0000000..c496e16 --- /dev/null +++ b/src/util/colorScheme.ts @@ -0,0 +1,36 @@ +import React from 'react'; + +function detectColorScheme(): boolean { + var theme = 'light'; //default to light + + //local storage is used to override OS theme settings + if (localStorage.getItem('theme')) { + if (localStorage.getItem('theme') == 'dark') { + var theme = 'dark'; + } + } else if (!window.matchMedia) { + //matchMedia method not supported + return false; + } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + //OS theme setting detected as dark + var theme = 'dark'; + } + + //dark theme preferred, set document with a `data-theme` attribute + if (theme == 'dark') { + document.documentElement.setAttribute('data-theme', 'dark'); + } + return theme === 'dark'; +} + +function switchTheme(dark: boolean) { + if (!dark) { + localStorage.setItem('theme', 'dark'); + document.documentElement.setAttribute('data-theme', 'dark'); + } else { + localStorage.setItem('theme', 'light'); + document.documentElement.setAttribute('data-theme', 'light'); + } +} + +export { detectColorScheme, switchTheme };