From 80da6e0ff6a4445a33d3f4ac7b3caf0e47572aaf Mon Sep 17 00:00:00 2001 From: Callil Capuozzo Date: Wed, 31 Jul 2019 14:11:58 -0400 Subject: [PATCH] Dark theme (#380) --- src/assets/svg/SVGArrowDown.js | 12 +++++ src/components/AddressInputPanel/index.js | 43 ++++++++------- src/components/ContextualInfo/index.js | 24 +++++++-- src/components/ContextualInfoNew/index.js | 4 +- src/components/CurrencyInputPanel/index.js | 23 ++++---- src/components/ExchangePage/index.jsx | 13 ++--- src/components/Header/index.js | 15 +++++- src/components/Modal/index.js | 8 +-- src/components/NavigationTabs/index.js | 14 +++-- src/components/TransactionDetails/index.js | 18 +++---- src/components/Web3Status/index.js | 10 ++-- src/contexts/LocalStorage.js | 20 +++++-- src/index.js | 20 +++---- src/pages/Pool/AddLiquidity.js | 27 +++++----- src/pages/Pool/CreateExchange.js | 2 +- src/pages/Pool/ModeSelector.js | 29 +++++++--- src/pages/Pool/RemoveLiquidity.js | 12 +++-- src/theme/components.js | 10 ++-- src/theme/index.js | 62 +++++++++++++--------- 19 files changed, 232 insertions(+), 134 deletions(-) create mode 100644 src/assets/svg/SVGArrowDown.js diff --git a/src/assets/svg/SVGArrowDown.js b/src/assets/svg/SVGArrowDown.js new file mode 100644 index 0000000000..24384a5504 --- /dev/null +++ b/src/assets/svg/SVGArrowDown.js @@ -0,0 +1,12 @@ +import React from 'react' + +const SVGArrowDown = props => ( + + + +) + +export default SVGArrowDown diff --git a/src/components/AddressInputPanel/index.js b/src/components/AddressInputPanel/index.js index f1561638da..fa15e54932 100644 --- a/src/components/AddressInputPanel/index.js +++ b/src/components/AddressInputPanel/index.js @@ -2,17 +2,17 @@ import React, { useState, useEffect } from 'react' import styled from 'styled-components' import { useTranslation } from 'react-i18next' import { useWeb3Context } from 'web3-react' -import { lighten } from 'polished' +import { transparentize } from 'polished' import { isAddress } from '../../utils' import { useDebounce } from '../../hooks' const InputPanel = styled.div` ${({ theme }) => theme.flexColumnNoWrap} - box-shadow: 0 4px 8px 0 ${({ theme }) => lighten(0.9, theme.royalBlue)}; + box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.royalBlue)}; position: relative; border-radius: 1.25rem; - background-color: ${({ theme }) => theme.white}; + background-color: ${({ theme }) => theme.inputBackground}; z-index: 1; ` @@ -21,9 +21,10 @@ const ContainerRow = styled.div` justify-content: center; align-items: center; border-radius: 1.25rem; - box-shadow: 0 0 0 1px ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; - background-color: ${({ theme }) => theme.white}; - transition: box-shadow 200ms ease-in-out; + border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; + + background-color: ${({ theme }) => theme.inputBackground}; + transition: box-shadow 125ms ease-in-out; ` const InputContainer = styled.div` @@ -59,13 +60,15 @@ const Input = styled.input` border: none; flex: 1 1 auto; width: 0; + background-color: ${({ theme }) => theme.inputBackground}; + color: ${({ error, theme }) => (error ? theme.salmonRed : theme.royalBlue)}; - transition: color 200ms ease-in-out; + transition: color 125ms ease-in-out; overflow: hidden; text-overflow: ellipsis; ::placeholder { - color: ${({ theme }) => theme.chaliceGray}; + color: ${({ theme }) => theme.placeholderGray}; } ` @@ -93,8 +96,9 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = let stale = false if (isAddress(debouncedInput)) { - try { - library.lookupAddress(debouncedInput).then(name => { + library + .lookupAddress(debouncedInput) + .then(name => { if (!stale) { // if an ENS name exists, set it as the destination if (name) { @@ -105,14 +109,15 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = } } }) - } catch { - setData({ address: debouncedInput, name: '' }) - setError(null) - } + .catch(() => { + setData({ address: debouncedInput, name: '' }) + setError(null) + }) } else { if (debouncedInput !== '') { - try { - library.resolveName(debouncedInput).then(address => { + library + .resolveName(debouncedInput) + .then(address => { if (!stale) { // if the debounced input name resolves to an address if (address) { @@ -123,9 +128,9 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = } } }) - } catch { - setError(true) - } + .catch(() => { + setError(true) + }) } } diff --git a/src/components/ContextualInfo/index.js b/src/components/ContextualInfo/index.js index 39abeba2e8..d9d0482234 100644 --- a/src/components/ContextualInfo/index.js +++ b/src/components/ContextualInfo/index.js @@ -2,8 +2,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' -import DropdownBlue from '../../assets/images/dropdown-blue.svg' -import DropupBlue from '../../assets/images/dropup-blue.svg' +import { ReactComponent as Dropup } from '../../assets/images/dropup-blue.svg' +import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg' const SummaryWrapper = styled.div` color: ${({ error, theme }) => (error ? theme.salmonRed : theme.doveGray)}; @@ -14,7 +14,7 @@ const SummaryWrapper = styled.div` ` const Details = styled.div` - background-color: ${({ theme }) => theme.concreteGray}; + background-color: ${({ theme }) => theme.doveGray}; padding: 1.5rem; border-radius: 1rem; font-size: 0.75rem; @@ -42,6 +42,20 @@ const SummaryWrapperContainer = styled.div` } ` +const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => +const ColoredDropup = styled(WrappedDropup)` + path { + stroke: ${({ theme }) => theme.royalBlue}; + } +` + +const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => +const ColoredDropdown = styled(WrappedDropdown)` + path { + stroke: ${({ theme }) => theme.royalBlue}; + } +` + class ContextualInfo extends Component { static propTypes = { openDetailsText: PropTypes.string, @@ -89,12 +103,12 @@ class ContextualInfo extends Component { {!this.state.showDetails ? ( <> {openDetailsText} - dropdown + ) : ( <> {closeDetailsText} - dropup + )} diff --git a/src/components/ContextualInfoNew/index.js b/src/components/ContextualInfoNew/index.js index 1373e5769a..5b5b0b0e2c 100644 --- a/src/components/ContextualInfoNew/index.js +++ b/src/components/ContextualInfoNew/index.js @@ -61,7 +61,7 @@ const ErrorSpan = styled.span` const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => const ColoredDropup = styled(WrappedDropup)` path { - stroke: ${({ isError, theme }) => isError && theme.salmonRed}; + stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)}; ${({ highSlippageWarning, theme }) => highSlippageWarning && @@ -74,7 +74,7 @@ const ColoredDropup = styled(WrappedDropup)` const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => const ColoredDropdown = styled(WrappedDropdown)` path { - stroke: ${({ isError, theme }) => isError && theme.salmonRed}; + stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)}; ${({ highSlippageWarning, theme }) => highSlippageWarning && diff --git a/src/components/CurrencyInputPanel/index.js b/src/components/CurrencyInputPanel/index.js index 9178a3a41e..4e44e4cb12 100644 --- a/src/components/CurrencyInputPanel/index.js +++ b/src/components/CurrencyInputPanel/index.js @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next' import { ethers } from 'ethers' import styled from 'styled-components' import escapeStringRegex from 'escape-string-regexp' -import { lighten, darken } from 'polished' +import { darken } from 'polished' import Tooltip from '@reach/tooltip' import '@reach/tooltip/styles.css' import { isMobile } from 'react-device-detect' @@ -18,6 +18,7 @@ import TokenLogo from '../TokenLogo' import SearchIcon from '../../assets/images/magnifying-glass.svg' import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions' import { useTokenDetails, useAllTokenDetails } from '../../contexts/Tokens' +import { transparentize } from 'polished' const GAS_MARGIN = ethers.utils.bigNumberify(1000) @@ -40,12 +41,14 @@ const SubCurrencySelect = styled.button` const InputRow = styled.div` ${({ theme }) => theme.flexRowNoWrap} align-items: center; + padding: 0.25rem 0.85rem 0.75rem; ` const Input = styled(BorderlessInput)` font-size: 1.5rem; color: ${({ error, theme }) => error && theme.salmonRed}; + background-color: ${({ theme }) => theme.inputBackground}; ` const StyledBorderlessInput = styled(BorderlessInput)` @@ -56,7 +59,7 @@ const StyledBorderlessInput = styled(BorderlessInput)` const CurrencySelect = styled.button` align-items: center; font-size: 1rem; - color: ${({ selected, theme }) => (selected ? theme.black : theme.royalBlue)}; + color: ${({ selected, theme }) => (selected ? theme.textColor : theme.royalBlue)}; height: 2rem; border: 1px solid ${({ selected, theme }) => (selected ? theme.mercuryGray : theme.royalBlue)}; border-radius: 2.5rem; @@ -90,27 +93,28 @@ const StyledDropDown = styled(DropDown)` height: 35%; path { - stroke: ${({ selected, theme }) => (selected ? theme.black : theme.royalBlue)}; + stroke: ${({ selected, theme }) => (selected ? theme.textColor : theme.royalBlue)}; } ` const InputPanel = styled.div` ${({ theme }) => theme.flexColumnNoWrap} - box-shadow: 0 4px 8px 0 ${({ theme }) => lighten(0.9, theme.royalBlue)}; + box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.royalBlue)}; position: relative; border-radius: 1.25rem; - background-color: ${({ theme }) => theme.white}; + background-color: ${({ theme }) => theme.inputBackground}; z-index: 1; ` const Container = styled.div` border-radius: 1.25rem; - box-shadow: 0 0 0 1px ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; - background-color: ${({ theme }) => theme.white}; - transition: box-shadow 200ms ease-in-out; + border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; + + background-color: ${({ theme }) => theme.inputBackground}; + transition: box-shadow 150ms ease-out; :focus-within { - box-shadow: 0 0 1px 1px ${({ theme }) => theme.malibuBlue}; + border: 1px solid ${({ theme }) => theme.malibuBlue}; } ` @@ -145,7 +149,6 @@ const ErrorSpan = styled.span` const TokenModal = styled.div` ${({ theme }) => theme.flexColumnNoWrap} - background-color: ${({ theme }) => theme.white}; width: 100%; ` diff --git a/src/components/ExchangePage/index.jsx b/src/components/ExchangePage/index.jsx index 8fdb242b77..d8d73a8d4a 100644 --- a/src/components/ExchangePage/index.jsx +++ b/src/components/ExchangePage/index.jsx @@ -10,8 +10,7 @@ import CurrencyInputPanel from '../CurrencyInputPanel' import AddressInputPanel from '../AddressInputPanel' import OversizedPanel from '../OversizedPanel' import TransactionDetails from '../TransactionDetails' -import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' -import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg' +import ArrowDown from '../../assets/svg/SVGArrowDown' import { amountFormatter, calculateGasMargin } from '../../utils' import { useExchangeContract } from '../../hooks' import { useTokenDetails } from '../../contexts/Tokens' @@ -42,7 +41,9 @@ const DownArrowBackground = styled.div` align-items: center; ` -const DownArrow = styled.img` +const WrappedArrowDown = ({ clickable, active, ...rest }) => +const DownArrow = styled(WrappedArrowDown)` + color: ${({ theme, active }) => (active ? theme.royalBlue : theme.chaliceGray)}; width: 0.625rem; height: 0.625rem; position: relative; @@ -61,7 +62,7 @@ const ExchangeRateWrapper = styled.div` const ExchangeRate = styled.span` flex: 1 1 auto; width: 0; - color: ${({ theme }) => theme.chaliceGray}; + color: ${({ theme }) => theme.doveGray}; ` const Flex = styled.div` @@ -619,7 +620,7 @@ export default function ExchangePage({ initialCurrency, sending }) { }} clickable alt="swap" - src={isValid ? ArrowDownBlue : ArrowDownGrey} + active={isValid} /> @@ -643,7 +644,7 @@ export default function ExchangePage({ initialCurrency, sending }) { <> - + diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 7d595a76e2..0fb4dc6237 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -4,6 +4,7 @@ import styled from 'styled-components' import { Link } from '../../theme' import Web3Status from '../Web3Status' import { darken } from 'polished' +import { useDarkModeManager } from '../../contexts/LocalStorage' const HeaderElement = styled.div` margin: 1.25rem; @@ -15,9 +16,19 @@ const Title = styled.div` display: flex; align-items: center; + :hover { + cursor: pointer; + } + #image { font-size: 1.5rem; margin-right: 1rem; + transform: rotate(0deg); + transition: transform 150ms ease-out; + + :hover { + transform: rotate(-10deg); + } } #link { @@ -36,11 +47,13 @@ const Title = styled.div` ` export default function Header() { + const [, toggleDarkMode] = useDarkModeManager() + return ( <> - <span id="image" role="img" aria-label="Unicorn Emoji"> + <span onClick={toggleDarkMode} id="image" role="img" aria-label="Unicorn Emoji"> 🦄 </span> diff --git a/src/components/Modal/index.js b/src/components/Modal/index.js index c819da6c62..2b5bb00a8c 100644 --- a/src/components/Modal/index.js +++ b/src/components/Modal/index.js @@ -21,8 +21,10 @@ const FilteredDialogContent = ({ minHeight, ...rest }) => <DialogContent {...res const StyledDialogContent = styled(FilteredDialogContent)` &[data-reach-dialog-content] { margin: 0 0 2rem 0; - ${({ theme }) => theme.mediaWidth.upToMedium`margin: 0;`} - padding: 0; + border: 1px solid ${({ theme }) => theme.concreteGray}; + background-color: ${({ theme }) => theme.inputBackground}; + ${({ theme }) => theme.mediaWidth.upToMedium`margin: 0;`}; + padding: 0px; width: 50vw; max-width: 650px; ${({ theme }) => theme.mediaWidth.upToMedium`width: 65vw;`} @@ -51,7 +53,7 @@ const HiddenCloseButton = styled.button` export default function Modal({ isOpen, onDismiss, minHeight = false, initialFocusRef, children }) { const transitions = useTransition(isOpen, null, { - config: { duration: 125 }, + config: { duration: 150 }, from: { opacity: 0 }, enter: { opacity: 1 }, leave: { opacity: 0 } diff --git a/src/components/NavigationTabs/index.js b/src/components/NavigationTabs/index.js index b9e16677a7..028ffa5f42 100644 --- a/src/components/NavigationTabs/index.js +++ b/src/components/NavigationTabs/index.js @@ -60,7 +60,7 @@ const Tabs = styled.div` height: 2.5rem; background-color: ${({ theme }) => theme.concreteGray}; border-radius: 3rem; - box-shadow: 0 0 0 1px ${({ theme }) => darken(0.05, theme.concreteGray)}; + /* border: 1px solid ${({ theme }) => theme.mercuryGray}; */ margin-bottom: 1rem; ` @@ -73,6 +73,7 @@ const StyledNavLink = styled(NavLink).attrs({ align-items: center; justify-content: center; height: 2.5rem; + border: 1px solid ${({ theme }) => transparentize(1, theme.mercuryGray)}; flex: 1 0 auto; border-radius: 3rem; outline: none; @@ -80,21 +81,24 @@ const StyledNavLink = styled(NavLink).attrs({ text-decoration: none; color: ${({ theme }) => theme.doveGray}; font-size: 1rem; + box-sizing: border-box; &.${activeClassName} { - background-color: ${({ theme }) => theme.white}; + background-color: ${({ theme }) => theme.inputBackground}; border-radius: 3rem; - box-shadow: 0 0 1px 1px ${({ theme }) => theme.mercuryGray}; + border: 1px solid ${({ theme }) => theme.mercuryGray}; + box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.royalBlue)}; + box-sizing: border-box; font-weight: 500; color: ${({ theme }) => theme.royalBlue}; :hover { - box-shadow: 0 0 1px 1px ${({ theme }) => darken(0.1, theme.mercuryGray)}; + border: 1px solid ${({ theme }) => darken(0.1, theme.mercuryGray)}; + background-color: ${({ theme }) => darken(0.01, theme.inputBackground)}; } } :hover, :focus { - font-weight: 500; color: ${({ theme }) => darken(0.1, theme.royalBlue)}; } ` diff --git a/src/components/TransactionDetails/index.js b/src/components/TransactionDetails/index.js index d452ca7afe..c9f7d038e1 100644 --- a/src/components/TransactionDetails/index.js +++ b/src/components/TransactionDetails/index.js @@ -86,12 +86,12 @@ const Popup = styled(Flex)` align-items: center; padding: 0.6rem 1rem; line-height: 150%; - background: ${({ theme }) => theme.charcoalBlack}; + background: ${({ theme }) => theme.inputBackground}; border-radius: 8px; animation: ${fadeIn} 0.15s linear; - color: white; + color: ${({ theme }) => theme.textColor}; font-style: italic; ${({ theme }) => theme.mediaWidth.upToSmall` @@ -100,6 +100,7 @@ const Popup = styled(Flex)` ` const FancyButton = styled.button` + color: ${({ theme }) => theme.textColor}; align-items: center; min-width: 55px; height: 2rem; @@ -107,7 +108,7 @@ const FancyButton = styled.button` font-size: 12px; border: 1px solid ${({ theme }) => theme.mercuryGray}; outline: none; - background: ${({ theme }) => theme.white}; + background: ${({ theme }) => theme.inputBackground}; :hover { cursor: inherit; @@ -115,7 +116,6 @@ const FancyButton = styled.button` } :focus { border: 1px solid ${({ theme }) => theme.royalBlue}; - /* box-shadow: 0 0 1px 1px #5CA2FF; */ } ` @@ -164,8 +164,9 @@ const OptionLarge = styled(Option)` ` const Input = styled.input` - background: ${({ theme }) => theme.white}; + background: ${({ theme }) => theme.inputBackground}; flex-grow: 1; + font-size: 12px; outline: none; box-sizing: border-box; @@ -191,6 +192,7 @@ const Input = styled.input` placeholder !== 'Custom' && css` text-align: right; + color: ${({ theme }) => theme.textColor}; `} ${({ color }) => @@ -479,7 +481,7 @@ export default function TransactionDetails(props) { : '' } > - {activeIndex === 4 && warningType.toString() === 'none' && 'Custom slippage value entered'} + {activeIndex === 4 && warningType.toString() === 'none' && 'Custom slippage value'} {warningType === WARNING_TYPE.emptyInput && 'Enter a slippage percentage'} {warningType === WARNING_TYPE.invalidEntryBound && 'Please select a value no greater than 50%'} {warningType === WARNING_TYPE.riskyEntryHigh && 'Your transaction may be frontrun'} @@ -617,7 +619,6 @@ export default function TransactionDetails(props) { )} ${props.outputSymbol}` )} </ValueWrapper> - . </div> <LastSummaryText> {t('priceChange')} <ValueWrapper>{b(`${props.percentSlippageFormatted}%`)}</ValueWrapper>. @@ -638,7 +639,7 @@ export default function TransactionDetails(props) { )} ${props.outputSymbol}` )} </ValueWrapper>{' '} - {t('to')} {b(props.recipientAddress)}. + {t('to')} {b(props.recipientAddress)} </div> <LastSummaryText> {t('itWillCost')}{' '} @@ -669,7 +670,6 @@ export default function TransactionDetails(props) { )} ${props.outputSymbol}` )} </ValueWrapper> - . </div> <LastSummaryText> {t('itWillCost')}{' '} diff --git a/src/components/Web3Status/index.js b/src/components/Web3Status/index.js index 9b8a9c00ea..06f7510afa 100644 --- a/src/components/Web3Status/index.js +++ b/src/components/Web3Status/index.js @@ -32,8 +32,8 @@ const Web3StatusGeneric = styled.button` ` const Web3StatusError = styled(Web3StatusGeneric)` background-color: ${({ theme }) => theme.salmonRed}; - color: ${({ theme }) => theme.white}; border: 1px solid ${({ theme }) => theme.salmonRed}; + color: ${({ theme }) => theme.white}; font-weight: 500; :hover, :focus { @@ -43,9 +43,10 @@ const Web3StatusError = styled(Web3StatusGeneric)` const Web3StatusConnect = styled(Web3StatusGeneric)` background-color: ${({ theme }) => theme.royalBlue}; - color: ${({ theme }) => theme.white}; border: 1px solid ${({ theme }) => theme.royalBlue}; + color: ${({ theme }) => theme.white}; font-weight: 500; + :hover, :focus { background-color: ${({ theme }) => darken(0.1, theme.royalBlue)}; @@ -53,9 +54,9 @@ const Web3StatusConnect = styled(Web3StatusGeneric)` ` const Web3StatusConnected = styled(Web3StatusGeneric)` - background-color: ${({ pending, theme }) => (pending ? theme.zumthorBlue : theme.white)}; - color: ${({ pending, theme }) => (pending ? theme.royalBlue : theme.doveGray)}; + background-color: ${({ pending, theme }) => (pending ? theme.zumthorBlue : theme.inputBackground)}; border: 1px solid ${({ pending, theme }) => (pending ? theme.royalBlue : theme.mercuryGray)}; + color: ${({ pending, theme }) => (pending ? theme.royalBlue : theme.doveGray)}; font-weight: 400; :hover { background-color: ${({ pending, theme }) => @@ -72,7 +73,6 @@ const Text = styled.p` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - margin: 0 0.5rem 0 0.25rem; font-size: 0.83rem; ` diff --git a/src/contexts/LocalStorage.js b/src/contexts/LocalStorage.js index cc5a3e1e3d..a7f04ce531 100644 --- a/src/contexts/LocalStorage.js +++ b/src/contexts/LocalStorage.js @@ -7,7 +7,8 @@ const CURRENT_VERSION = 0 const LAST_SAVED = 'LAST_SAVED' const BETA_MESSAGE_DISMISSED = 'BETA_MESSAGE_DISMISSED' -const UPDATABLE_KEYS = [BETA_MESSAGE_DISMISSED] +const DARK_MODE = 'DARK_MODE' +const UPDATABLE_KEYS = [BETA_MESSAGE_DISMISSED, DARK_MODE] const UPDATE_KEY = 'UPDATE_KEY' @@ -39,7 +40,8 @@ function reducer(state, { type, payload }) { function init() { const defaultLocalStorage = { [VERSION]: CURRENT_VERSION, - [BETA_MESSAGE_DISMISSED]: false + [BETA_MESSAGE_DISMISSED]: false, + [DARK_MODE]: false } try { @@ -48,7 +50,7 @@ function init() { // this is where we could run migration logic return defaultLocalStorage } else { - return parsed + return { ...defaultLocalStorage, ...parsed } } } catch { return defaultLocalStorage @@ -88,3 +90,15 @@ export function useBetaMessageManager() { return [!state[BETA_MESSAGE_DISMISSED], dismissBetaMessage] } + +export function useDarkModeManager() { + const [state, { updateKey }] = useLocalStorageContext() + + const isDarkMode = state[DARK_MODE] + + const toggleDarkMode = useCallback(() => { + updateKey(DARK_MODE, !isDarkMode) + }, [updateKey, isDarkMode]) + + return [state[DARK_MODE], toggleDarkMode] +} diff --git a/src/index.js b/src/index.js index 62bb4beec7..9192ff53c8 100644 --- a/src/index.js +++ b/src/index.js @@ -55,16 +55,16 @@ function Updaters() { } ReactDOM.render( - <ThemeProvider> - <> - <GlobalStyle /> - <Web3Provider connectors={connectors} libraryName="ethers.js"> - <ContextProviders> - <Updaters /> + <Web3Provider connectors={connectors} libraryName="ethers.js"> + <ContextProviders> + <Updaters /> + <ThemeProvider> + <> + <GlobalStyle /> <App /> - </ContextProviders> - </Web3Provider> - </> - </ThemeProvider>, + </> + </ThemeProvider> + </ContextProviders> + </Web3Provider>, document.getElementById('root') ) diff --git a/src/pages/Pool/AddLiquidity.js b/src/pages/Pool/AddLiquidity.js index 41b2821ad6..8752098a97 100644 --- a/src/pages/Pool/AddLiquidity.js +++ b/src/pages/Pool/AddLiquidity.js @@ -9,8 +9,8 @@ import { Button } from '../../theme' import CurrencyInputPanel from '../../components/CurrencyInputPanel' import OversizedPanel from '../../components/OversizedPanel' import ContextualInfo from '../../components/ContextualInfo' -import PlusBlue from '../../assets/images/plus-blue.svg' -import PlusGrey from '../../assets/images/plus-grey.svg' +import { ReactComponent as Plus } from '../../assets/images/plus-blue.svg' + import { useExchangeContract } from '../../hooks' import { amountFormatter, calculateGasMargin } from '../../utils' import { useTransactionAdder } from '../../contexts/Transactions' @@ -63,14 +63,6 @@ const DownArrowBackground = styled.div` justify-content: center; align-items: center; ` - -const DownArrow = styled.img` - width: 0.625rem; - height: 0.625rem; - position: relative; - padding: 0.875rem; -` - const SummaryPanel = styled.div` ${({ theme }) => theme.flexColumnNoWrap} padding: 1rem 0; @@ -87,7 +79,7 @@ const ExchangeRateWrapper = styled.div` const ExchangeRate = styled.span` flex: 1 1 auto; width: 0; - color: ${({ theme }) => theme.chaliceGray}; + color: ${({ theme }) => theme.doveGray}; ` const Flex = styled.div` @@ -100,6 +92,17 @@ const Flex = styled.div` } ` +const WrappedPlus = ({ isError, highSlippageWarning, ...rest }) => <Plus {...rest} /> +const ColoredWrappedPlus = styled(WrappedPlus)` + width: 0.625rem; + height: 0.625rem; + position: relative; + padding: 0.875rem; + path { + stroke: ${({ active, theme }) => (active ? theme.royalBlue : theme.chaliceGray)}; + } +` + function calculateSlippageBounds(value) { if (value) { const offset = value.mul(ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000)) @@ -558,7 +561,7 @@ export default function AddLiquidity() { /> <OversizedPanel> <DownArrowBackground> - <DownArrow src={isActive ? PlusBlue : PlusGrey} alt="plus" /> + <ColoredWrappedPlus active={isActive} alt="plus" /> </DownArrowBackground> </OversizedPanel> <CurrencyInputPanel diff --git a/src/pages/Pool/CreateExchange.js b/src/pages/Pool/CreateExchange.js index 903e664a80..c768576d53 100644 --- a/src/pages/Pool/CreateExchange.js +++ b/src/pages/Pool/CreateExchange.js @@ -29,7 +29,7 @@ const ExchangeRateWrapper = styled.div` const ExchangeRate = styled.span` flex: 1 1 auto; width: 0; - color: ${({ theme }) => theme.chaliceGray}; + color: ${({ theme }) => theme.doveGray}; ` const CreateExchangeWrapper = styled.div` diff --git a/src/pages/Pool/ModeSelector.js b/src/pages/Pool/ModeSelector.js index 20f12567d4..c43ef6c8c0 100644 --- a/src/pages/Pool/ModeSelector.js +++ b/src/pages/Pool/ModeSelector.js @@ -4,10 +4,13 @@ import { useTranslation } from 'react-i18next' import styled from 'styled-components' import OversizedPanel from '../../components/OversizedPanel' -import Dropdown from '../../assets/images/dropdown-blue.svg' +import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg' + import Modal from '../../components/Modal' import { useBodyKeyDown } from '../../hooks' +import { lighten } from 'polished' + const poolTabOrder = [ { path: '/add-liquidity', @@ -29,13 +32,16 @@ const poolTabOrder = [ const LiquidityContainer = styled.div` ${({ theme }) => theme.flexRowNoWrap}; align-items: center; - font-size: 0.75rem; - padding: 0.625rem 1rem; - font-size: 0.75rem; + padding: 1rem 1rem; + font-size: 1rem; color: ${({ theme }) => theme.royalBlue}; font-weight: 500; cursor: pointer; + :hover { + color: ${({ theme }) => lighten(0.1, theme.royalBlue)}; + } + img { height: 0.75rem; width: 0.75rem; @@ -62,21 +68,28 @@ const StyledNavLink = styled(NavLink).attrs({ font-size: 1rem; &.${activeClassName} { - background-color: ${({ theme }) => theme.white}; + background-color: ${({ theme }) => theme.inputBackground}; border-radius: 3rem; - box-shadow: 0 0 1px 1px ${({ theme }) => theme.mercuryGray}; + border: 1px solid ${({ theme }) => theme.mercuryGray}; font-weight: 500; color: ${({ theme }) => theme.royalBlue}; } ` const PoolModal = styled.div` - background-color: ${({ theme }) => theme.white}; + background-color: ${({ theme }) => theme.inputBackground}; width: 100%; height: 100%; padding: 2rem 0 2rem 0; ` +const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} /> +const ColoredDropdown = styled(WrappedDropdown)` + path { + stroke: ${({ theme }) => theme.royalBlue}; + } +` + function ModeSelector({ location: { pathname }, history }) { const { t } = useTranslation() @@ -109,7 +122,7 @@ function ModeSelector({ location: { pathname }, history }) { }} > <LiquidityLabel>{t(activeTabKey)}</LiquidityLabel> - <img src={Dropdown} alt="dropdown" /> + <ColoredDropdown alt="arrow down" /> </LiquidityContainer> <Modal isOpen={modalIsOpen} diff --git a/src/pages/Pool/RemoveLiquidity.js b/src/pages/Pool/RemoveLiquidity.js index 00d0b682e5..5f0236c1a4 100644 --- a/src/pages/Pool/RemoveLiquidity.js +++ b/src/pages/Pool/RemoveLiquidity.js @@ -9,8 +9,8 @@ import { Button } from '../../theme' import CurrencyInputPanel from '../../components/CurrencyInputPanel' import ContextualInfo from '../../components/ContextualInfo' import OversizedPanel from '../../components/OversizedPanel' -import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' -import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg' +import ArrowDown from '../../assets/svg/SVGArrowDown' + import { useExchangeContract } from '../../hooks' import { useTransactionAdder } from '../../contexts/Transactions' import { useTokenDetails } from '../../contexts/Tokens' @@ -36,7 +36,9 @@ const DownArrowBackground = styled.div` align-items: center; ` -const DownArrow = styled.img` +const DownArrow = styled(ArrowDown)` + ${({ theme }) => theme.flexRowNoWrap} + color: ${({ theme, active }) => (active ? theme.royalBlue : theme.doveGray)}; width: 0.625rem; height: 0.625rem; position: relative; @@ -80,7 +82,7 @@ const ExchangeRateWrapper = styled.div` const ExchangeRate = styled.span` flex: 1 1 auto; width: 0; - color: ${({ theme }) => theme.chaliceGray}; + color: ${({ theme }) => theme.doveGray}; ` const Flex = styled.div` @@ -347,7 +349,7 @@ export default function RemoveLiquidity() { /> <OversizedPanel> <DownArrowBackground> - <DownArrow src={isActive ? ArrowDownBlue : ArrowDownGrey} alt="arrow" /> + <DownArrow active={isActive} alt="arrow" /> </DownArrowBackground> </OversizedPanel> <CurrencyInputPanel diff --git a/src/theme/components.js b/src/theme/components.js index 4dcb2ebb2d..1f5ae1c542 100644 --- a/src/theme/components.js +++ b/src/theme/components.js @@ -12,8 +12,8 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({ border: none; outline: none; background-color: ${({ backgroundColor }) => backgroundColor}; + transition: background-color 150ms ease-out; color: ${({ theme }) => theme.white}; - transition: background-color 125ms ease-in-out; width: 100%; :hover, @@ -26,7 +26,8 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({ } :disabled { - background-color: ${({ theme }) => theme.mercuryGray}; + background-color: ${({ theme }) => theme.concreteGray}; + color: ${({ theme }) => theme.silverGray}; cursor: auto; } ` @@ -50,12 +51,13 @@ export const Link = styled.a.attrs({ ` export const BorderlessInput = styled.input` - color: ${({ theme }) => theme.mineshaftGray}; + color: ${({ theme }) => theme.textColor}; font-size: 1rem; outline: none; border: none; flex: 1 1 auto; width: 0; + background-color: ${({ theme }) => theme.inputBackground}; [type='number'] { -moz-appearance: textfield; @@ -67,7 +69,7 @@ export const BorderlessInput = styled.input` } ::placeholder { - color: ${({ theme }) => theme.mercuryGray}; + color: ${({ theme }) => theme.placeholderGray}; } ` diff --git a/src/theme/index.js b/src/theme/index.js index 6045ba5cf0..9fbcc88a18 100644 --- a/src/theme/index.js +++ b/src/theme/index.js @@ -1,5 +1,6 @@ import React from 'react' import { ThemeProvider as StyledComponentsThemeProvider, createGlobalStyle, css } from 'styled-components' +import { useDarkModeManager } from '../contexts/LocalStorage' export * from './components' @@ -28,23 +29,34 @@ const flexRowNoWrap = css` flex-flow: row nowrap; ` -const theme = { - white: '#FFFFFF', - black: '#000000', +const white = '#FFFFFF' +const black = '#000000' + +const theme = darkMode => ({ + white, + black, + textColor: darkMode ? white : '#010101', + + // for setting css on <html> + backgroundColor: darkMode ? '#333639' : white, + + inputBackground: darkMode ? '#202124' : white, + placeholderGray: darkMode ? '#5F5F5F' : '#E1E1E1', + // grays - concreteGray: '#FAFAFA', - mercuryGray: '#E1E1E1', - silverGray: '#C4C4C4', - chaliceGray: '#AEAEAE', - doveGray: '#737373', - mineshaftGray: '#2B2B2B', - buttonOutlineGrey: '#f2f2f2', + concreteGray: darkMode ? '#292C2F' : '#FAFAFA', + mercuryGray: darkMode ? '#333333' : '#E1E1E1', + silverGray: darkMode ? '#737373' : '#C4C4C4', + chaliceGray: darkMode ? '#7B7B7B' : '#AEAEAE', + doveGray: darkMode ? '#C4C4C4' : '#737373', + mineshaftGray: darkMode ? '#E1E1E1' : '#2B2B2B', + buttonOutlineGrey: darkMode ? '#FAFAFA' : '#F2F2F2', //blacks - charcoalBlack: '#404040', + charcoalBlack: darkMode ? '#F2F2F2' : '#404040', // blues - zumthorBlue: '#EBF4FF', - malibuBlue: '#5CA2FF', - royalBlue: '#2F80ED', + zumthorBlue: darkMode ? '#212529' : '#EBF4FF', + malibuBlue: darkMode ? '#E67AEF' : '#5CA2FF', + royalBlue: darkMode ? '#DC6BE5' : '#2F80ED', // purples wisteriaPurple: '#DC6BE5', // reds @@ -62,10 +74,12 @@ const theme = { // css snippets flexColumnNoWrap, flexRowNoWrap -} +}) export default function ThemeProvider({ children }) { - return <StyledComponentsThemeProvider theme={theme}>{children}</StyledComponentsThemeProvider> + const [darkMode] = useDarkModeManager() + + return <StyledComponentsThemeProvider theme={theme(darkMode)}>{children}</StyledComponentsThemeProvider> } export const GlobalStyle = createGlobalStyle` @@ -79,20 +93,16 @@ export const GlobalStyle = createGlobalStyle` body { margin: 0; padding: 0; + } + + html { font-size: 16px; font-variant: none; + color: ${({ theme }) => theme.textColor}; + background-color: ${({ theme }) => theme.backgroundColor}; + transition: color 150ms ease-out, background-color 150ms ease-out; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - - #root { - ${({ theme }) => theme.flexColumnNoWrap} - justify-content: center; - align-items: center; - width: 100vw; - height: 100vh; - overflow-x: hidden; - overflow-y: auto; - } `