diff --git a/packages/apps-electron/src/index.tsx b/packages/apps-electron/src/index.tsx index 16e81c514f..63bd8a298e 100644 --- a/packages/apps-electron/src/index.tsx +++ b/packages/apps-electron/src/index.tsx @@ -6,50 +6,23 @@ import '@polkadot/apps/initSettings'; import 'semantic-ui-css/semantic.min.css'; import '@polkadot/react-components/i18n'; -import React, { Suspense } from 'react'; +import React from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter } from 'react-router-dom'; -import { ThemeProvider } from 'styled-components'; -import { Api } from '@polkadot/react-api'; -import Apps from '@polkadot/apps/Apps'; -import WindowDimensions from '@polkadot/apps/WindowDimensions'; -import Queue from '@polkadot/react-components/Status/Queue'; -import { BlockAuthors, Events } from '@polkadot/react-query'; -import settings from '@polkadot/ui-settings'; +import Root from '@polkadot/apps/Root'; import { electronMainApi } from './api/global-exported-api'; import { RemoteElectronStore } from './renderer/remote-electron-store'; const rootId = 'root'; const rootElement = document.getElementById(rootId); -const theme = { theme: settings.uiTheme }; - -const store = new RemoteElectronStore(electronMainApi.accountStore); if (!rootElement) { throw new Error(`Unable to find element with id '${rootId}'`); } +const store = new RemoteElectronStore(electronMainApi.accountStore); + ReactDOM.render( - - - - - - - - - - - - - - - - - , + , rootElement ); diff --git a/packages/apps/src/Apps.tsx b/packages/apps/src/Apps.tsx index deb23c79d2..c16cfdbe94 100644 --- a/packages/apps/src/Apps.tsx +++ b/packages/apps/src/Apps.tsx @@ -1,10 +1,10 @@ // Copyright 2017-2020 @polkadot/apps authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { BareProps as Props } from '@polkadot/react-components/types'; +import { BareProps as Props, ThemeDef, ThemeProps } from '@polkadot/react-components/types'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; +import React, { useContext, useMemo } from 'react'; +import styled, { ThemeContext } from 'styled-components'; import AccountSidebar from '@polkadot/app-accounts/Sidebar'; import { getSystemChainColor } from '@polkadot/apps-config/ui'; import GlobalStyle from '@polkadot/react-components/styles'; @@ -19,6 +19,7 @@ import WarmUp from './WarmUp'; export const PORTAL_ID = 'portals'; function Apps ({ className = '' }: Props): React.ReactElement { + const { theme } = useContext(ThemeContext); const { systemChain, systemName } = useApi(); const uiHighlight = useMemo( @@ -29,7 +30,7 @@ function Apps ({ className = '' }: Props): React.ReactElement { return ( <> -
+
@@ -44,9 +45,10 @@ function Apps ({ className = '' }: Props): React.ReactElement { ); } -export default React.memo(styled(Apps)` +export default React.memo(styled(Apps)(({ theme }: ThemeProps) => ` + background: ${theme.bgPage}; box-sizing: border-box; display: flex; flex-direction: column; min-height: 100vh; -`); +`)); diff --git a/packages/apps/src/Endpoints/Group.tsx b/packages/apps/src/Endpoints/Group.tsx index 61eb42c25a..139ba1e29e 100644 --- a/packages/apps/src/Endpoints/Group.tsx +++ b/packages/apps/src/Endpoints/Group.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/apps authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '@polkadot/react-components/types'; import { Group } from './types'; import React, { useCallback } from 'react'; @@ -54,7 +55,7 @@ function GroupDisplay ({ apiUrl, children, className = '', index, isSelected, se ); } -export default React.memo(styled(GroupDisplay)` +export default React.memo(styled(GroupDisplay)(({ theme }: ThemeProps) => ` .groupHeader { border-radius: 0.25rem; cursor: pointer; @@ -64,7 +65,7 @@ export default React.memo(styled(GroupDisplay)` text-transform: uppercase; &:hover { - background: white; + background: ${theme.bgTable}; } .ui--Icon { @@ -75,4 +76,4 @@ export default React.memo(styled(GroupDisplay)` .groupNetworks { padding: 0.25rem 0 0.5rem 1rem; } -`); +`)); diff --git a/packages/apps/src/Endpoints/Network.tsx b/packages/apps/src/Endpoints/Network.tsx index d0c55e6a10..85b1d9b340 100644 --- a/packages/apps/src/Endpoints/Network.tsx +++ b/packages/apps/src/Endpoints/Network.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/apps authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '@polkadot/react-components/types'; import { Network } from './types'; import React, { useCallback, useMemo } from 'react'; @@ -52,7 +53,7 @@ function NetworkDisplay ({ apiUrl, className = '', setApiUrl, value: { icon, isC ); } -export default React.memo(styled(NetworkDisplay)` +export default React.memo(styled(NetworkDisplay)(({ theme }: ThemeProps) => ` border-left: 0.25rem solid transparent; border-radius: 0.25rem; cursor: pointer; @@ -62,7 +63,7 @@ export default React.memo(styled(NetworkDisplay)` &.isSelected, &:hover { - background: #fffefd; + background: ${theme.bgTable}; } .endpointIcon { @@ -85,4 +86,4 @@ export default React.memo(styled(NetworkDisplay)` margin-top: -0.125rem; } } -`); +`)); diff --git a/packages/apps/src/Endpoints/index.tsx b/packages/apps/src/Endpoints/index.tsx index 494179ddc2..45510c6c62 100644 --- a/packages/apps/src/Endpoints/index.tsx +++ b/packages/apps/src/Endpoints/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { LinkOption } from '@polkadot/apps-config/settings/endpoints'; +import { ThemeProps } from '@polkadot/react-components/types'; import { Group } from './types'; import React, { useCallback, useMemo, useState } from 'react'; @@ -250,8 +251,8 @@ function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElem ); } -export default React.memo(styled(Endpoints)` - color: #4e4e4e; +export default React.memo(styled(Endpoints)(({ theme }: ThemeProps) => ` + color: ${theme.color}; padding-top: 3.5rem; .customButton { @@ -269,4 +270,4 @@ export default React.memo(styled(Endpoints)` .endpointCustomWrapper { position: relative; } -`); +`)); diff --git a/packages/apps/src/Menu/Grouping.tsx b/packages/apps/src/Menu/Grouping.tsx index 884e066a68..50148cfab2 100644 --- a/packages/apps/src/Menu/Grouping.tsx +++ b/packages/apps/src/Menu/Grouping.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/apps authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '@polkadot/react-components/types'; import { Group } from './types'; import React from 'react'; @@ -28,7 +29,7 @@ function Grouping ({ className = '', name, routes }: Props): React.ReactElement< return (
  • -
    +
    {name}
    @@ -44,7 +45,7 @@ function Grouping ({ className = '', name, routes }: Props): React.ReactElement< ); } -export default React.memo(styled(Grouping)` +export default React.memo(styled(Grouping)(({ theme }: ThemeProps) => ` cursor: pointer; position: relative; @@ -79,8 +80,8 @@ export default React.memo(styled(Grouping)` &:hover { .groupHdr, .groupMenu li { - background: #fff; - color: #4e4e4e; + background: ${theme.bgMenu}; + color: ${theme.color}; } .groupHdr { @@ -91,8 +92,8 @@ export default React.memo(styled(Grouping)` display: block; > li:hover { - background: rgba(255, 255, 255, 0.5); + background: ${theme.bgMenuHover}; } } } -`); +`)); diff --git a/packages/apps/src/Menu/index.tsx b/packages/apps/src/Menu/index.tsx index 5ea550a6f7..a5f680f1c6 100644 --- a/packages/apps/src/Menu/index.tsx +++ b/packages/apps/src/Menu/index.tsx @@ -3,6 +3,7 @@ import { Route, Routes } from '@polkadot/apps-routing/types'; import { ApiProps } from '@polkadot/react-api/types'; +import { ThemeProps } from '@polkadot/react-components/types'; import { AccountId } from '@polkadot/types/interfaces'; import { Group, Groups, ItemRoute } from './types'; @@ -158,7 +159,7 @@ function Menu ({ className = '' }: Props): React.ReactElement { ); } -export default React.memo(styled(Menu)` +export default React.memo(styled(Menu)(({ theme }: ThemeProps) => ` align-items: center; display: flex; justify-content: space-between; @@ -168,12 +169,12 @@ export default React.memo(styled(Menu)` &.isLoading { background: #999 !important; - &:before { - filter: grayscale(1); + .menuActive { + background: ${theme.bgPage}; } - .menuActive { - background: #f5f3f1; + &:before { + filter: grayscale(1); } .menuItems { @@ -188,10 +189,10 @@ export default React.memo(styled(Menu)` } .menuActive { - background: #fff; + background: ${theme.bgTabs}; border-bottom: none; border-radius: 0.25rem 0.25rem 0 0; - color: #4e4e4e; + color: ${theme.color}; padding: 1rem 1.5rem; margin: 0 1rem -1px; z-index: 1; @@ -211,4 +212,4 @@ export default React.memo(styled(Menu)` display: inline-block; } } -`); +`)); diff --git a/packages/apps/src/Root.tsx b/packages/apps/src/Root.tsx new file mode 100644 index 0000000000..3d7448f576 --- /dev/null +++ b/packages/apps/src/Root.tsx @@ -0,0 +1,60 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ThemeDef } from '@polkadot/react-components/types'; +import { KeyringStore } from '@polkadot/ui-keyring/types'; + +import React, { Suspense, useEffect, useState } from 'react'; +import { ThemeProvider } from 'styled-components'; +import { HashRouter } from 'react-router-dom'; +import { Api } from '@polkadot/react-api'; +import Queue from '@polkadot/react-components/Status/Queue'; +import { BlockAuthors, Events } from '@polkadot/react-query'; +import settings from '@polkadot/ui-settings'; + +import Apps from './Apps'; +import WindowDimensions from './WindowDimensions'; +import { darkTheme, lightTheme } from './themes'; + +interface Props { + store?: KeyringStore; +} + +function createTheme ({ uiTheme }: { uiTheme: string }): ThemeDef { + return uiTheme === 'dark' + ? darkTheme + : lightTheme; +} + +function Root ({ store }: Props): React.ReactElement { + const [theme, setTheme] = useState(createTheme(settings)); + + useEffect((): void => { + settings.on('change', (settings) => setTheme(createTheme(settings))); + }, []); + + return ( + + + + + + + + + + + + + + + + + + ); +} + +export default React.memo(Root); diff --git a/packages/apps/src/index.tsx b/packages/apps/src/index.tsx index 48e9226090..42fb6fb393 100644 --- a/packages/apps/src/index.tsx +++ b/packages/apps/src/index.tsx @@ -6,43 +6,19 @@ import './initSettings'; import 'semantic-ui-css/semantic.min.css'; import '@polkadot/react-components/i18n'; -import React, { Suspense } from 'react'; +import React from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter } from 'react-router-dom'; -import { ThemeProvider } from 'styled-components'; -import { Api } from '@polkadot/react-api'; -import Queue from '@polkadot/react-components/Status/Queue'; -import { BlockAuthors, Events } from '@polkadot/react-query'; -import settings from '@polkadot/ui-settings'; -import Apps from './Apps'; -import WindowDimensions from './WindowDimensions'; +import Root from './Root'; const rootId = 'root'; const rootElement = document.getElementById(rootId); -const theme = { theme: settings.uiTheme }; if (!rootElement) { throw new Error(`Unable to find element with id '${rootId}'`); } ReactDOM.render( - - - - - - - - - - - - - - - - - , + , rootElement ); diff --git a/packages/apps/src/overlays/Base.tsx b/packages/apps/src/overlays/Base.tsx index f9f178ca56..88bdd377c4 100644 --- a/packages/apps/src/overlays/Base.tsx +++ b/packages/apps/src/overlays/Base.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/apps authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '@polkadot/react-components/types'; + import React from 'react'; import styled from 'styled-components'; import { IconName } from '@fortawesome/fontawesome-svg-core'; @@ -44,8 +46,8 @@ function BaseOverlay ({ children, className = '', icon, type }: Props): React.Re ); } -export default React.memo(styled(BaseOverlay)` - background: white; +export default React.memo(styled(BaseOverlay)(({ theme }: ThemeProps) => ` + background: ${theme.bgMenu}; border: 1px solid transparent; border-radius: 0.25rem; border-left-width: 0.25rem; @@ -111,4 +113,4 @@ export default React.memo(styled(BaseOverlay)` right: 0em; top: 0.75rem; } -`); +`)); diff --git a/packages/apps/src/themes.ts b/packages/apps/src/themes.ts new file mode 100644 index 0000000000..c34e52a6b0 --- /dev/null +++ b/packages/apps/src/themes.ts @@ -0,0 +1,40 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ThemeDef } from '@polkadot/react-components/types'; + +export const darkTheme: ThemeDef = { + bgInput: '#38393f', + bgInverse: 'rgba(64, 64, 64, 0.8)', + bgMenu: '#26272c', + bgMenuHover: 'rgba(255, 255, 255, 0.05)', + bgPage: '#26272c', + bgTable: '#38393f', + bgTabs: '#38393f', + bgToggle: '#58595f', + borderTable: 'transparent', + borderTabs: 'transparent', + color: 'rgba(254, 242, 240, 0.9)', + colorError: 'rgba(255, 0, 0, 0.85)', + colorLabel: 'rgba(254, 242, 240, 0.45)', + colorSummary: 'rgba(254, 242, 240, 0.75)', + theme: 'dark' +}; + +export const lightTheme: ThemeDef = { + bgInput: '#fff', + bgInverse: 'rgba(254, 242, 240, 0.91)', + bgMenu: '#fff', + bgMenuHover: 'rgba(255, 255, 255, 0.5)', + bgPage: '#f5f3f1', + bgTable: '#fff', + bgTabs: '#fff', + bgToggle: '#e4e5e6', + borderTable: '#eeecea', + borderTabs: '#eeecea', + color: '#4e4e4e', + colorError: 'rgba(139, 0, 0)', + colorLabel: 'rgba(78, 78, 78, 0.66)', + colorSummary: 'rgba(0, 0, 0, 0.6)', + theme: 'light' +}; diff --git a/packages/page-accounts/src/Accounts/Account.tsx b/packages/page-accounts/src/Accounts/Account.tsx index ebbd2164f9..52cd7eac2e 100644 --- a/packages/page-accounts/src/Accounts/Account.tsx +++ b/packages/page-accounts/src/Accounts/Account.tsx @@ -4,13 +4,14 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'; import { DeriveBalancesAll, DeriveDemocracyLock } from '@polkadot/api-derive/types'; import { ActionStatus } from '@polkadot/react-components/Status/types'; +import { ThemeDef } from '@polkadot/react-components/types'; import { ProxyDefinition, RecoveryConfig } from '@polkadot/types/interfaces'; import { KeyringAddress } from '@polkadot/ui-keyring/types'; import { Delegation } from '../types'; import BN from 'bn.js'; import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import styled from 'styled-components'; +import styled, { ThemeContext } from 'styled-components'; import { ApiPromise } from '@polkadot/api'; import { getLedger } from '@polkadot/react-api'; import { AddressInfo, AddressMini, AddressSmall, Badge, Button, ChainLock, CryptoType, Forget, Icon, IdentityIcon, LinkExternal, Menu, Popup, StatusContext, Tags } from '@polkadot/react-components'; @@ -78,6 +79,7 @@ const transformRecovery = { function Account ({ account: { address, meta }, className = '', delegation, filter, isFavorite, proxy, setBalance, toggleFavorite }: Props): React.ReactElement | null { const { t } = useTranslation(); + const { theme } = useContext(ThemeContext); const { queueExtrinsic } = useContext(StatusContext); const api = useApi(); const bestNumber = useCall(api.api.derive.chain.bestNumber); @@ -416,7 +418,7 @@ function Account ({ account: { address, meta }, className = '', delegation, filt /> )} | null { const { t } = useTranslation(); + const { theme } = useContext(ThemeContext); const api = useApi(); const info = useCall(api.api.derive.accounts.info, [address]); const balancesAll = useCall(api.api.derive.balances.all, [address]); @@ -203,7 +205,7 @@ function Address ({ address, className = '', filter, isFavorite, toggleFavorite /> )} ); } -export default React.memo(styled(CalendarApp)` +export default React.memo(styled(CalendarApp)(({ theme }: ThemeProps) => ` .calendarFlex { align-items: flex-start; display: flex; flex-wrap: nowrap; > div { - background-color: #fff; - border: 1px solid #eeecea; + background-color: ${theme.bgTable}; + border: 1px solid ${theme.borderTable}; border-radius: 0.25rem; &+div { @@ -169,4 +170,4 @@ export default React.memo(styled(CalendarApp)` } } } -`); +`)); diff --git a/packages/page-claims/src/Statement.tsx b/packages/page-claims/src/Statement.tsx index ee7ce15dd7..f88e6666cd 100644 --- a/packages/page-claims/src/Statement.tsx +++ b/packages/page-claims/src/Statement.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/app-claims authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '@polkadot/react-components/types'; import { StatementKind } from '@polkadot/types/interfaces'; import React from 'react'; @@ -56,7 +57,7 @@ function Statement ({ className, kind, systemChain }: Props): React.ReactElement ); } -export default React.memo(styled(Statement)` +export default React.memo(styled(Statement)(({ theme }: ThemeProps) => ` .statement{ border: 1px solid #c2c2c2; background: #f2f2f2; @@ -67,7 +68,7 @@ export default React.memo(styled(Statement)` white-space: normal; p { - color: #4e4e4e !important; + color: ${theme.color} !important; } iframe { @@ -80,4 +81,4 @@ export default React.memo(styled(Statement)` .statementUrl{ margin-left: 0.3rem } -`); +`)); diff --git a/packages/page-settings/src/General.tsx b/packages/page-settings/src/General.tsx index cf748efa08..b92d00e1d3 100644 --- a/packages/page-settings/src/General.tsx +++ b/packages/page-settings/src/General.tsx @@ -17,12 +17,17 @@ interface Props { } const ledgerConnOptions = uiSettings.availableLedgerConn; +const isLocalDev = window.location.host === 'localhost:3000'; function General ({ className = '' }: Props): React.ReactElement { const { t } = useTranslation(); // tri-state: null = nothing changed, false = no reload, true = reload required const [changed, setChanged] = useState(null); - const [settings, setSettings] = useState(uiSettings.get()); + const [settings, setSettings] = useState((): SettingsStruct => { + const settings = uiSettings.get(); + + return { ...settings, uiTheme: settings.uiTheme === 'dark' ? 'dark' : 'light' }; + }); const iconOptions = useMemo( () => uiSettings.availableIcons @@ -36,6 +41,14 @@ function General ({ className = '' }: Props): React.ReactElement { [t] ); + const themeOptions = useMemo( + () => [ + { text: t('Light theme (default)'), value: 'light' }, + { text: t('Dark theme (experimental, work-in-progress)'), value: 'dark' } + ], + [t] + ); + const translateLanguages = useMemo( () => createLanguages(t), [t] @@ -54,7 +67,7 @@ function General ({ className = '' }: Props): React.ReactElement { }, [settings]); const _handleChange = useCallback( - (key: keyof SettingsStruct) => (value: T): void => + (key: keyof SettingsStruct) => (value: T) => setSettings((settings) => ({ ...settings, [key]: value })), [] ); @@ -72,7 +85,7 @@ function General ({ className = '' }: Props): React.ReactElement { [settings] ); - const { i18nLang, icon, ledgerConn, prefix } = settings; + const { i18nLang, icon, ledgerConn, prefix, uiTheme } = settings; return (
    @@ -105,6 +118,16 @@ function General ({ className = '' }: Props): React.ReactElement { />
    )} + {isLocalDev && ( +
    + ('default interface theme')} + onChange={_handleChange('uiTheme')} + options={themeOptions} + /> +
    + )}
    ` .staking--hidden { display: none; } @@ -204,7 +204,7 @@ export default React.memo(styled(StakingApp)` .ui--Expander.stakeOver { .ui--Expander-summary { - color: darkred; + color: ${theme.colorError}; } } -`); +`)); diff --git a/packages/react-api/src/Api.tsx b/packages/react-api/src/Api.tsx index bc8289b49f..9c5746a87d 100644 --- a/packages/react-api/src/Api.tsx +++ b/packages/react-api/src/Api.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { InjectedExtension } from '@polkadot/extension-inject/types'; +import { KeyringStore } from '@polkadot/ui-keyring/types'; import { ChainProperties, ChainType } from '@polkadot/types/interfaces'; import { ApiProps, ApiState } from './types'; @@ -16,7 +17,6 @@ import { WsProvider } from '@polkadot/rpc-provider'; import { StatusContext } from '@polkadot/react-components/Status'; import { TokenUnit } from '@polkadot/react-components/InputNumber'; import keyring from '@polkadot/ui-keyring'; -import { KeyringStore } from '@polkadot/ui-keyring/types'; import uiSettings from '@polkadot/ui-settings'; import ApiSigner from '@polkadot/react-signer/signers/ApiSigner'; diff --git a/packages/react-components/src/Button/Button.tsx b/packages/react-components/src/Button/Button.tsx index cbd32441ba..69abbd03a9 100644 --- a/packages/react-components/src/Button/Button.tsx +++ b/packages/react-components/src/Button/Button.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; import { ButtonProps } from './types'; import React, { useCallback } from 'react'; @@ -36,7 +37,7 @@ function Button ({ children, className = '', icon, isBasic, isBusy, isCircular, const ICON_PADDING = 0.5; -export default React.memo(styled(Button)` +export default React.memo(styled(Button)(({ theme }: ThemeProps) => ` background: transparent; border: none; color: inherit; @@ -73,7 +74,7 @@ export default React.memo(styled(Button)` } &.isBasic { - background: #fff; + background: ${theme.bgTable}; } &.isCircular { @@ -131,4 +132,4 @@ export default React.memo(styled(Button)` &.isDisabled { color: #bcbbba; } -`); +`)); diff --git a/packages/react-components/src/CardSummary.tsx b/packages/react-components/src/CardSummary.tsx index f842fca076..947836362c 100644 --- a/packages/react-components/src/CardSummary.tsx +++ b/packages/react-components/src/CardSummary.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from './types'; + import BN from 'bn.js'; import React from 'react'; import styled from 'styled-components'; @@ -91,12 +93,12 @@ function CardSummary ({ children, className = '', help, label, progress }: Props ); } -export default React.memo(styled(CardSummary)` +export default React.memo(styled(CardSummary)(({ theme }: ThemeProps) => ` align-items: center; background: transparent !important; border: none !important; box-shadow: none !important; - color: rgba(0, 0, 0, 0.6); + color: ${theme.colorSummary}; display: flex; flex: 0 1 auto; flex-flow: row wrap; @@ -152,4 +154,4 @@ export default React.memo(styled(CardSummary)` font-size: 1.4rem; } } -`); +`)); diff --git a/packages/react-components/src/HelpOverlay.tsx b/packages/react-components/src/HelpOverlay.tsx index d97c27c239..eaba33a4be 100644 --- a/packages/react-components/src/HelpOverlay.tsx +++ b/packages/react-components/src/HelpOverlay.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from './types'; + import React from 'react'; import ReactMd from 'react-markdown'; import styled from 'styled-components'; @@ -17,7 +19,7 @@ function HelpOverlay ({ className = '', md }: Props): React.ReactElement const [isVisible, toggleVisible] = useToggle(); return ( -
    +
    ); } -export default React.memo(styled(HelpOverlay)` +export default React.memo(styled(HelpOverlay)(({ theme }: ThemeProps) => ` .help-button { + color: ${theme.color}; cursor: pointer; font-size: 2rem; padding: 1rem 1.5rem 0 0; @@ -79,4 +82,4 @@ export default React.memo(styled(HelpOverlay)` right: 0; } } -`); +`)); diff --git a/packages/react-components/src/Modal/index.tsx b/packages/react-components/src/Modal/index.tsx index 84af474f6c..0e96294174 100644 --- a/packages/react-components/src/Modal/index.tsx +++ b/packages/react-components/src/Modal/index.tsx @@ -1,9 +1,11 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeDef } from '@polkadot/react-components/types'; import { ActionsProps, ColumnProps, ModalProps } from './types'; -import React from 'react'; +import React, { useContext } from 'react'; +import { ThemeContext } from 'styled-components'; import SUIModal from 'semantic-ui-react/dist/commonjs/modules/Modal/Modal'; import Actions from './Actions'; @@ -20,12 +22,13 @@ type ModalType = React.FC & { }; function ModalBase (props: ModalProps): React.ReactElement { + const { theme } = useContext(ThemeContext); const { children, className = '', header, open = true } = props; return ( @@ -47,21 +50,3 @@ Modal.Header = SUIModal.Header; Modal.Description = SUIModal.Description; export default Modal; - -// rgba(0, 0, 0, 0.5); -// @media only screen and (min-width: 1200px) -// .ui.large.modal { -// width: 1080px; -// margin: 0; -// } - -// @media only screen and (min-width: 992px) -// .ui.large.modal { -// width: 1020px; -// margin: 0; -// } -// @media only screen and (min-width: 768px) -// .ui.large.modal { -// width: 88%; -// margin: 0; -// } diff --git a/packages/react-components/src/Progress.tsx b/packages/react-components/src/Progress.tsx index 2e86843afb..b82b6dc616 100644 --- a/packages/react-components/src/Progress.tsx +++ b/packages/react-components/src/Progress.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from './types'; + import BN from 'bn.js'; import React from 'react'; import styled from 'styled-components'; @@ -69,7 +71,7 @@ function Progress ({ className = '', isDisabled, size = 'normal', total, value } ); } -export default React.memo(styled(Progress)` +export default React.memo(styled(Progress)(({ theme }: ThemeProps) => ` border-radius: 100%; clip-path: circle(50%); height: 4.5rem; @@ -125,9 +127,10 @@ export default React.memo(styled(Progress)` .inner { align-items: center; - background: rgba(245, 243, 241, 0.91); + background: ${theme.bgInverse}; border-radius: 100%; bottom: 0.375rem; + color: ${theme.colorSummary}; display: flex; justify-content: center; left: 0.375rem; @@ -157,4 +160,4 @@ export default React.memo(styled(Progress)` font-size: 0.625rem; } } -`); +`)); diff --git a/packages/react-components/src/Sidebar.tsx b/packages/react-components/src/Sidebar.tsx index defac6b58f..1181c3f074 100644 --- a/packages/react-components/src/Sidebar.tsx +++ b/packages/react-components/src/Sidebar.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/app-accounts authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from './types'; + import React from 'react'; import styled from 'styled-components'; @@ -32,8 +34,8 @@ function Sidebar ({ button, children, className = '', onClose }: Props): React.R ); } -export default React.memo(styled(Sidebar)(({ offset = 0, position }: Props) => ` - background: #f5f3f1; +export default React.memo(styled(Sidebar)(({ offset = 0, position, theme }: Props & ThemeProps) => ` + background: ${theme.bgPage}; bottom: 0; box-shadow: ${position === 'right' ? '-6px' : '6px'} 0px 20px 0px rgba(0, 0, 0, 0.3); margin-left: -0.125rem; diff --git a/packages/react-components/src/Table/Body.tsx b/packages/react-components/src/Table/Body.tsx index 36cdfd57f3..3b48f6e1ab 100644 --- a/packages/react-components/src/Table/Body.tsx +++ b/packages/react-components/src/Table/Body.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import React from 'react'; import styled from 'styled-components'; import { isString } from '@polkadot/util'; @@ -28,7 +30,7 @@ function Body ({ children, className = '', empty, emptySpinner }: Props): React. ); } -export default React.memo(styled(Body)` +export default React.memo(styled(Body)(({ theme }: ThemeProps) => ` position: relative; td { @@ -37,11 +39,11 @@ export default React.memo(styled(Body)` vertical-align: middle; &:first-child { - border-left: 1px solid #eeecea; + border-left: 1px solid ${theme.borderTable}; } &:last-child { - border-right: 1px solid #eeecea; + border-right: 1px solid ${theme.borderTable}; } label { @@ -158,7 +160,7 @@ export default React.memo(styled(Body)` // } &:nth-child(odd) { - background: white; + background: ${theme.bgTable}; } &:first-child { @@ -173,7 +175,7 @@ export default React.memo(styled(Body)` &:last-child { td { - border-bottom: 1px solid #eeecea; + border-bottom: 1px solid ${theme.borderTable}; &:first-child { border-bottom-left-radius: 0.25rem; @@ -203,4 +205,4 @@ export default React.memo(styled(Body)` background-color: #eee !important; } } -`); +`)); diff --git a/packages/react-components/src/Table/Foot.tsx b/packages/react-components/src/Table/Foot.tsx index 59b29df068..33215d6233 100644 --- a/packages/react-components/src/Table/Foot.tsx +++ b/packages/react-components/src/Table/Foot.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import React from 'react'; import styled from 'styled-components'; @@ -22,9 +24,9 @@ function Foot ({ className = '', footer, isEmpty }: Props): React.ReactElement

    ` td { - color: rgba(78, 78, 78, .66); + color: rgba(${theme.theme === 'dark' ? '254, 240, 240' : '78, 78, 78'}, 0.66); font-family: sans-serif; font-weight: 100; padding: 0.75rem 1rem 0.25rem; @@ -34,6 +36,6 @@ export default React.memo(styled(Foot)` } tr { - background: #f5f3f1; + background: ${theme.bgPage}; } -`); +`)); diff --git a/packages/react-components/src/Table/Head.tsx b/packages/react-components/src/Table/Head.tsx index 24978c19fc..d62fd2f230 100644 --- a/packages/react-components/src/Table/Head.tsx +++ b/packages/react-components/src/Table/Head.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import React from 'react'; import styled from 'styled-components'; @@ -46,7 +48,7 @@ function Head ({ className = '', filter, header, isEmpty }: Props): React.ReactE ); } -export default React.memo(styled(Head)` +export default React.memo(styled(Head)(({ theme }: ThemeProps) => ` position: relative; z-index: 1; @@ -63,11 +65,11 @@ export default React.memo(styled(Head)` } &:first-child { - border-left: 1px solid #eeecea; + border-left: 1px solid ${theme.borderTable}; } &:last-child { - border-right: 1px solid #eeecea; + border-right: 1px solid ${theme.borderTable}; } &.address { @@ -98,18 +100,12 @@ export default React.memo(styled(Head)` } tr { - background: white; + background: ${theme.bgTable}; text-transform: lowercase; &:first-child { th { - border-top: 1px solid #eeecea; - } - } - - &:not(.filter) { - th { - color: rgba(78, 78, 78, 0.66); + border-top: 1px solid ${theme.borderTable}; } } @@ -126,5 +122,11 @@ export default React.memo(styled(Head)` padding: 0; } } + + &:not(.filter) { + th { + color: rgba(${theme.theme === 'dark' ? '254, 240, 240' : '78, 78, 78'}, 0.66); + } + } } -`); +`)); diff --git a/packages/react-components/src/Tabs/Tab.tsx b/packages/react-components/src/Tabs/Tab.tsx index d1e96e84c4..c4cfd4128b 100644 --- a/packages/react-components/src/Tabs/Tab.tsx +++ b/packages/react-components/src/Tabs/Tab.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; import { TabItem } from './types'; import React from 'react'; @@ -31,7 +32,7 @@ function Tab ({ basePath, className = '', count, hasParams, index, isExact, isRo return ( ` border-bottom: 2px solid transparent; - color: #4e4e4e !important; + color: ${theme.color} !important; margin-bottom: -3px; padding: 0.5rem 1.5rem 0.75rem; @@ -64,7 +65,7 @@ export default React.memo(styled(Tab)` } &:hover { - color: inherit !important; + filter: highlight(120%); &:not(.tabLinkActive) { border-bottom-color: #e6e6e6; @@ -78,4 +79,4 @@ export default React.memo(styled(Tab)` .tabIcon { margin-left: 0.75rem; } -`); +`)); diff --git a/packages/react-components/src/Tabs/index.tsx b/packages/react-components/src/Tabs/index.tsx index 1ae6e85ffe..009d95d47d 100644 --- a/packages/react-components/src/Tabs/index.tsx +++ b/packages/react-components/src/Tabs/index.tsx @@ -1,6 +1,7 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; import { TabItem } from './types'; import React, { useEffect } from 'react'; @@ -57,10 +58,10 @@ function Tabs ({ basePath, className = '', hidden, isSequence, items }: Props): ); } -export default React.memo(styled(Tabs)` +export default React.memo(styled(Tabs)(({ theme }: ThemeProps) => ` align-items: flex-end; - background: #fff; - border-bottom: 1px solid #eeecea; + background: ${theme.bgTabs}; + border-bottom: 1px solid ${theme.borderTabs}; display: flex; margin: 0 -1.5rem; padding: 1.5rem 1.5rem 0; @@ -71,4 +72,4 @@ export default React.memo(styled(Tabs)` display: none; width: 0px; } -`); +`)); diff --git a/packages/react-components/src/Toggle.tsx b/packages/react-components/src/Toggle.tsx index 5a67c3387d..bb57971b9b 100644 --- a/packages/react-components/src/Toggle.tsx +++ b/packages/react-components/src/Toggle.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from './types'; + import React, { useCallback } from 'react'; import styled from 'styled-components'; @@ -41,9 +43,8 @@ function Toggle ({ className = '', isDisabled, isOverlay, isRadio, label, onChan ); } -export default React.memo(styled(Toggle)` +export default React.memo(styled(Toggle)(({ theme }: ThemeProps) => ` > label { - color: rgba(78, 78, 78, 0.75); display: inline-block; margin: 0 0.5rem; } @@ -54,7 +55,7 @@ export default React.memo(styled(Toggle)` } .ui--Toggle-Slider { - background: #e4e5e6; + background: ${theme.bgToggle}; border-radius: 1.5rem; display: inline-block; height: 1.5rem; @@ -62,8 +63,8 @@ export default React.memo(styled(Toggle)` width: 3rem; &::before { - background: white; - border: 0.125rem solid #e4e5e6; + background: ${theme.bgTable}; + border: 0.125rem solid ${theme.bgToggle}; border-radius: 50%; content: ""; height: 1.5rem; @@ -107,4 +108,4 @@ export default React.memo(styled(Toggle)` position: absolute; right: 3.5rem; } -`); +`)); diff --git a/packages/react-components/src/styles/components.ts b/packages/react-components/src/styles/components.ts index 53f74c7039..994f8e2369 100644 --- a/packages/react-components/src/styles/components.ts +++ b/packages/react-components/src/styles/components.ts @@ -1,11 +1,13 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import { css } from 'styled-components'; export default css` .ui--output { - background: #fefefe; + background: ${({ theme }: ThemeProps) => theme.bgInput}; border-radius: 4px; border: 1px dashed #eee; box-sizing: border-box; diff --git a/packages/react-components/src/styles/index.ts b/packages/react-components/src/styles/index.ts index f8e47471dc..9da9acc724 100644 --- a/packages/react-components/src/styles/index.ts +++ b/packages/react-components/src/styles/index.ts @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import { createGlobalStyle } from 'styled-components'; import cssComponents from './components'; @@ -20,8 +22,8 @@ const PARTS = [0, 2, 4]; const defaultHighlight = '#f19135'; // '#f19135'; // #999 -function getHighlight (props: Props): string { - return (props.uiHighlight || defaultHighlight); +function getHighlight ({ uiHighlight }: Props): string { + return (uiHighlight || defaultHighlight); } function getContrast (props: Props): string { @@ -33,7 +35,7 @@ function getContrast (props: Props): string { : 'rgba(255, 253, 251, 0.875)'; } -export default createGlobalStyle` +export default createGlobalStyle` .highlight--all { background: ${getHighlight} !important; border-color: ${getHighlight} !important; @@ -58,7 +60,7 @@ export default createGlobalStyle` .highlight--bg-faint, .highlight--bg-light { - background: white; + background: ${({ theme }) => theme.bgTable}; position: relative; &:before { @@ -98,7 +100,7 @@ export default createGlobalStyle` } .highlight--gradient { - background: ${(props: Props) => `linear-gradient(90deg, ${props.uiHighlight || defaultHighlight}, transparent)`}; + background: ${({ uiHighlight }: Props) => `linear-gradient(90deg, ${uiHighlight || defaultHighlight}, transparent)`}; } .highlight--hover-bg:hover { @@ -177,14 +179,12 @@ export default createGlobalStyle` } } - .theme--default { - .ui--Tabs-Tab.tabLinkActive { - border-bottom-color: ${getHighlight}; - } - - .ui.negative.button, - .ui.buttons .negative.button { - background: #666 !important; + .theme--dark, + .theme--light { + .ui--Tabs { + .ui--Tab.tabLinkActive { + border-bottom-color: ${getHighlight}; + } } .ui.primary.button, @@ -213,7 +213,8 @@ export default createGlobalStyle` } #root { - color: #4e4e4e; + background: ${({ theme }) => theme.bgPage}; + color: ${({ theme }) => theme.color}; font-family: sans-serif; height: 100%; } @@ -223,7 +224,7 @@ export default createGlobalStyle` } article { - background: white; + background: ${({ theme }) => theme.bgTable}; border: 1px solid #f2f2f2; border-radius: 0.25rem; box-sizing: border-box; @@ -297,7 +298,6 @@ export default createGlobalStyle` } body { - background: #f5f3f1; height: 100%; margin: 0; } @@ -330,7 +330,7 @@ export default createGlobalStyle` } h1, h2, h3, h4, h5 { - color: rgba(0, 0, 0, .6); + color: ${({ theme }) => theme.colorSummary}; font-family: sans-serif; font-weight: 100; } @@ -363,7 +363,7 @@ export default createGlobalStyle` label { box-sizing: border-box; - color: rgba(78, 78, 78, .66); + color: ${({ theme }) => theme.colorLabel}; display: block; font-family: sans-serif; font-size: 1rem; diff --git a/packages/react-components/src/styles/semantic.ts b/packages/react-components/src/styles/semantic.ts index 66ca00c709..43040cff70 100644 --- a/packages/react-components/src/styles/semantic.ts +++ b/packages/react-components/src/styles/semantic.ts @@ -1,6 +1,8 @@ // Copyright 2017-2020 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ThemeProps } from '../types'; + import { css } from 'styled-components'; export default css` @@ -21,7 +23,13 @@ export default css` .ui.selection.dropdown, .ui.input > input { - color: inherit; + background: ${({ theme }: ThemeProps) => theme.bgInput}; + color: ${({ theme }: ThemeProps) => theme.color}; + + &:focus { + background: ${({ theme }: ThemeProps) => theme.bgInput}; + color: ${({ theme }: ThemeProps) => theme.color}; + } } .ui.action.input > .buttons { @@ -39,21 +47,40 @@ export default css` } } + &.selection.visible { + background: ${({ theme }: ThemeProps) => theme.bgInput}; + color: ${({ theme }: ThemeProps) => theme.color}; + } + + .menu { + background: ${({ theme }: ThemeProps) => theme.bgInput}; + color: ${({ theme }: ThemeProps) => theme.color}; + + > .item { + border-color: transparent !important; + color: ${({ theme }: ThemeProps) => theme.color} !important; + + &.header.disabled { + margin: 1em 0 0 0; + opacity: 1; + + &:hover, + &.selected { + background: ${({ theme }: ThemeProps) => theme.bgInput}; + } + } + } + } + > .text { min-height: 1em; + + &:not(.default) { + color: ${({ theme }: ThemeProps) => theme.color} !important; + } } } - .ui.dropdown .menu > .item.header.disabled { - margin: 1em 0 0 0; - opacity: 1; - } - - .ui.dropdown .menu > .item.header.disabled:hover, - .ui.dropdown .menu > .item.header.disabled.selected { - background: white; - } - .ui.input { width: 100%; @@ -96,9 +123,9 @@ export default css` } .ui.modal { - background: #f9f8f7; + background: ${({ theme }: ThemeProps) => theme.bgPage}; box-shadow: none; - color: #4e4e4e; + color: ${({ theme }: ThemeProps) => theme.color}; font-family: sans-serif; > .actions, @@ -116,7 +143,7 @@ export default css` > .header:not(.ui) { background: transparent; border-bottom: none; - color: rgba(0, 0, 0, .6); + color: ${({ theme }: ThemeProps) => theme.colorSummary}; font-family: sans-serif; font-size: 1.75rem; font-weight: 100; @@ -139,6 +166,15 @@ export default css` display: flex !important; } + .ui.popup { + background: ${({ theme }: ThemeProps) => theme.bgMenu}; + color: ${({ theme }: ThemeProps) => theme.color}; + + .ui.text.menu .item { + color: ${({ theme }: ThemeProps) => theme.color}; + } + } + .ui.secondary.vertical.menu > .item { margin: 0; } diff --git a/packages/react-components/src/styles/theme.ts b/packages/react-components/src/styles/theme.ts index f7812fbdd5..1f2975a035 100644 --- a/packages/react-components/src/styles/theme.ts +++ b/packages/react-components/src/styles/theme.ts @@ -20,8 +20,9 @@ export const colorBtnText = '#f9f8f7'; export const colorLink = '#2e86ab'; export default css` - .theme--default { - a { + .theme--dark, + .theme--light { + a:not(.ui--Tab) { color: ${colorLink}; &:hover, @@ -41,5 +42,10 @@ export default css` .ui.modal > .header:not(.ui) { border-bottom-color: ${colorBtnDefault}; } + + .ui.negative.button, + .ui.buttons .negative.button { + background: #666 !important; + } } `; diff --git a/packages/react-components/src/types.ts b/packages/react-components/src/types.ts index 5c993af9a3..87249dbcb4 100644 --- a/packages/react-components/src/types.ts +++ b/packages/react-components/src/types.ts @@ -124,3 +124,25 @@ export interface NullContract { abi: null; address: null; } + +export interface ThemeDef { + bgInput: string; + bgInverse: string; + bgMenu: string; + bgMenuHover: string; + bgPage: string; + bgTable: string; + bgTabs: string; + bgToggle: string; + borderTable: string; + borderTabs: string; + color: string; + colorError: string; + colorLabel: string; + colorSummary: string; + theme: 'dark' | 'light'; +} + +export interface ThemeProps { + theme: ThemeDef; +}