From 5f41608e92312490e5fec881b3b8823e8b6df8f0 Mon Sep 17 00:00:00 2001 From: Brandon Dean Date: Sat, 10 Jul 2021 17:57:08 -0400 Subject: [PATCH] Add light/dark theme switching --- src/app.tsx | 21 ++++++++--- src/components/app.tsx | 5 +-- src/components/bookmarks.tsx | 3 +- src/components/elements.tsx | 9 ++--- src/components/greeter.tsx | 5 +-- src/components/icon.tsx | 5 +-- src/components/imprint.tsx | 5 +-- src/components/modal.tsx | 7 ++-- src/components/searchBar.tsx | 6 +-- src/components/select.tsx | 4 +- src/components/settings.tsx | 72 ++++++++++++++++++++++-------------- src/lib/theme.tsx | 32 ++++++++++++++-- src/lib/useMediaQuery.tsx | 24 ++++++++++++ 13 files changed, 133 insertions(+), 65 deletions(-) create mode 100644 src/lib/useMediaQuery.tsx diff --git a/src/app.tsx b/src/app.tsx index 2263ae0..ce4342a 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,4 +1,4 @@ -import { createGlobalStyle } from "styled-components"; +import { createGlobalStyle, ThemeProvider } from "styled-components"; import SearchBar from "./components/searchBar"; import Greeter from "./components/greeter"; @@ -7,12 +7,13 @@ import BookmarkList from "./components/bookmarks"; import Settings from "./components/settings"; import Imprint from "./components/imprint"; -import selectedTheme from "./lib/theme"; +import { IThemeProps, getTheme, setScheme } from "./lib/theme"; import useFetcher from "./lib/fetcher"; +import useMediaQuery from "./lib/useMediaQuery"; -export const GlobalStyle = createGlobalStyle` +export const GlobalStyle = createGlobalStyle<{ theme: IThemeProps }>` body { - background-color: ${selectedTheme.backgroundColor}; + background-color: ${(props) => props.theme.backgroundColor}; font-family: Roboto, sans-serif; margin: auto; @@ -38,8 +39,16 @@ const App = () => { greeterData, } = useFetcher(); + const theme = getTheme(); + let isDark = useMediaQuery("(prefers-color-scheme: dark"); + if (isDark) { + setScheme("dark-theme"); + } else { + setScheme("light-theme"); + } + return ( - <> +
@@ -56,7 +65,7 @@ const App = () => { {!bookmarkData.error && } {!imprintData.error && }
- +
); }; diff --git a/src/components/app.tsx b/src/components/app.tsx index 891e909..ea013bc 100644 --- a/src/components/app.tsx +++ b/src/components/app.tsx @@ -1,12 +1,11 @@ import Icon from "./icon"; import styled from "styled-components"; -import selectedTheme from "../lib/theme"; const AppContainer = styled.a` display: flex; flex: 1 0 auto; padding: 1rem; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; font-weight: 500; text-transform: uppercase; margin: 0; @@ -37,7 +36,7 @@ const AppDescription = styled.p` margin: 0; font-size: 0.65rem; font-weight: 400; - color: ${selectedTheme.accentColor}; + color: ${(props) => props.theme.accentColor}; `; export interface IAppProps { diff --git a/src/components/bookmarks.tsx b/src/components/bookmarks.tsx index 0abc783..0c7cf66 100644 --- a/src/components/bookmarks.tsx +++ b/src/components/bookmarks.tsx @@ -6,7 +6,6 @@ import { ListContainer, SubHeadline, } from "./elements"; -import selectedTheme from "../lib/theme"; const GroupContainer = styled.div` display: flex; @@ -18,7 +17,7 @@ const GroupContainer = styled.div` const Bookmark = styled.a` font-weight: 400; text-decoration: none; - color: ${selectedTheme.accentColor}; + color: ${(props) => props.theme.accentColor}; padding-top: 0.75rem; font-size: 0.9rem; diff --git a/src/components/elements.tsx b/src/components/elements.tsx index 3c14029..3051995 100644 --- a/src/components/elements.tsx +++ b/src/components/elements.tsx @@ -1,5 +1,4 @@ import styled from "styled-components"; -import selectedTheme from "../lib/theme"; export const ListContainer = styled.div` padding: 2rem 0; @@ -11,7 +10,7 @@ export const Headline = styled.h2` text-transform: uppercase; margin: 0; font-size: 1.5rem; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; `; export const SubHeadline = styled.h3` @@ -19,7 +18,7 @@ export const SubHeadline = styled.h3` font-weight: 700; text-transform: uppercase; margin: 0; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; `; export const ItemList = styled.ul` @@ -44,8 +43,8 @@ export const Button = styled.button` text-transform: uppercase; font-family: Roboto, sans-serif; font-weight: 400; - border: 1px solid ${selectedTheme.mainColor}; - color: ${selectedTheme.mainColor}; + border: 1px solid ${(props) => props.theme.mainColor}; + color: ${(props) => props.theme.mainColor}; background: none; min-height: 2rem; diff --git a/src/components/greeter.tsx b/src/components/greeter.tsx index 25170d8..f5c1205 100644 --- a/src/components/greeter.tsx +++ b/src/components/greeter.tsx @@ -1,5 +1,4 @@ import styled from "styled-components"; -import selectedTheme from "../lib/theme"; const GreeterContainer = styled.div` padding: 2rem 0; @@ -9,7 +8,7 @@ const GreetText = styled.h1` font-weight: 900; font-size: 3rem; margin: 0.5rem 0; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; `; const DateText = styled.h3` @@ -17,7 +16,7 @@ const DateText = styled.h3` font-size: 1rem; text-transform: uppercase; margin: 0; - color: ${selectedTheme.accentColor}; + color: ${(props) => props.theme.accentColor}; `; export interface IGreeterComponentProps { diff --git a/src/components/icon.tsx b/src/components/icon.tsx index a3fb907..ca1d5ab 100644 --- a/src/components/icon.tsx +++ b/src/components/icon.tsx @@ -1,6 +1,5 @@ import React from "react"; import styled from "styled-components"; -import selectedTheme from "../lib/theme"; interface IIconProps { name: string; @@ -40,7 +39,7 @@ const IconContainer = styled.i` -moz-osx-font-smoothing: grayscale; font-feature-settings: "liga"; font-size: ${(props) => props.about}; - color: ${(props) => props.color}; + color: ${(props) => props.theme.mainColor}; `; /** @@ -49,7 +48,7 @@ const IconContainer = styled.i` * @returns {React.ReactNode} the icon node */ export const Icon = ({ name, size }: IIconProps) => ( - + {name} ); diff --git a/src/components/imprint.tsx b/src/components/imprint.tsx index 68b07a3..0331646 100644 --- a/src/components/imprint.tsx +++ b/src/components/imprint.tsx @@ -1,6 +1,5 @@ import Modal from "./modal"; import styled from "styled-components"; -import selectedTheme from "../lib/theme"; import { ListContainer, ItemList, Headline, SubHeadline } from "./elements"; const ModalSubHeadline = styled(SubHeadline)` @@ -12,14 +11,14 @@ const Text = styled.p` padding: 0; margin: 0; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; `; const Link = styled.a` display: block; padding: 0; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; text-decoration: none; &:hover { diff --git a/src/components/modal.tsx b/src/components/modal.tsx index 8b18bea..7af24e8 100644 --- a/src/components/modal.tsx +++ b/src/components/modal.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; import styled from "styled-components"; -import selectedTheme from "../lib/theme"; import { Headline } from "./elements"; import { IconButton } from "./icon"; @@ -12,8 +11,8 @@ const ModalContainer = styled.div` padding: 1rem; transform: translate(-50%, -50%); z-index: 10; - border: 1px solid ${selectedTheme.mainColor}; - background-color: ${selectedTheme.backgroundColor}; + border: 1px solid ${(props) => props.theme.mainColor}; + background-color: ${(props) => props.theme.backgroundColor}; `; const Text = styled.p` @@ -22,7 +21,7 @@ const Text = styled.p` font-weight: 400; text-decoration: none; - color: ${selectedTheme.accentColor}; + color: ${(props) => props.theme.accentColor}; padding-top: 0.75rem; font-size: 0.9rem; diff --git a/src/components/searchBar.tsx b/src/components/searchBar.tsx index d1931c5..770965b 100644 --- a/src/components/searchBar.tsx +++ b/src/components/searchBar.tsx @@ -1,8 +1,6 @@ import React, { useEffect, useState } from "react"; import styled from "styled-components"; -import selectedTheme from "../lib/theme"; - import { Button } from "./elements"; const Search = styled.form` @@ -21,11 +19,11 @@ const SearchInput = styled.input` font-size: 1rem; border: none; - border-bottom: 1px solid ${selectedTheme.accentColor}; + border-bottom: 1px solid ${(props) => props.theme.accentColor}; border-radius: 0; background: none; - color: ${selectedTheme.mainColor}; + color: ${(props) => props.theme.mainColor}; :focus { outline: none; diff --git a/src/components/select.tsx b/src/components/select.tsx index 75fc3c2..65d4229 100644 --- a/src/components/select.tsx +++ b/src/components/select.tsx @@ -8,6 +8,7 @@ export interface IItemProps { export interface ISelectProps { items: Array; onChange: (item: any) => void; + current: string; className?: string; } @@ -19,7 +20,7 @@ const update = ( onChange(items.find((item) => item.value.toString() === e.target.value)); }; -const Select = ({ items, onChange, className }: ISelectProps) => ( +const Select = ({ items, onChange, current, className }: ISelectProps) => (