diff --git a/app/components/Actions.js b/app/components/Actions.js index 4602050a4..77a8b51fb 100644 --- a/app/components/Actions.js +++ b/app/components/Actions.js @@ -20,7 +20,7 @@ export const Separator = styled.div` margin-left: 12px; width: 1px; height: 20px; - background: ${props => props.theme.slateLight}; + background: ${props => props.theme.divider}; `; const Actions = styled(Flex)` @@ -29,7 +29,7 @@ const Actions = styled(Flex)` right: 0; left: 0; border-radius: 3px; - background: rgba(255, 255, 255, 0.9); + background: ${props => props.theme.background}; padding: 12px; -webkit-backdrop-filter: blur(20px); diff --git a/app/components/Alert.js b/app/components/Alert.js index eb69a09e1..fc5f4c91b 100644 --- a/app/components/Alert.js +++ b/app/components/Alert.js @@ -26,7 +26,7 @@ class Alert extends React.Component { const Container = styled(Flex)` height: $headerHeight; - color: #ffffff; + color: ${props => props.theme.white}; font-size: 14px; line-height: 1; diff --git a/app/components/Avatar/Avatar.js b/app/components/Avatar/Avatar.js index ee3db84b8..035cbada8 100644 --- a/app/components/Avatar/Avatar.js +++ b/app/components/Avatar/Avatar.js @@ -39,7 +39,7 @@ const CircleImg = styled.img` width: ${props => props.size}px; height: ${props => props.size}px; border-radius: 50%; - border: 2px solid ${props => props.theme.white}; + border: 2px solid ${props => props.theme.background}; flex-shrink: 0; `; diff --git a/app/components/Button.js b/app/components/Button.js index 8d102fd3c..b6c596743 100644 --- a/app/components/Button.js +++ b/app/components/Button.js @@ -8,8 +8,8 @@ const RealButton = styled.button` margin: 0; padding: 0; border: 0; - background: ${props => props.theme.blackLight}; - color: ${props => props.theme.white}; + background: ${props => props.theme.buttonBackground}; + color: ${props => props.theme.buttonText}; box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 2px; border-radius: 4px; font-size: 12px; @@ -28,26 +28,26 @@ const RealButton = styled.button` } &:hover { - background: ${props => darken(0.05, props.theme.blackLight)}; + background: ${props => darken(0.05, props.theme.buttonBackground)}; } &:disabled { cursor: default; pointer-events: none; - color: ${props => lighten(0.2, props.theme.blackLight)}; + color: ${props => lighten(0.2, props.theme.buttonText)}; } ${props => props.neutral && ` - background: ${props.theme.white}; - color: ${props.theme.text}; + background: ${props.theme.buttonNeutralBackground}; + color: ${props.theme.buttonNeutralText}; box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px; - border: 1px solid ${props.theme.slateLight}; + border: 1px solid ${darken(0.1, props.theme.buttonNeutralBackground)}; &:hover { - background: ${darken(0.05, props.theme.white)}; - border: 1px solid ${darken(0.05, props.theme.slateLight)}; + background: ${darken(0.05, props.theme.buttonNeutralBackground)}; + border: 1px solid ${darken(0.15, props.theme.buttonNeutralBackground)}; } `} ${props => props.danger && diff --git a/app/components/DocumentPreview/DocumentPreview.js b/app/components/DocumentPreview/DocumentPreview.js index e5ca56f37..95850e4f9 100644 --- a/app/components/DocumentPreview/DocumentPreview.js +++ b/app/components/DocumentPreview/DocumentPreview.js @@ -4,6 +4,7 @@ import { observer } from 'mobx-react'; import { Link } from 'react-router-dom'; import Document from 'models/Document'; import styled, { withTheme } from 'styled-components'; +import { darken } from 'polished'; import Flex from 'shared/components/Flex'; import Highlight from 'components/Highlight'; import { StarredIcon } from 'outline-icons'; @@ -20,7 +21,7 @@ type Props = { }; const StyledStar = withTheme(styled(({ solid, theme, ...props }) => ( - + ))` flex-shrink: 0; opacity: ${props => (props.solid ? '1 !important' : 0)}; @@ -59,8 +60,8 @@ const DocumentLink = styled(Link)` &:hover, &:active, &:focus { - background: ${props => props.theme.smokeLight}; - border: 2px solid ${props => props.theme.smoke}; + background: ${props => props.theme.listItemHoverBackground}; + border: 2px solid ${props => props.theme.listItemHoverBorder}; outline: none; ${StyledStar}, ${StyledDocumentMenu} { @@ -73,7 +74,7 @@ const DocumentLink = styled(Link)` } &:focus { - border: 2px solid ${props => props.theme.slateDark}; + border: 2px solid ${props => darken(0.5, props.theme.listItemHoverBorder)}; } `; @@ -102,7 +103,7 @@ const Title = styled(Highlight)` const ResultContext = styled(Highlight)` display: block; - color: ${props => props.theme.slateDark}; + color: ${props => props.theme.textTertiary}; font-size: 14px; margin-top: -0.25em; margin-bottom: 0.25em; diff --git a/app/components/DocumentPreview/components/PublishingInfo.js b/app/components/DocumentPreview/components/PublishingInfo.js index 7723f613e..3425667dd 100644 --- a/app/components/DocumentPreview/components/PublishingInfo.js +++ b/app/components/DocumentPreview/components/PublishingInfo.js @@ -7,13 +7,13 @@ import Flex from 'shared/components/Flex'; import Time from 'shared/components/Time'; const Container = styled(Flex)` - color: ${props => props.theme.slate}; + color: ${props => props.theme.textTertiary}; font-size: 13px; `; const Modified = styled.span` color: ${props => - props.highlight ? props.theme.slateDark : props.theme.slate}; + props.highlight ? props.theme.text : props.theme.textTertiary}; font-weight: ${props => (props.highlight ? '600' : '400')}; `; diff --git a/app/components/DropdownMenu/DropdownMenu.js b/app/components/DropdownMenu/DropdownMenu.js index c8fc05c8e..2408b2140 100644 --- a/app/components/DropdownMenu/DropdownMenu.js +++ b/app/components/DropdownMenu/DropdownMenu.js @@ -3,8 +3,8 @@ import * as React from 'react'; import invariant from 'invariant'; import { observable } from 'mobx'; import { observer } from 'mobx-react'; -import styled from 'styled-components'; import { PortalWithState } from 'react-portal'; +import styled from 'styled-components'; import Flex from 'shared/components/Flex'; import { fadeAndScaleIn } from 'shared/styles/animations'; @@ -89,14 +89,13 @@ const Menu = styled.div` right: ${({ right }) => right}px; top: ${({ top }) => top}px; z-index: 1000; - border: ${props => props.theme.slateLight}; - background: ${props => props.theme.white}; + + background: ${props => props.theme.menuBackground}; border-radius: 2px; padding: 0.5em 0; min-width: 180px; overflow: hidden; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 8px rgba(0, 0, 0, 0.08), - 0 2px 4px rgba(0, 0, 0, 0.08); + box-shadow: ${props => props.theme.menuShadow}; @media print { display: none; diff --git a/app/components/DropdownMenu/DropdownMenuItem.js b/app/components/DropdownMenu/DropdownMenuItem.js index 662d80274..31036ab48 100644 --- a/app/components/DropdownMenu/DropdownMenuItem.js +++ b/app/components/DropdownMenu/DropdownMenuItem.js @@ -23,13 +23,13 @@ const MenuItem = styled.a` height: 32px; color: ${props => - props.disabled ? props.theme.slate : props.theme.slateDark}; + props.disabled ? props.theme.textTertiary : props.theme.textSecondary}; justify-content: left; align-items: center; font-size: 15px; cursor: default; - svg { + svg:not(:last-child) { margin-right: 8px; } diff --git a/app/components/Editor/Editor.js b/app/components/Editor/Editor.js index eda2a9d30..c2c542651 100644 --- a/app/components/Editor/Editor.js +++ b/app/components/Editor/Editor.js @@ -3,6 +3,7 @@ import * as React from 'react'; import { Redirect } from 'react-router-dom'; import { observable } from 'mobx'; import { observer } from 'mobx-react'; +import { withTheme } from 'styled-components'; import RichMarkdownEditor from 'rich-markdown-editor'; import { uploadFile } from 'utils/uploadFile'; import isInternalUrl from 'utils/isInternalUrl'; @@ -89,7 +90,7 @@ class Editor extends React.Component { } } -// $FlowIssue - https://github.com/facebook/flow/issues/6103 -export default React.forwardRef((props, ref) => ( - -)); +export default withTheme( + // $FlowIssue - https://github.com/facebook/flow/issues/6103 + React.forwardRef((props, ref) => ) +); diff --git a/app/components/HelpText.js b/app/components/HelpText.js index afacf1ee9..1ae8e4cda 100644 --- a/app/components/HelpText.js +++ b/app/components/HelpText.js @@ -3,7 +3,7 @@ import styled from 'styled-components'; const HelpText = styled.p` margin-top: 0; - color: ${props => props.theme.slateDark}; + color: ${props => props.theme.textSecondary}; font-size: ${props => (props.small ? '13px' : 'inherit')}; `; diff --git a/app/components/Highlight.js b/app/components/Highlight.js index d70c49a1b..41cc25f33 100644 --- a/app/components/Highlight.js +++ b/app/components/Highlight.js @@ -39,6 +39,8 @@ function Highlight({ const Mark = styled.mark` background: ${props => props.theme.yellow}; + border-radius: 2px; + padding: 0 4px; `; export default Highlight; diff --git a/app/components/Input.js b/app/components/Input.js index b71f77b34..c092ad0df 100644 --- a/app/components/Input.js +++ b/app/components/Input.js @@ -1,5 +1,7 @@ // @flow import * as React from 'react'; +import { observer } from 'mobx-react'; +import { observable } from 'mobx'; import styled from 'styled-components'; import Flex from 'shared/components/Flex'; @@ -9,10 +11,11 @@ const RealTextarea = styled.textarea` padding: 8px 12px; outline: none; background: none; + color: ${props => props.theme.text}; &:disabled, &::placeholder { - color: ${props => props.theme.slate}; + color: ${props => props.theme.placeholder}; } `; @@ -22,10 +25,11 @@ const RealInput = styled.input` padding: 8px 12px; outline: none; background: none; + color: ${props => props.theme.text}; &:disabled, &::placeholder { - color: ${props => props.theme.slate}; + color: ${props => props.theme.placeholder}; } &::-webkit-search-cancel-button { @@ -46,13 +50,14 @@ export const Outline = styled(Flex)` color: inherit; border-width: 1px; border-style: solid; - border-color: ${props => (props.hasError ? 'red' : props.theme.slateLight)}; + border-color: ${props => + props.hasError + ? 'red' + : props.focused + ? props.theme.inputBorderFocused + : props.theme.inputBorder}; border-radius: 4px; font-weight: normal; - - &:focus { - border-color: ${props => props.theme.slate}; - } `; export const LabelText = styled.div` @@ -68,26 +73,39 @@ export type Props = { short?: boolean, }; -export default function Input({ - type = 'text', - label, - className, - short, - ...rest -}: Props) { - const InputComponent = type === 'textarea' ? RealTextarea : RealInput; +@observer +class Input extends React.Component { + @observable focused: boolean = false; - return ( - - - - ); + handleBlur = () => { + this.focused = false; + }; + + handleFocus = () => { + this.focused = true; + }; + + render() { + const { type = 'text', label, className, short, ...rest } = this.props; + + const InputComponent = type === 'textarea' ? RealTextarea : RealInput; + + return ( + + + + ); + } } + +export default Input; diff --git a/app/components/InputRich.js b/app/components/InputRich.js index 3f2d61aee..861e75cc3 100644 --- a/app/components/InputRich.js +++ b/app/components/InputRich.js @@ -2,7 +2,7 @@ import * as React from 'react'; import { observable } from 'mobx'; import { observer } from 'mobx-react'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import Input, { LabelText, Outline } from 'components/Input'; type Props = { @@ -15,11 +15,20 @@ type Props = { @observer class InputRich extends React.Component { @observable editorComponent: *; + @observable focused: boolean = false; componentDidMount() { this.loadEditor(); } + handleBlur = () => { + this.focused = false; + }; + + handleFocus = () => { + this.focused = true; + }; + loadEditor = async () => { const EditorImport = await import('./Editor'); this.editorComponent = EditorImport.default; @@ -33,8 +42,16 @@ class InputRich extends React.Component { {label} {Editor ? ( - - + + ) : ( props.theme.text}; + color: ${props => props.theme.almostBlack}; vertical-align: middle; background-color: ${props => props.theme.smokeLight}; border: solid 1px ${props => props.theme.slateLight}; diff --git a/app/components/Labeled.js b/app/components/Labeled.js index 167e7d80f..1ae754544 100644 --- a/app/components/Labeled.js +++ b/app/components/Labeled.js @@ -21,7 +21,7 @@ export const Label = styled(Flex)` font-size: 13px; font-weight: 500; text-transform: uppercase; - color: #9fa6ab; + color: ${props => props.theme.textTertiary}; letter-spacing: 0.04em; `; diff --git a/app/components/Layout.js b/app/components/Layout.js index 5f266bd9b..e7ef2c1b2 100644 --- a/app/components/Layout.js +++ b/app/components/Layout.js @@ -2,7 +2,7 @@ import * as React from 'react'; import { Switch, Route, Redirect } from 'react-router-dom'; import { Helmet } from 'react-helmet'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import breakpoint from 'styled-components-breakpoint'; import { observable } from 'mobx'; import { observer, inject } from 'mobx-react'; @@ -35,6 +35,7 @@ type Props = { auth: AuthStore, ui: UiStore, notifications?: React.Node, + theme: Object, }; @observer @@ -42,12 +43,23 @@ class Layout extends React.Component { scrollable: ?HTMLDivElement; @observable redirectTo: ?string; + componentWillMount() { + this.updateBackground(); + } + componentDidUpdate() { + this.updateBackground(); + if (this.redirectTo) { this.redirectTo = undefined; } } + updateBackground() { + // ensure the wider page color always matches the theme + window.document.body.style.background = this.props.theme.background; + } + @keydown(['/', 't', 'meta+k']) goToSearch(ev) { ev.preventDefault(); @@ -124,6 +136,7 @@ class Layout extends React.Component { } const Container = styled(Flex)` + background: ${props => props.theme.background}; position: relative; width: 100vw; min-height: 100%; @@ -142,4 +155,4 @@ const Content = styled(Flex)` `}; `; -export default inject('auth', 'ui', 'documents')(Layout); +export default inject('auth', 'ui', 'documents')(withTheme(Layout)); diff --git a/app/components/List/Item.js b/app/components/List/Item.js index 1d6a29cfe..d9efc2fe4 100644 --- a/app/components/List/Item.js +++ b/app/components/List/Item.js @@ -29,7 +29,7 @@ const Wrapper = styled.li` display: flex; padding: ${props => (props.compact ? '8px' : '12px')} 0; margin: 0; - border-bottom: 1px solid ${props => props.theme.smokeDark}; + border-bottom: 1px solid ${props => props.theme.divider}; `; const Image = styled(Flex)` diff --git a/app/components/Mask.js b/app/components/Mask.js index 32ecd667f..d557499b9 100644 --- a/app/components/Mask.js +++ b/app/components/Mask.js @@ -25,7 +25,7 @@ const Redacted = styled(Flex)` width: ${props => (props.header ? props.width / 2 : props.width)}%; height: ${props => (props.height ? props.height : props.header ? 24 : 18)}px; margin-bottom: 6px; - background-color: ${props => props.theme.smokeDark}; + background-color: ${props => props.theme.divider}; animation: ${pulsate} 1.3s infinite; &:last-child { diff --git a/app/components/Modal.js b/app/components/Modal.js index b3759e92c..721534c16 100644 --- a/app/components/Modal.js +++ b/app/components/Modal.js @@ -4,6 +4,7 @@ import { observer } from 'mobx-react'; import styled, { createGlobalStyle } from 'styled-components'; import breakpoint from 'styled-components-breakpoint'; import ReactModal from 'react-modal'; +import { transparentize } from 'polished'; import { CloseIcon } from 'outline-icons'; import { fadeAndScaleIn } from 'shared/styles/animations'; import Flex from 'shared/components/Flex'; @@ -17,6 +18,8 @@ type Props = { const GlobalStyles = createGlobalStyle` .ReactModal__Overlay { + background-color: ${props => + transparentize(0.25, props.theme.background)} !important; z-index: 100; } @@ -76,7 +79,7 @@ const StyledModal = styled(ReactModal)` align-items: flex-start; overflow-x: hidden; overflow-y: auto; - background: white; + background: ${props => props.theme.background}; padding: 13vh 2rem 2rem; outline: none; `; @@ -93,7 +96,7 @@ const Close = styled.a` top: 16px; right: 16px; opacity: 0.5; - color: ${props => props.theme.text}; + color: ${props => props.theme.textSecondary}; &:hover { opacity: 1; diff --git a/app/components/Sidebar/Sidebar.js b/app/components/Sidebar/Sidebar.js index f20a07cc3..ca3bf5705 100644 --- a/app/components/Sidebar/Sidebar.js +++ b/app/components/Sidebar/Sidebar.js @@ -6,10 +6,12 @@ import styled from 'styled-components'; import breakpoint from 'styled-components-breakpoint'; import { observer, inject } from 'mobx-react'; import { CloseIcon, MenuIcon } from 'outline-icons'; +import Fade from 'components/Fade'; import Flex from 'shared/components/Flex'; - import UiStore from 'stores/UiStore'; +let firstRender = true; + type Props = { children: React.Node, location: Location, @@ -30,8 +32,7 @@ class Sidebar extends React.Component { render() { const { children, ui } = this.props; - - return ( + const content = ( { {children} ); + + // Fade in the sidebar on first render after page load + if (firstRender) { + firstRender = false; + return {content}; + } + + return content; } } @@ -55,7 +64,7 @@ const Container = styled(Flex)` bottom: 0; left: ${props => (props.editMode ? `-${props.theme.sidebarWidth}` : 0)}; width: 100%; - background: ${props => props.theme.smoke}; + background: ${props => props.theme.sidebarBackground}; transition: left 100ms ease-out; margin-left: ${props => (props.mobileSidebarVisible ? 0 : '-100%')}; z-index: 2; @@ -68,7 +77,7 @@ const Container = styled(Flex)` &:before, &:after { content: ''; - background: ${props => props.theme.smoke}; + background: ${props => props.theme.sidebarBackground}; position: absolute; top: -50vh; left: 0; diff --git a/app/components/Sidebar/components/Header.js b/app/components/Sidebar/components/Header.js index 48c7405df..2b1ac0c42 100644 --- a/app/components/Sidebar/components/Header.js +++ b/app/components/Sidebar/components/Header.js @@ -6,7 +6,7 @@ const Header = styled(Flex)` font-size: 11px; font-weight: 600; text-transform: uppercase; - color: ${props => props.theme.slateDark}; + color: ${props => props.theme.sidebarText}; letter-spacing: 0.04em; margin: 4px 16px; `; diff --git a/app/components/Sidebar/components/HeaderBlock.js b/app/components/Sidebar/components/HeaderBlock.js index 1dea87eba..d7a6b61ff 100644 --- a/app/components/Sidebar/components/HeaderBlock.js +++ b/app/components/Sidebar/components/HeaderBlock.js @@ -46,7 +46,7 @@ const Subheading = styled.div` font-size: 11px; text-transform: uppercase; font-weight: 500; - color: ${props => props.theme.slateDark}; + color: ${props => props.theme.sidebarText}; `; const TeamName = styled.div` diff --git a/app/components/Sidebar/components/SidebarLink.js b/app/components/Sidebar/components/SidebarLink.js index 82577590a..f86862bc2 100644 --- a/app/components/Sidebar/components/SidebarLink.js +++ b/app/components/Sidebar/components/SidebarLink.js @@ -34,7 +34,7 @@ class SidebarLink extends React.Component { activeStyle = { color: this.props.theme.text, - background: 'rgba(0, 0, 0, 0.05)', + background: this.props.theme.sidebarItemBackground, fontWeight: 600, ...this.style, }; @@ -115,7 +115,7 @@ const StyledNavLink = styled(NavLink)` text-overflow: ellipsis; padding: 4px 16px; border-radius: 4px; - color: ${props => props.theme.slateDark}; + color: ${props => props.theme.sidebarText}; font-size: 15px; cursor: pointer; @@ -128,7 +128,7 @@ const Action = styled.span` position: absolute; top: 4px; right: 4px; - color: ${props => props.theme.slate}; + color: ${props => props.theme.textTertiary}; svg { opacity: 0.75; diff --git a/app/components/Subheading.js b/app/components/Subheading.js index afe2bcb66..84dad4f85 100644 --- a/app/components/Subheading.js +++ b/app/components/Subheading.js @@ -5,9 +5,9 @@ const Subheading = styled.h3` font-size: 11px; font-weight: 500; text-transform: uppercase; - color: ${props => props.theme.slate}; + color: ${props => props.theme.textTertiary}; letter-spacing: 0.04em; - border-bottom: 1px solid ${props => props.theme.slateLight}; + border-bottom: 1px solid ${props => props.theme.textTertiary}; padding-bottom: 8px; margin-top: 30px; margin-bottom: 10px; diff --git a/app/components/Tab.js b/app/components/Tab.js index e4e9708f0..6b974655d 100644 --- a/app/components/Tab.js +++ b/app/components/Tab.js @@ -8,21 +8,21 @@ const NavItem = styled(NavLink)` font-size: 11px; font-weight: 500; text-transform: uppercase; - color: ${props => props.theme.slate}; + color: ${props => props.theme.textTertiary}; letter-spacing: 0.04em; margin-right: 24px; padding-bottom: 8px; &:hover { - color: ${props => props.theme.slateDark}; + color: ${props => props.theme.text}; } `; function Tab(props: *) { const activeStyle = { paddingBottom: '5px', - borderBottom: `3px solid ${props.theme.slateLight}`, - color: props.theme.slate, + borderBottom: `3px solid ${props.theme.textTertiary}`, + color: props.theme.textTertiary, }; return ; diff --git a/app/components/Tabs.js b/app/components/Tabs.js index 78fd63123..d1c1f239e 100644 --- a/app/components/Tabs.js +++ b/app/components/Tabs.js @@ -2,7 +2,7 @@ import styled from 'styled-components'; const Tabs = styled.nav` - border-bottom: 1px solid ${props => props.theme.slateLight}; + border-bottom: 1px solid ${props => props.theme.textTertiary}; margin-top: 22px; margin-bottom: 10px; `; diff --git a/app/components/Theme.js b/app/components/Theme.js new file mode 100644 index 000000000..b995e8835 --- /dev/null +++ b/app/components/Theme.js @@ -0,0 +1,25 @@ +// @flow +import * as React from 'react'; +import { inject, observer } from 'mobx-react'; +import { ThemeProvider } from 'styled-components'; +import { dark, light } from 'shared/styles/theme'; +import GlobalStyles from 'shared/styles/globals'; +import UiStore from 'stores/UiStore'; + +type Props = { + ui: UiStore, + children: React.Node, +}; + +function Theme({ children, ui }: Props) { + return ( + + + + {children} + + + ); +} + +export default inject('ui')(observer(Theme)); diff --git a/app/components/Tooltip.js b/app/components/Tooltip.js index 8cb76f132..14ee7f4df 100644 --- a/app/components/Tooltip.js +++ b/app/components/Tooltip.js @@ -4,7 +4,194 @@ import { TooltipTrigger } from 'pui-react-tooltip'; import { createGlobalStyle } from 'styled-components'; const GlobalStyles = createGlobalStyle` - .tooltip:hover .tooltip-container:not(.tooltip-container-hidden){visibility:visible;opacity:1}.tooltip-container{visibility:hidden;-webkit-transition:opacity ease-out 0.2s;transition:opacity ease-out 0.2s;z-index:10;position:absolute;bottom:100%;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin:0 0 8px 0;text-align:left}.tooltip-container.tooltip-container-visible{visibility:visible}.tooltip-container.tooltip-hoverable:after{content:"";position:absolute;width:calc(100% + 16px);height:calc(100% + 16px);top:50%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.tooltip-container .tooltip-content{white-space:nowrap;padding:4px 8px;font-size:12px;line-height:16px;font-weight:400;letter-spacing:0;text-transform:none;background-color:#243641;color:#fff;border-radius:2px;border:1px solid #243641;box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1)}.tooltip-container .tooltip-content:before{content:"";z-index:1;position:absolute;bottom:-4px;left:50%;-webkit-transform:translateX(-50%) rotateZ(45deg);transform:translateX(-50%) rotateZ(45deg);background-color:#243641;border-bottom:1px solid #243641;border-right:1px solid #243641;width:8px;height:8px}.tooltip-container .tooltip-content:after{content:"";box-sizing:content-box;z-index:-1;position:absolute;bottom:-4px;left:50%;-webkit-transform:translateX(-50%) rotateZ(45deg);transform:translateX(-50%) rotateZ(45deg);background-color:#243641;box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1);width:8px;height:8px}.tooltip{position:relative;display:inline-block}.tooltip.tooltip-light .tooltip-content{background-color:#fff;color:#243641;border:1px solid #DFE5E8}.tooltip.tooltip-light .tooltip-content:before{background-color:#fff;border-bottom:1px solid #DFE5E8;border-right:1px solid #DFE5E8}.tooltip.tooltip-light .tooltip-content:after{background-color:#fff}.tooltip.tooltip-bottom .tooltip-container{top:100%;bottom:auto;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);margin:8px 0 0 0}.tooltip.tooltip-bottom .tooltip-container .tooltip-content:before{bottom:auto;top:-4px;border-top:1px solid #243641;border-right:none;border-bottom:none;border-left:1px solid #243641}.tooltip.tooltip-bottom .tooltip-container .tooltip-content:after{bottom:auto;top:-4px}.tooltip.tooltip-bottom.tooltip-light .tooltip-content:before{border-top:1px solid #DFE5E8;border-left:1px solid #DFE5E8}.tooltip.tooltip-right .tooltip-container{top:50%;bottom:auto;left:100%;-webkit-transform:translatey(-50%);transform:translatey(-50%);margin:0 0 0 8px}.tooltip.tooltip-right .tooltip-container .tooltip-content:before{bottom:auto;left:-4px;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg);border-top:none;border-right:none;border-bottom:1px solid #243641;border-left:1px solid #243641}.tooltip.tooltip-right .tooltip-container .tooltip-content:after{bottom:auto;left:-4px;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg)}.tooltip.tooltip-right.tooltip-light .tooltip-content:before{border-bottom:1px solid #DFE5E8;border-left:1px solid #DFE5E8}.tooltip.tooltip-left .tooltip-container{top:50%;bottom:auto;right:100%;left:auto;-webkit-transform:translatey(-50%);transform:translatey(-50%);margin:0 8px 0 0}.tooltip.tooltip-left .tooltip-container .tooltip-content:before{bottom:auto;right:-4px;left:auto;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg);border-top:1px solid #243641;border-right:1px solid #243641;border-bottom:none;border-left:none}.tooltip.tooltip-left .tooltip-container .tooltip-content:after{bottom:auto;right:-4px;left:auto;top:50%;-webkit-transform:translatey(-50%) rotateZ(45deg);transform:translatey(-50%) rotateZ(45deg)}.tooltip.tooltip-left.tooltip-light .tooltip-content:before{border-top:1px solid #DFE5E8;border-right:1px solid #DFE5E8}.tooltip-sm.tooltip-container{width:120px}.tooltip-sm.tooltip-container .tooltip-content{white-space:normal}.tooltip-md.tooltip-container{width:240px}.tooltip-md.tooltip-container .tooltip-content{white-space:normal}.tooltip-lg.tooltip-container{width:360px}.tooltip-lg.tooltip-container .tooltip-content{white-space:normal}.tether-element{z-index:99}.overlay-trigger{color:#1B78B3;-webkit-transition:all 300ms ease-out;transition:all 300ms ease-out;-webkit-transition-property:background-color, color, opacity;transition-property:background-color, color, opacity}.overlay-trigger:hover,.overlay-trigger:focus{color:#1f8ace;cursor:pointer;outline:none;text-decoration:none}.overlay-trigger:active,.overlay-trigger.active{color:#176698} + .tooltip:hover .tooltip-container:not(.tooltip-container-hidden){ + visibility:visible; + opacity:1 + } + .tooltip-container{ + visibility:hidden; + -webkit-transition:opacity ease-out 0.2s; + transition:opacity ease-out 0.2s; + z-index:10; + position:absolute; + bottom:100%; + left:50%; + -webkit-transform:translateX(-50%); + transform:translateX(-50%); + margin:0 0 8px 0; + text-align:left + } + .tooltip-container.tooltip-container-visible{ + visibility:visible + } + .tooltip-container.tooltip-hoverable:after{ + content:""; + position:absolute; + width:calc(100% + 16px); + height:calc(100% + 16px); + top:50%; + left:50%; + -webkit-transform:translateX(-50%) translateY(-50%); + transform:translateX(-50%) translateY(-50%) + } + .tooltip-container .tooltip-content{ + white-space:nowrap; + padding:4px 8px; + font-size:12px; + line-height:16px; + font-weight:400; + letter-spacing:0; + text-transform:none; + background-color:${props => props.theme.tooltipBackground}; + color: ${props => props.theme.tooltipText}; + border-radius:2px; + border:1px solid ${props => props.theme.tooltipBackground}; + box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1) + } + .tooltip-container .tooltip-content:before{ + content:""; + z-index:1; + position:absolute; + bottom:-4px; + left:50%; + -webkit-transform:translateX(-50%) rotateZ(45deg); + transform:translateX(-50%) rotateZ(45deg); + background-color:${props => props.theme.tooltipBackground}; + border-bottom:1px solid ${props => props.theme.tooltipBackground}; + border-right:1px solid ${props => props.theme.tooltipBackground}; + width:8px; + height:8px + } + .tooltip-container .tooltip-content:after{ + content:""; + box-sizing:content-box; + z-index:-1; + position:absolute; + bottom:-4px; + left:50%; + -webkit-transform:translateX(-50%) rotateZ(45deg); + transform:translateX(-50%) rotateZ(45deg); + background-color:${props => props.theme.tooltipBackground}; + box-shadow:0px 2px 2px 0px rgba(36, 54, 65, .1),0px 0px 2px 0px rgba(36, 54, 65, .1); + width:8px; + height:8px + } + .tooltip{ + position:relative; + display:inline-block + } + .tooltip.tooltip-bottom .tooltip-container{ + top:100%; + bottom:auto; + left:50%; + -webkit-transform:translateX(-50%); + transform:translateX(-50%); + margin:8px 0 0 0 + } + .tooltip.tooltip-bottom .tooltip-container .tooltip-content:before{ + bottom:auto; + top:-4px; + border-top:1px solid ${props => props.theme.tooltipBackground}; + border-right:none; + border-bottom:none; + border-left:1px solid ${props => props.theme.tooltipBackground} + } + .tooltip.tooltip-bottom .tooltip-container .tooltip-content:after{ + bottom:auto; + top:-4px + } + .tooltip.tooltip-right .tooltip-container{ + top:50%; + bottom:auto; + left:100%; + -webkit-transform:translatey(-50%); + transform:translatey(-50%); + margin:0 0 0 8px + } + .tooltip.tooltip-right .tooltip-container .tooltip-content:before{ + bottom:auto; + left:-4px; + top:50%; + -webkit-transform:translatey(-50%) rotateZ(45deg); + transform:translatey(-50%) rotateZ(45deg); + border-top:none; + border-right:none; + border-bottom:1px solid ${props => props.theme.tooltipBackground}; + border-left:1px solid ${props => props.theme.tooltipBackground} + } + .tooltip.tooltip-right .tooltip-container .tooltip-content:after{ + bottom:auto; + left:-4px; + top:50%; + -webkit-transform:translatey(-50%) rotateZ(45deg); + transform:translatey(-50%) rotateZ(45deg) + } + .tooltip.tooltip-left .tooltip-container{ + top:50%; + bottom:auto; + right:100%; + left:auto; + -webkit-transform:translatey(-50%); + transform:translatey(-50%); + margin:0 8px 0 0 + } + .tooltip.tooltip-left .tooltip-container .tooltip-content:before{ + bottom:auto; + right:-4px; + left:auto; + top:50%; + -webkit-transform:translatey(-50%) rotateZ(45deg); + transform:translatey(-50%) rotateZ(45deg); + border-top:1px solid ${props => props.theme.tooltipBackground}; + border-right:1px solid ${props => props.theme.tooltipBackground}; + border-bottom:none; + border-left:none + } + .tooltip.tooltip-left .tooltip-container .tooltip-content:after{ + bottom:auto; + right:-4px; + left:auto; + top:50%; + -webkit-transform:translatey(-50%) rotateZ(45deg); + transform:translatey(-50%) rotateZ(45deg) + } + .tooltip-sm.tooltip-container{ + width:120px + } + .tooltip-sm.tooltip-container .tooltip-content{ + white-space:normal + } + .tooltip-md.tooltip-container{ + width:240px + } + .tooltip-md.tooltip-container .tooltip-content{ + white-space:normal + } + .tooltip-lg.tooltip-container{ + width:360px + } + .tooltip-lg.tooltip-container .tooltip-content{ + white-space:normal + } + .tether-element{ + z-index:99 + } + .overlay-trigger{ + color:#1B78B3; + -webkit-transition:all 300ms ease-out; + transition:all 300ms ease-out; + -webkit-transition-property:background-color, color, opacity; + transition-property:background-color, color, opacity + } + .overlay-trigger:hover,.overlay-trigger:focus{ + color:#1f8ace; + cursor:pointer; + outline:none; + text-decoration:none + } + .overlay-trigger:active,.overlay-trigger.active{ + color:#176698 + } `; const Tooltip = function(props: *) { diff --git a/app/embeds/components/Frame.js b/app/embeds/components/Frame.js index 1a323aa33..1b0e3387a 100644 --- a/app/embeds/components/Frame.js +++ b/app/embeds/components/Frame.js @@ -70,7 +70,7 @@ const Rounded = styled.div` const Iframe = styled.iframe` border: 1px solid; - border-color: #ddd #ddd #ccc; + border-color: ${props => props.theme.embedBorder}; border-radius: 3px; `; diff --git a/app/index.js b/app/index.js index 10479c416..a12c3252b 100644 --- a/app/index.js +++ b/app/index.js @@ -2,17 +2,15 @@ import * as React from 'react'; import { render } from 'react-dom'; import { Provider } from 'mobx-react'; -import { ThemeProvider } from 'styled-components'; import { BrowserRouter as Router } from 'react-router-dom'; import stores from 'stores'; -import theme from 'shared/styles/theme'; -import GlobalStyles from 'shared/styles/globals'; import 'shared/styles/prism.css'; import ErrorBoundary from 'components/ErrorBoundary'; import ScrollToTop from 'components/ScrollToTop'; import Toasts from 'components/Toasts'; +import Theme from 'components/Theme'; import Routes from './routes'; let DevTools; @@ -25,10 +23,9 @@ const element = document.getElementById('root'); if (element) { render( - - - - + + + @@ -37,9 +34,9 @@ if (element) { - - - + + + {DevTools && } , element diff --git a/app/menus/AccountMenu.js b/app/menus/AccountMenu.js index 257bad6ae..a2dec29f8 100644 --- a/app/menus/AccountMenu.js +++ b/app/menus/AccountMenu.js @@ -3,8 +3,11 @@ import * as React from 'react'; import { Redirect } from 'react-router-dom'; import { observable } from 'mobx'; import { inject, observer } from 'mobx-react'; +import { MoonIcon } from 'outline-icons'; +import styled, { withTheme } from 'styled-components'; import UiStore from 'stores/UiStore'; import AuthStore from 'stores/AuthStore'; +import Flex from 'shared/components/Flex'; import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu'; import { developers, @@ -18,6 +21,7 @@ type Props = { label: React.Node, ui: UiStore, auth: AuthStore, + theme: Object, }; @observer @@ -42,6 +46,8 @@ class AccountMenu extends React.Component { render() { if (this.redirectTo) return ; + const { ui, theme } = this.props; + const isLightTheme = ui.theme === 'light'; return ( {
Logout +
+ + + Night Mode{' '} + + +
); } } -export default inject('ui', 'auth')(AccountMenu); +const NightMode = styled(Flex)` + width: 100%; +`; + +export default inject('ui', 'auth')(withTheme(AccountMenu)); diff --git a/app/scenes/Collection.js b/app/scenes/Collection.js index f8ac69580..1200c35b4 100644 --- a/app/scenes/Collection.js +++ b/app/scenes/Collection.js @@ -4,7 +4,7 @@ import { observable } from 'mobx'; import { observer, inject } from 'mobx-react'; import { Redirect, Link, Switch, Route } from 'react-router-dom'; -import styled from 'styled-components'; +import styled, { withTheme } from 'styled-components'; import { CollectionIcon, PrivateCollectionIcon, @@ -43,6 +43,7 @@ type Props = { documents: DocumentsStore, collections: CollectionsStore, match: Object, + theme: Object, }; @observer @@ -115,7 +116,7 @@ class CollectionScene extends React.Component { } render() { - const { documents } = this.props; + const { documents, theme } = this.props; if (this.redirectTo) return ; if (!this.isFetching && !this.collection) return ; @@ -139,7 +140,7 @@ class CollectionScene extends React.Component { -    @@ -184,6 +185,7 @@ class CollectionScene extends React.Component { id={collection.id} key={collection.description} defaultValue={collection.description} + theme={theme} readOnly /> )} @@ -289,4 +291,6 @@ const Wrapper = styled(Flex)` margin: 10px 0; `; -export default inject('collections', 'documents', 'ui')(CollectionScene); +export default inject('collections', 'documents', 'ui')( + withTheme(CollectionScene) +); diff --git a/app/scenes/Document/components/Header.js b/app/scenes/Document/components/Header.js index 972392006..cfb796193 100644 --- a/app/scenes/Document/components/Header.js +++ b/app/scenes/Document/components/Header.js @@ -7,6 +7,7 @@ import { Redirect } from 'react-router-dom'; import styled from 'styled-components'; import breakpoint from 'styled-components-breakpoint'; import { NewDocumentIcon } from 'outline-icons'; +import { transparentize } from 'polished'; import Document from 'models/Document'; import AuthStore from 'stores/AuthStore'; import { documentEditUrl } from 'utils/routeHelpers'; @@ -227,9 +228,9 @@ const Actions = styled(Flex)` right: 0; left: 0; z-index: 1; - background: rgba(255, 255, 255, 0.9); + background: ${props => transparentize(0.1, props.theme.background)}; border-bottom: 1px solid - ${props => (props.isCompact ? props.theme.smoke : 'transparent')}; + ${props => (props.isCompact ? props.theme.background : 'transparent')}; padding: 12px; transition: all 100ms ease-out; transform: translate3d(0, 0, 0); diff --git a/app/scenes/Search/components/SearchField.js b/app/scenes/Search/components/SearchField.js index a6697b9d9..56d5e5bf9 100644 --- a/app/scenes/Search/components/SearchField.js +++ b/app/scenes/Search/components/SearchField.js @@ -1,6 +1,5 @@ // @flow import * as React from 'react'; -import { lighten } from 'polished'; import styled, { withTheme } from 'styled-components'; import { SearchIcon } from 'outline-icons'; import Flex from 'shared/components/Flex'; @@ -26,8 +25,8 @@ class SearchField extends React.Component { props.theme.smoke}; + background: ${props => props.theme.sidebarBackground}; border-radius: 4px; + color: ${props => props.theme.text}; + ::-webkit-search-cancel-button { -webkit-appearance: searchfield-cancel-button; } - ::-webkit-input-placeholder { - color: ${props => props.theme.slate}; + color: ${props => props.theme.placeholder}; } :-moz-placeholder { - color: ${props => props.theme.slate}; + color: ${props => props.theme.placeholder}; } ::-moz-placeholder { - color: ${props => props.theme.slate}; + color: ${props => props.theme.placeholder}; } :-ms-input-placeholder { - color: ${props => props.theme.slate}; + color: ${props => props.theme.placeholder}; } `; diff --git a/app/scenes/Settings/Profile.js b/app/scenes/Settings/Profile.js index 49567c100..ed5eb23a4 100644 --- a/app/scenes/Settings/Profile.js +++ b/app/scenes/Settings/Profile.js @@ -126,7 +126,7 @@ class Profile extends React.Component { } const DangerZone = styled.div` - background: #fff; + background: ${props => props.theme.background}; position: absolute; bottom: 16px; `; diff --git a/app/scenes/Settings/Shares.js b/app/scenes/Settings/Shares.js index 73382373b..acd91b237 100644 --- a/app/scenes/Settings/Shares.js +++ b/app/scenes/Settings/Shares.js @@ -8,6 +8,7 @@ import AuthStore from 'stores/AuthStore'; import ShareListItem from './components/ShareListItem'; import List from 'components/List'; import CenteredContent from 'components/CenteredContent'; +import Subheading from 'components/Subheading'; import PageTitle from 'components/PageTitle'; import HelpText from 'components/HelpText'; @@ -46,6 +47,7 @@ class Shares extends React.Component { sharing in security settings. )} + Shared Documents {shares.orderedData.map(share => ( diff --git a/app/stores/UiStore.js b/app/stores/UiStore.js index de704082e..5e2c36477 100644 --- a/app/stores/UiStore.js +++ b/app/stores/UiStore.js @@ -7,6 +7,8 @@ import Collection from 'models/Collection'; import type { Toast } from '../types'; class UiStore { + @observable + theme: 'light' | 'dark' = window.localStorage.getItem('theme') || 'light'; @observable activeModalName: ?string; @observable activeModalProps: ?Object; @observable activeDocumentId: ?string; @@ -16,6 +18,12 @@ class UiStore { @observable mobileSidebarVisible: boolean = false; @observable toasts: Map = new Map(); + @action + toggleDarkMode = () => { + this.theme = this.theme === 'dark' ? 'light' : 'dark'; + window.localStorage.setItem('theme', this.theme); + }; + @action setActiveModal = (name: string, props: ?Object): void => { this.activeModalName = name; diff --git a/package.json b/package.json index 05db03cb8..fd2e1fefe 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "mobx-react": "^5.4.2", "natural-sort": "^1.0.0", "nodemailer": "^4.4.0", - "outline-icons": "^1.6.0", + "outline-icons": "^1.7.0", "oy-vey": "^0.10.0", "pg": "^6.1.5", "pg-hstore": "2.3.2", diff --git a/server/static/dev.html b/server/static/dev.html index f3962d071..28e6b2c83 100644 --- a/server/static/dev.html +++ b/server/static/dev.html @@ -20,21 +20,15 @@ flex: 1; min-height: 100vh; } - - #sidebar-placeholder { - position: fixed; - top: 0; - bottom: 0; - left: 0; - width: 280px; - background: #F4F7FA; - } -
- -
+
+ \ No newline at end of file diff --git a/server/static/index.html b/server/static/index.html index 49a454d45..87eef72b8 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -23,21 +23,15 @@ flex: 1; min-height: 100vh; } - - #sidebar-placeholder { - position: fixed; - top: 0; - bottom: 0; - left: 0; - width: 280px; - background: #F4F7FA; - } -
- -
+
+ diff --git a/server/utils/renderpage.js b/server/utils/renderpage.js index f4007dd3c..53d169586 100644 --- a/server/utils/renderpage.js +++ b/server/utils/renderpage.js @@ -8,7 +8,7 @@ import { ThemeProvider, } from 'styled-components'; import Layout from '../pages/components/Layout'; -import theme from '../../shared/styles/theme'; +import { light } from '../../shared/styles/theme'; const sheet = new ServerStyleSheet(); @@ -28,7 +28,7 @@ export default function renderpage(ctx: Object, children: React.Node) { const html = ReactDOMServer.renderToString( - + {children} diff --git a/shared/components/TeamLogo.js b/shared/components/TeamLogo.js index 6d9f2b09f..c6f85bce7 100644 --- a/shared/components/TeamLogo.js +++ b/shared/components/TeamLogo.js @@ -1,12 +1,13 @@ // @flow import styled from 'styled-components'; +import { darken } from 'polished'; const TeamLogo = styled.img` width: 38px; height: 38px; border-radius: 4px; background: ${props => props.theme.white}; - border: 1px solid ${props => props.theme.slateLight}; + border: 1px solid ${props => darken(0.1, props.theme.sidebarBackground)}; `; export default TeamLogo; diff --git a/shared/styles/base.js b/shared/styles/base.js deleted file mode 100644 index 4231af751..000000000 --- a/shared/styles/base.js +++ /dev/null @@ -1,84 +0,0 @@ -// @flow -import theme from './theme'; - -export default ` - @font-face { - font-family: 'Lato'; - src: url('/fonts/LatoLatin-Semibold.woff') format('woff'); - } - - * { - box-sizing: border-box; - } - - html, - body { - width: 100%; - min-height: 100vh; - margin: 0; - padding: 0; - } - - body, - button, - input, - optgroup, - select, - textarea { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, - Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; - } - - body { - font-size: 16px; - line-height: 1.5; - color: ${theme.text}; - - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; - } - - a { - color: ${theme.blue}; - text-decoration: none; - cursor: pointer; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: 'Lato',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif; - font-weight: 500; - line-height: 1.25; - margin-top: 1em; - margin-bottom: 0.5em; - color: ${theme.text}; - } - h1 { font-size: 2.25em; } - h2 { font-size: 1.5em; } - h3 { font-size: 1.25em; } - h4 { font-size: 1em; } - h5 { font-size: 0.875em; } - h6 { font-size: 0.75em; } - - p, - dl, - ol, - ul, - pre, - blockquote { - margin-top: 1em; - margin-bottom: 1em; - } - - hr { - border: 0; - height: 0; - border-top: 1px solid rgba(0, 0, 0, 0.1); - border-bottom: 1px solid rgba(255, 255, 255, 0.3); - } -`; diff --git a/shared/styles/globals.js b/shared/styles/globals.js index 0f36fd88a..3639bf893 100644 --- a/shared/styles/globals.js +++ b/shared/styles/globals.js @@ -1,9 +1,86 @@ // @flow import styledNormalize from 'styled-normalize'; import { createGlobalStyle } from 'styled-components'; -import base from './base'; export default createGlobalStyle` ${styledNormalize} - ${base} + + @font-face { + font-family: 'Lato'; + src: url('/fonts/LatoLatin-Semibold.woff') format('woff'); + } + + * { + box-sizing: border-box; + } + + html, + body { + width: 100%; + min-height: 100vh; + margin: 0; + padding: 0; + } + + body, + button, + input, + optgroup, + select, + textarea { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, + Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + } + + body { + font-size: 16px; + line-height: 1.5; + color: ${props => props.theme.text}; + + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + } + + a { + color: ${props => props.theme.primary}; + text-decoration: none; + cursor: pointer; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: 'Lato',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif; + font-weight: 500; + line-height: 1.25; + margin-top: 1em; + margin-bottom: 0.5em; + color: ${props => props.theme.text}; + } + h1 { font-size: 2.25em; } + h2 { font-size: 1.5em; } + h3 { font-size: 1.25em; } + h4 { font-size: 1em; } + h5 { font-size: 0.875em; } + h6 { font-size: 0.75em; } + + p, + dl, + ol, + ul, + pre, + blockquote { + margin-top: 1em; + margin-bottom: 1em; + } + + hr { + border: 0; + height: 0; + border-top: 1px solid ${props => props.theme.divider}; + } `; diff --git a/shared/styles/theme.js b/shared/styles/theme.js index b8dc020c0..b480663ae 100644 --- a/shared/styles/theme.js +++ b/shared/styles/theme.js @@ -1,18 +1,10 @@ // @flow -const theme = { - fontFamily: - "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif", - monospaceFontFamily: `Menlo, Consolas, 'Liberation Mono', monospace;`, - fontWeight: 400, +import { darken, lighten } from 'polished'; - text: '#171B35', - link: '#1AB6FF', - primary: '#1AB6FF', - placeholder: '#b1becc', - danger: '#D0021B', - warning: '#f08a24', - success: '#2f3336', - info: '#a0d3e8', +const colors = { + almostBlack: '#111319', + lightBlack: '#2F3336', + almostWhite: '#E6E6E6', slate: '#9BA6B2', slateLight: '#DAE1E9', @@ -22,19 +14,131 @@ const theme = { smokeLight: '#F9FBFC', smokeDark: '#E8EBED', - white: '#FFFFFF', - blue: '#1AB6FF', - black: '#000000', - blackLight: '#2f3336', + white: '#FFF', + white10: 'rgba(255, 255, 255, 0.1)', + black: '#000', + black05: 'rgba(0, 0, 0, 0.05)', + black10: 'rgba(0, 0, 0, 0.1)', + black50: 'rgba(0, 0, 0, 0.50)', + primary: '#1AB6FF', + yellow: '#FBCA04', + danger: '#D0021B', + warning: '#f08a24', + success: '#2f3336', + info: '#a0d3e8', +}; + +const spacing = { padding: '1.5vw 1.875vw', vpadding: '1.5vw', hpadding: '1.875vw', sidebarWidth: '280px', sidebarMinWidth: '250px', sidebarMaxWidth: '350px', - - contentHeaderBackground: 'hsl(180, 58%, 85%)', }; -export default theme; +export const base = { + ...colors, + ...spacing, + fontFamily: + "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen, Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif", + fontWeight: 400, + link: colors.primary, +}; + +export const light = { + ...base, + background: colors.white, + + text: colors.almostBlack, + textSecondary: colors.slateDark, + textTertiary: colors.slate, + placeholder: '#B1BECC', + + sidebarBackground: 'rgb(244, 247, 250)', + sidebarItemBackground: colors.black05, + sidebarText: 'rgb(78, 92, 110)', + + menuBackground: colors.white, + menuShadow: + '0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 8px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.08)', + divider: colors.slateLight, + inputBorder: colors.slateLight, + inputBorderFocused: colors.slate, + + listItemHoverBackground: colors.smoke, + listItemHoverBorder: colors.smokeDark, + + toolbarBackground: colors.lightBlack, + toolbarInput: colors.white10, + toolbarItem: colors.white, + + buttonBackground: colors.lightBlack, + buttonText: colors.white, + buttonNeutralBackground: colors.white, + buttonNeutralText: colors.almostBlack, + + tooltipBackground: colors.almostBlack, + tooltipText: colors.white, + + blockToolbarBackground: colors.smoke, + blockToolbarTrigger: colors.slate, + blockToolbarTriggerIcon: colors.white, + blockToolbarItem: colors.almostBlack, + + quote: colors.slateLight, + codeBackground: colors.smoke, + codeBorder: colors.smokeDark, + embedBorder: '#DDD #DDD #CCC', + horizontalRule: colors.smokeDark, +}; + +export const dark = { + ...base, + background: colors.almostBlack, + + text: colors.almostWhite, + textSecondary: lighten(0.2, colors.slate), + textTertiary: colors.slate, + placeholder: darken(0.5, '#B1BECC'), + + sidebarBackground: colors.black50, + sidebarItemBackground: colors.black50, + sidebarText: colors.slate, + + menuBackground: lighten(0.015, colors.almostBlack), + menuShadow: + '0 0 0 1px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.08)', + divider: colors.slate, + inputBorder: colors.slateDark, + inputBorderFocused: colors.slate, + + listItemHoverBackground: colors.black10, + listItemHoverBorder: colors.black50, + + toolbarBackground: colors.white, + toolbarInput: colors.black10, + toolbarItem: colors.lightBlack, + + buttonBackground: colors.white, + buttonText: colors.lightBlack, + buttonNeutralBackground: colors.almostBlack, + buttonNeutralText: colors.white, + + tooltipBackground: colors.white, + tooltipText: colors.lightBlack, + + blockToolbarBackground: colors.white, + blockToolbarTrigger: colors.almostWhite, + blockToolbarTriggerIcon: colors.almostBlack, + blockToolbarItem: colors.lightBlack, + + quote: colors.almostWhite, + codeBackground: colors.almostBlack, + codeBorder: colors.black50, + embedBorder: colors.black50, + horizontalRule: colors.almostWhite, +}; + +export default light; diff --git a/yarn.lock b/yarn.lock index a726d58ac..daf4fed12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7038,6 +7038,10 @@ outline-icons@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.6.0.tgz#6c7897d354e6bd77ca5498cd3a989b8cb9482574" +outline-icons@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/outline-icons/-/outline-icons-1.7.0.tgz#093f2f18c80bf5577bc31a6ff41460f2feb76fb7" + oy-vey@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/oy-vey/-/oy-vey-0.10.0.tgz#16160f837f0ea3d0340adfc2377ba93d1ed9ce76"