diff --git a/components/BottomShare/index.js b/components/BottomShare/index.js new file mode 100644 index 0000000..6662a13 --- /dev/null +++ b/components/BottomShare/index.js @@ -0,0 +1,21 @@ +// @flow +import React from 'react'; +import Card from '../Card'; +import ShareButtons from '../ShareButtons'; +import { CardContent, TopBorder } from './style'; +import { SmallHeading, SmallSubheading } from '../Page/style'; + +const BottomShare = () => ( + + + + Feeling more secure? + + Spread the word about improving online privacy and security + + + + +); + +export default BottomShare; diff --git a/components/BottomShare/style.js b/components/BottomShare/style.js new file mode 100644 index 0000000..aa08645 --- /dev/null +++ b/components/BottomShare/style.js @@ -0,0 +1,23 @@ +// @flow +import styled from 'styled-components'; + +export const CardContent = styled.div` + padding: 24px; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + text-align: center; + + @media (max-width: 768px) { + padding-top: 40px; + } +`; + +export const TopBorder = styled.div` + width: 100%; + height: 4px; + background-image: linear-gradient(to left, #a913de, #6ac9ff); + border-radius: 6px 6px 0 0; +`; diff --git a/components/Checklist/style.js b/components/Checklist/style.js index 535ac4f..342e70a 100644 --- a/components/Checklist/style.js +++ b/components/Checklist/style.js @@ -4,6 +4,6 @@ import styled from 'styled-components'; export const Grid = styled.div` display: grid; grid-template-columns: 1fr; - margin-top: 32px; + margin-top: 48px; width: 100%; `; diff --git a/components/ChecklistItem/Heading.js b/components/ChecklistItem/Heading.js index 2e9936e..77c9fb5 100644 --- a/components/ChecklistItem/Heading.js +++ b/components/ChecklistItem/Heading.js @@ -1,15 +1,22 @@ // @flow import React from 'react'; -import { Title, Description } from './style'; +import { Title, Description, Uncollapse } from './style'; import type { ChecklistResource } from '../../types'; type Props = { resource: ChecklistResource, + isCollapsed: boolean, + handleCollapse: Function, }; -export const Heading = ({ resource }: Props) => ( +export const Heading = ({ resource, isCollapsed, handleCollapse }: Props) => ( - {resource.title} - + + {resource.title} + <Uncollapse onClick={handleCollapse}> + {isCollapsed ? 'Show details' : 'Hide details'} + </Uncollapse> + + {!isCollapsed && } ); diff --git a/components/ChecklistItem/index.js b/components/ChecklistItem/index.js index 5c74a60..c9fbeb1 100644 --- a/components/ChecklistItem/index.js +++ b/components/ChecklistItem/index.js @@ -23,26 +23,34 @@ type Props = { type State = { isChecked: boolean, isLoading: boolean, + isCollapsed: boolean, }; class ChecklistItem extends React.Component { - state = { isChecked: false, isLoading: true }; + state = { isChecked: false, isLoading: true, isCollapsed: true }; componentDidMount() { const { resource } = this.props; const isChecked = getCheckedStatusById(resource.id); - return this.setState({ isChecked, isLoading: false }); + return this.setState({ + isChecked, + isLoading: false, + isCollapsed: isChecked, + }); } handleSetChecked = () => { const { isChecked } = this.state; const { resource } = this.props; setCheckedStatusById(resource.id, !isChecked); - return this.setState({ isChecked: !isChecked }); + return this.setState({ isChecked: !isChecked, isCollapsed: !isChecked }); }; + uncollapse = () => + this.setState(state => ({ isCollapsed: !state.isCollapsed })); + render() { - const { isChecked, isLoading } = this.state; + const { isChecked, isLoading, isCollapsed } = this.state; const { resource } = this.props; if (isLoading) return ; @@ -50,21 +58,26 @@ class ChecklistItem extends React.Component { return ( - + - - - {resource.apps && ( + + + + {!isCollapsed && resource.apps && ( )} - {resource.resources && ( + {!isCollapsed && resource.resources && ( diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index ddc17ff..cacc8e6 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -14,7 +14,7 @@ export const CardContent = styled.div` width: 100%; @media (max-width: 768px) { - flex-direction: column; + flex-direction: ${props => (props.isCollapsed ? 'row' : 'column')}; } `; @@ -22,6 +22,10 @@ export const Title = styled.h3` font-size: 18px; font-weight: 500; color: ${theme.text.default}; + display: flex; + line-height: 1.2; + justify-content: space-between; + align-items: center; `; export const Description = styled(Markdown)` @@ -72,16 +76,25 @@ export const Checkbox = styled.span` height: 32px; border-radius: 4px; border: 1px solid - ${props => (props.isChecked ? theme.brand.default : theme.border.default)}; + ${props => (props.isChecked ? theme.bg.default : theme.border.default)}; background: ${props => - props.isChecked ? theme.brand.default : theme.bg.wash}; + props.isChecked ? theme.success.default : theme.bg.wash}; cursor: pointer; position: relative; + background-image: ${props => + props.isChecked + ? 'radial-gradient(circle at top right, #a913de, #6ac9ff)' + : 'none'}; + box-shadow: ${props => + props.isChecked ? 'inset 0 0 1px rgba(0,0,0,0.4)' : 'none'}; &:hover { - ${Shadows.default}; - border: 1px solid - ${props => (props.isChecked ? theme.brand.default : theme.border.active)}; + ${props => !props.isChecked && Shadows.default}; + background: ${theme.bg.default}; + background-image: ${props => + props.isChecked + ? 'radial-gradient(circle at top right, #a913de, #6ac9ff)' + : 'none'}; } &:after { @@ -102,12 +115,15 @@ export const Checkbox = styled.span` export const ResourceContent = styled.div` display: flex; flex-direction: column; + margin-top: 2px; padding-left: 16px; width: 100%; + justify-content: ${props => (props.isCollapsed ? 'center' : 'flex-start')}; @media (max-width: 768px) { - padding-left: 8px; - margin-top: 24px; + padding-left: ${props => (props.isCollapsed ? '16px' : '8px')}; + margin-top: ${props => (props.isCollapsed ? '0px' : '24px')}; + justify-content: ${props => (props.isCollapsed ? 'center' : 'flex-start')}; } `; @@ -302,3 +318,26 @@ export const Divider = styled.div` display: none; } `; + +export const Uncollapse = styled.span` + background: ${theme.bg.wash}; + border-radius: 20px; + padding: 8px 16px; + font-size: 15px; + color: ${theme.text.tertiary}; + cursor: pointer; + line-height: 1; + display: flex; + align-items: center; + justify-content: center; + margin-right: 8px; + + @media (max-width: 768px) { + display: none; + } + + &:hover { + color: ${theme.text.default}; + background: ${tint(theme.bg.wash, -4)}; + } +`; diff --git a/components/Header/Logo.js b/components/Header/Logo.js index 211599b..310bfec 100644 --- a/components/Header/Logo.js +++ b/components/Header/Logo.js @@ -9,22 +9,26 @@ const Logo = () => ( fill="none" xmlns="http://www.w3.org/2000/svg" > - - + @@ -33,9 +37,9 @@ const Logo = () => ( diff --git a/components/LoadingChecklistItem/index.js b/components/LoadingChecklistItem/index.js index ca79e8e..1065f72 100644 --- a/components/LoadingChecklistItem/index.js +++ b/components/LoadingChecklistItem/index.js @@ -4,7 +4,7 @@ import { ShimmerInboxThread, ShimmerBase, ShimmerLine, Cover } from './style'; import Card from '../Card'; const LoadingChecklistItem = () => ( -
+
diff --git a/components/Page/index.js b/components/Page/index.js index 83612f1..c3075cc 100644 --- a/components/Page/index.js +++ b/components/Page/index.js @@ -13,14 +13,12 @@ import { SectionHeading, Heading, Subheading, - LargeHeading, - LargeSubheading, InnerContainer, ScrollToTop, } from './style'; import * as gtag from '../../lib/gtag'; -export { SectionHeading, Heading, Subheading, LargeHeading, LargeSubheading }; +export { SectionHeading, Heading, Subheading }; type Props = { children: Node, diff --git a/components/Page/style.js b/components/Page/style.js index 903f6f7..20b4973 100644 --- a/components/Page/style.js +++ b/components/Page/style.js @@ -75,12 +75,16 @@ export const Subheading = styled.h4` } `; -export const LargeHeading = styled(Heading)` - font-size: 32px; +export const SmallHeading = styled(Heading)` + font-size: 30px; + margin-bottom: 4px; + margin-top: 8px; `; -export const LargeSubheading = styled(Subheading)` +export const SmallSubheading = styled(Subheading)` font-size: 20px; + margin-top: 4px; + margin-bottom: 32px; `; export const ScrollToTop = styled.button` @@ -97,6 +101,7 @@ export const ScrollToTop = styled.button` transition: all 0.2s ease-in-out; opacity: ${props => (props.isVisible ? '1' : '0')}; background: ${theme.text.default}; + background-image: radial-gradient(circle at top right, #a913de, #6ac9ff); color: ${theme.bg.default}; transform: translateY(${props => (props.isVisible ? '0' : '80px')}); cursor: pointer; diff --git a/components/ShareButtons/style.js b/components/ShareButtons/style.js index 5946ddd..45e9a2a 100644 --- a/components/ShareButtons/style.js +++ b/components/ShareButtons/style.js @@ -5,8 +5,6 @@ export const Container = styled.div` display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 16px; - margin-top: 40px; - margin-bottom: 16px; width: 100%; button { diff --git a/config/browsers.js b/config/browsers.js index aa7257a..3174673 100644 --- a/config/browsers.js +++ b/config/browsers.js @@ -30,6 +30,15 @@ export default { android: 'https://www.mozilla.org/en-US/firefox/mobile/', }, }, + { + name: 'Safari', + image: '/static/img/safari.jpg', + url: 'https://www.apple.com/safari/', + sources: { + windows: 'https://www.apple.com/safari/', + macos: 'https://www.apple.com/safari/', + }, + }, { name: 'Tor', image: '/static/img/tor.png', diff --git a/config/data.js b/config/data.js index 38b7676..e544ed6 100644 --- a/config/data.js +++ b/config/data.js @@ -19,15 +19,15 @@ export default { strongDevicePasscode, twoFactor, carrierPin, - creditFreeze, encryptedHardware, + creditFreeze, dns, vpn, + webcam, browsers, searchEngine, email, appPermissions, - webcam, socialMedia, phishing, }; diff --git a/cypress/integration/home_spec.js b/cypress/integration/home_spec.js index fbe5eec..4213161 100644 --- a/cypress/integration/home_spec.js +++ b/cypress/integration/home_spec.js @@ -1,3 +1,5 @@ +import data from '../../config/data'; + describe('Home', () => { before(() => { cy.visit('/'); @@ -23,4 +25,11 @@ describe('Home', () => { 'be.visible' ); }); + + it('should render content', () => { + const dataKeys = Object.keys(data); + dataKeys.map(key => { + cy.contains(data[key].title); + }); + }); }); diff --git a/pages/index.js b/pages/index.js index dce05c1..625a04c 100644 --- a/pages/index.js +++ b/pages/index.js @@ -4,6 +4,7 @@ import Page, { SectionHeading, Heading, Subheading } from '../components/Page'; import type { GetInitialProps } from '../types'; import Checklist from '../components/Checklist'; import ShareButtons from '../components/ShareButtons'; +import BottomShare from '../components/BottomShare'; class Index extends React.Component<{}> { static async getInitialProps({ res }: GetInitialProps) { @@ -22,14 +23,19 @@ class Index extends React.Component<{}> { Be safe on the internet. - An open source collection of resources designed to improve your - online privacy and security. + An open source checklist of resources designed to improve your + online privacy and security. Check things off to keep track as you + go. +
+ + + ); } diff --git a/static/android-icon-144x144.png b/static/android-icon-144x144.png index f3594a0..266176c 100644 Binary files a/static/android-icon-144x144.png and b/static/android-icon-144x144.png differ diff --git a/static/android-icon-192x192.png b/static/android-icon-192x192.png index a3300c5..16b7a57 100644 Binary files a/static/android-icon-192x192.png and b/static/android-icon-192x192.png differ diff --git a/static/android-icon-36x36.png b/static/android-icon-36x36.png index f7848c3..9b83f81 100644 Binary files a/static/android-icon-36x36.png and b/static/android-icon-36x36.png differ diff --git a/static/android-icon-48x48.png b/static/android-icon-48x48.png index 708bbe5..c91694d 100644 Binary files a/static/android-icon-48x48.png and b/static/android-icon-48x48.png differ diff --git a/static/android-icon-72x72.png b/static/android-icon-72x72.png index 7ceb0b6..9c4f4c0 100644 Binary files a/static/android-icon-72x72.png and b/static/android-icon-72x72.png differ diff --git a/static/android-icon-96x96.png b/static/android-icon-96x96.png index 2dac94b..a089eab 100644 Binary files a/static/android-icon-96x96.png and b/static/android-icon-96x96.png differ diff --git a/static/apple-icon-114x114.png b/static/apple-icon-114x114.png index dcaedba..53947f2 100644 Binary files a/static/apple-icon-114x114.png and b/static/apple-icon-114x114.png differ diff --git a/static/apple-icon-120x120.png b/static/apple-icon-120x120.png index fc595f3..f0abcb9 100644 Binary files a/static/apple-icon-120x120.png and b/static/apple-icon-120x120.png differ diff --git a/static/apple-icon-144x144.png b/static/apple-icon-144x144.png index f3594a0..266176c 100644 Binary files a/static/apple-icon-144x144.png and b/static/apple-icon-144x144.png differ diff --git a/static/apple-icon-152x152.png b/static/apple-icon-152x152.png index dc0e378..e97cbbf 100644 Binary files a/static/apple-icon-152x152.png and b/static/apple-icon-152x152.png differ diff --git a/static/apple-icon-180x180.png b/static/apple-icon-180x180.png index e903fad..6634e95 100644 Binary files a/static/apple-icon-180x180.png and b/static/apple-icon-180x180.png differ diff --git a/static/apple-icon-57x57.png b/static/apple-icon-57x57.png index 3272046..22f72c5 100644 Binary files a/static/apple-icon-57x57.png and b/static/apple-icon-57x57.png differ diff --git a/static/apple-icon-60x60.png b/static/apple-icon-60x60.png index f45ae31..669b3b8 100644 Binary files a/static/apple-icon-60x60.png and b/static/apple-icon-60x60.png differ diff --git a/static/apple-icon-72x72.png b/static/apple-icon-72x72.png index 7ceb0b6..9c4f4c0 100644 Binary files a/static/apple-icon-72x72.png and b/static/apple-icon-72x72.png differ diff --git a/static/apple-icon-76x76.png b/static/apple-icon-76x76.png index 59fcc93..3de1155 100644 Binary files a/static/apple-icon-76x76.png and b/static/apple-icon-76x76.png differ diff --git a/static/apple-icon-precomposed.png b/static/apple-icon-precomposed.png index 5897c1f..2d5d9cf 100644 Binary files a/static/apple-icon-precomposed.png and b/static/apple-icon-precomposed.png differ diff --git a/static/apple-icon.png b/static/apple-icon.png index 5897c1f..2d5d9cf 100644 Binary files a/static/apple-icon.png and b/static/apple-icon.png differ diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png index 628ee61..7c5db09 100644 Binary files a/static/favicon-16x16.png and b/static/favicon-16x16.png differ diff --git a/static/favicon-32x32.png b/static/favicon-32x32.png index f74f51b..5de2630 100644 Binary files a/static/favicon-32x32.png and b/static/favicon-32x32.png differ diff --git a/static/favicon-96x96.png b/static/favicon-96x96.png index 2dac94b..a089eab 100644 Binary files a/static/favicon-96x96.png and b/static/favicon-96x96.png differ diff --git a/static/favicon.ico b/static/favicon.ico index 0c2b0d4..5ac692e 100644 Binary files a/static/favicon.ico and b/static/favicon.ico differ diff --git a/static/img/safari.jpg b/static/img/safari.jpg new file mode 100644 index 0000000..20d19c3 Binary files /dev/null and b/static/img/safari.jpg differ diff --git a/static/ms-icon-144x144.png b/static/ms-icon-144x144.png index f3594a0..266176c 100644 Binary files a/static/ms-icon-144x144.png and b/static/ms-icon-144x144.png differ diff --git a/static/ms-icon-150x150.png b/static/ms-icon-150x150.png index 3230170..f7c13ff 100644 Binary files a/static/ms-icon-150x150.png and b/static/ms-icon-150x150.png differ diff --git a/static/ms-icon-310x310.png b/static/ms-icon-310x310.png index af428ac..e45b34e 100644 Binary files a/static/ms-icon-310x310.png and b/static/ms-icon-310x310.png differ diff --git a/static/ms-icon-70x70.png b/static/ms-icon-70x70.png index 71b103e..4afbe6d 100644 Binary files a/static/ms-icon-70x70.png and b/static/ms-icon-70x70.png differ diff --git a/static/og-image.png b/static/og-image.png index fe224b3..5880c87 100644 Binary files a/static/og-image.png and b/static/og-image.png differ