Merge branch 'master' into patch-2

This commit is contained in:
Brian Lovin 2019-02-08 15:08:29 -08:00 committed by GitHub
commit 815630dd4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 475 additions and 218 deletions

View file

@ -23,4 +23,4 @@ unclear-type=warn
unsafe-getters-setters=error
[version]
0.91.0
0.92.1

View file

@ -1,16 +1,10 @@
// @flow
// $FlowIssue
import React, { useState } from 'react';
import dynamic from 'next/dynamic';
import * as Styled from './style';
import { CopyLinkButton as StyledCopyLinkButton } from './style';
import Icon from '../Icon';
import type { ButtonProps } from './types';
const Clipboard = dynamic(() => import('react-clipboard.js'), {
ssr: false,
loading: () => null,
});
type CopyLinkProps = {
...$Exact<ButtonProps>,
text: string,
@ -26,20 +20,18 @@ export default function CopyLinkButton(props: CopyLinkProps) {
};
return (
<Clipboard
style={{ background: 'none' }}
<StyledCopyLinkButton
data-clipboard-text={text}
onSuccess={onClick}
component="a"
component="button"
data-cy="copy-link-button"
isClicked={isClicked}
aria-label="Copy the websites address to your clipboard."
type="button"
{...props}
>
<Styled.CopyLinkButton
data-cy="copy-link-button"
isClicked={isClicked}
{...props}
>
<Icon glyph="link" size={24} />
{isClicked ? 'Copied!' : children}
</Styled.CopyLinkButton>
</Clipboard>
<Icon glyph="link" size={24} />
<span>{isClicked ? 'Copied!' : children}</span>
</StyledCopyLinkButton>
);
}

View file

@ -3,6 +3,12 @@ import styled, { css } from 'styled-components';
import { hexa, tint } from '../globals';
import type { ButtonSize } from './types';
import { theme } from '../theme';
import dynamic from 'next/dynamic';
const Clipboard = dynamic(() => import('react-clipboard.js'), {
ssr: false,
loading: () => null,
});
const getPadding = (size: ButtonSize) => {
switch (size) {
@ -32,7 +38,7 @@ const getFontSize = (size: ButtonSize) => {
}
};
const base = css`
export const base = css`
-webkit-appearance: none;
display: flex;
flex: none;
@ -58,11 +64,15 @@ const base = css`
cursor: not-allowed;
}
&:hover {
&:hover, &:active, &:focus {
transition: all 0.2s ease-in-out;
box-shadow: ${props =>
props.disabled ? 'none' : `${theme.shadows.button}`};
}
&:active, &:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.5)};
}
`;
export const Button = styled.button`
@ -126,7 +136,7 @@ export const PrimaryButton = styled.button`
&:focus {
box-shadow: 0 0 0 1px ${props =>
props.theme.bg.default}, 0 0 0 3px ${props =>
hexa(props.theme.brand.alt, 0.16)};
hexa(props.theme.brand.alt, 0.5)};
}
`;
@ -137,15 +147,15 @@ export const GhostButton = styled.button`
background-color: transparent;
background-image: none;
&:hover {
background: ${props => tint(props.theme.bg.wash, -3)};
&:hover, &:active, &:focus {
background: ${props => tint(props.theme.bg.wash, -8)};
color: ${theme.text.default};
box-shadow: none;
}
&:focus {
&:active, &:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.08)};
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;
@ -220,12 +230,12 @@ export const ButtonSegmentRow = styled.div`
${PrimaryButton} {
&:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.brand.alt, 0.16)};
0 0 0 3px ${props => hexa(props.theme.brand.alt, 0.5)};
}
}
`;
export const FacebookButton = styled.button`
export const FacebookButton = styled.a`
${base}
border: 1px solid ${theme.social.facebook};
color: ${theme.bg.default};
@ -262,11 +272,11 @@ export const FacebookButton = styled.button`
&:focus {
box-shadow: 0 0 0 1px ${props =>
props.theme.bg.default}, 0 0 0 3px ${props =>
hexa(props.theme.social.facebook, 0.16)};
hexa(props.theme.social.facebook, 0.5)};
}
`;
export const TwitterButton = styled.button`
export const TwitterButton = styled.a`
${base}
border: 1px solid ${theme.social.twitter};
color: ${theme.bg.default};
@ -303,12 +313,13 @@ export const TwitterButton = styled.button`
&:focus {
box-shadow: 0 0 0 1px ${props =>
props.theme.bg.default}, 0 0 0 3px ${props =>
hexa(props.theme.social.twitter, 0.16)};
hexa(props.theme.social.twitter, 0.5)};
}
`;
export const CopyLinkButton = styled.button`
export const CopyLinkButton = styled(Clipboard)`
${base}
transition: all ${theme.animations.default};
border: 1px solid ${props =>
props.isClicked
? tint(props.theme.success.default, -10)
@ -356,8 +367,9 @@ export const CopyLinkButton = styled.button`
&:focus {
box-shadow: 0 0 0 1px ${props =>
props.theme.bg.default}, 0 0 0 3px ${props =>
props.isClicked
? hexa(props.theme.success.default, 0.16)
: props.theme.border.default};
props.isClicked
? hexa(props.theme.success.default, 0.5)
: props.theme.border.default};
}
`;

View file

@ -38,8 +38,8 @@ export const AppRow = ({ app }: Props) => {
};
return (
<AppRowContainer href={app.url} target="_blank" rel="noopener noreferrer">
<AppMeta>
<AppRowContainer>
<AppMeta href={app.url} target="_blank" rel="noopener noreferrer" title={`go to: ${app.name}`}>
{app.image && <AppIcon alt={app.name} src={app.image} />}
<AppName>{app.name}</AppName>
</AppMeta>
@ -49,4 +49,4 @@ export const AppRow = ({ app }: Props) => {
{app.offer && <Offer offer={app.offer} />}
</AppRowContainer>
);
};
};

View file

@ -1,23 +1,39 @@
// @flow
// $FlowIssue
import React, { useState } from 'react';
import { AppsContainer, SectionHeading, ExpandContainer } from './style';
import React, { useState, useRef } from 'react';
import { AppsContainer, SectionHeading, ExpandContainer, ExpandContent } from './style';
import { Button } from '../Button';
import type { ChecklistResource } from '../../types';
import { AppRow } from './App';
type Props = {
resource: ChecklistResource,
handleAppsExpand: Function,
};
export const Apps = ({ resource }: Props) => {
export const Apps = ({ resource, handleAppsExpand }: Props) => {
const [overflowExpanded, setOverflowExpanded] = useState(false);
const [contentHeight, setcontentHeight] = useState(2000);
const expandContentContainer = useRef(null);
function handleExpand() {
let expandContentHeight =
expandContentContainer.current
? expandContentContainer.current.scrollHeight
: contentHeight;
setcontentHeight(expandContentHeight)
setOverflowExpanded(!overflowExpanded)
handleAppsExpand(overflowExpanded ? -expandContentHeight : expandContentHeight);
}
if (!resource.apps) return null;
let appList = resource.apps;
let overflowAppList;
if (appList.length > 3) {
if (appList && appList.length > 3) {
overflowAppList = appList.slice(3, appList.length);
appList = appList.slice(0, 3);
}
@ -25,19 +41,34 @@ export const Apps = ({ resource }: Props) => {
return (
<AppsContainer overflowExpanded={overflowExpanded}>
<SectionHeading>Apps</SectionHeading>
{appList.map(app => (
{appList && appList.map(app => (
<AppRow key={app.name} app={app} />
))}
{overflowAppList && !overflowExpanded && (
<ExpandContainer onClick={() => setOverflowExpanded(true)}>
<Button>Show more choices</Button>
</ExpandContainer>
{overflowAppList && (
<React.Fragment>
<ExpandContent
id={`apps_${resource.id}`}
role="region"
tabindex="-1"
ref={expandContentContainer}
style={{ '--maxHeight': `${contentHeight}px` }}
aria-hidden={!overflowExpanded}
>
{overflowAppList.map(app => <AppRow key={app.name} app={app} />)}
</ExpandContent>
<ExpandContainer
onClick={handleExpand}
role="button"
aria-expanded={overflowExpanded}
aria-controls={`apps_${resource.id}`}
>
<Button type="button">Show {overflowExpanded ? "less" : "more"} choices</Button>
</ExpandContainer>
</React.Fragment>
)}
{overflowAppList &&
overflowExpanded &&
overflowAppList.map(app => <AppRow key={app.name} app={app} />)}
</AppsContainer>
);
};

View file

@ -1,6 +1,6 @@
// @flow
import React from 'react';
import { Title, Description, Uncollapse } from './style';
import { Title, Uncollapse } from './style';
import type { ChecklistResource } from '../../types';
type Props = {
@ -13,10 +13,14 @@ export const Heading = ({ resource, isCollapsed, handleCollapse }: Props) => (
<React.Fragment>
<Title>
{resource.title}
<Uncollapse onClick={handleCollapse}>
<Uncollapse
onClick={handleCollapse}
aria-controls={`content_${resource.id}`}
aria-expanded={!isCollapsed}
type="button"
>
{isCollapsed ? 'Show details' : 'Hide details'}
</Uncollapse>
</Title>
{!isCollapsed && <Description source={resource.description} />}
</React.Fragment>
);

View file

@ -10,10 +10,11 @@ import { Resources } from './Resources';
import {
Container,
CheckboxContainer,
Checkbox,
CardContent,
ResourceContent,
Divider,
Description,
Content,
} from './style';
type Props = {
@ -24,10 +25,18 @@ type State = {
isChecked: boolean,
isLoading: boolean,
isCollapsed: boolean,
contentHeight: number,
};
class ChecklistItem extends React.Component<Props, State> {
state = { isChecked: false, isLoading: true, isCollapsed: true };
state = { isChecked: false, isLoading: true, isCollapsed: true, contentHeight: 2000, };
contentContainer: { current: null | HTMLDivElement }
constructor(props: Props) {
super(props);
this.contentContainer = React.createRef();
}
componentDidMount() {
const { resource } = this.props;
@ -39,6 +48,14 @@ class ChecklistItem extends React.Component<Props, State> {
});
}
componentDidUpdate(prevProps: Props, prevState: State) {
if (prevState.isLoading && !this.state.isLoading && this.contentContainer.current) {
return this.setState({
contentHeight: this.contentContainer.current.scrollHeight,
})
}
}
handleSetChecked = () => {
const { isChecked } = this.state;
const { resource } = this.props;
@ -46,11 +63,19 @@ class ChecklistItem extends React.Component<Props, State> {
return this.setState({ isChecked: !isChecked, isCollapsed: !isChecked });
};
uncollapse = () =>
this.setState(state => ({ isCollapsed: !state.isCollapsed }));
uncollapse = () => {
this.setState(state => ({ isCollapsed: !state.isCollapsed }));
this.contentContainer.current && this.contentContainer.current.focus();
};
handleAppsExpand = (appsContainerHeight: number) => {
return this.contentContainer.current && this.setState({
contentHeight: this.contentContainer.current.scrollHeight + appsContainerHeight,
})
}
render() {
const { isChecked, isLoading, isCollapsed } = this.state;
const { isChecked, isLoading, isCollapsed, contentHeight } = this.state;
const { resource } = this.props;
if (isLoading) return <LoadingChecklistItem />;
@ -59,30 +84,60 @@ class ChecklistItem extends React.Component<Props, State> {
<Container>
<Card isChecked={isChecked}>
<CardContent isCollapsed={isCollapsed}>
<CheckboxContainer onClick={this.handleSetChecked}>
<Checkbox isChecked={isChecked} />
<CheckboxContainer>
<input
aria-expanded={!isCollapsed}
type="checkbox"
checked={isChecked}
id={`checkbox_${resource.id}`}
onChange={this.handleSetChecked}
aria-controls={`content_${resource.id}`}
/>
<label htmlFor={`checkbox_${resource.id}`}>
{resource.title}
</label>
</CheckboxContainer>
<ResourceContent isChecked={isChecked} isCollapsed={isCollapsed}>
<ResourceContent
isChecked={isChecked}
isCollapsed={isCollapsed}
>
<Heading
resource={resource}
isCollapsed={isCollapsed}
handleCollapse={this.uncollapse}
/>
{!isCollapsed && resource.apps && (
<React.Fragment>
<Divider />
<Apps resource={resource} />
</React.Fragment>
)}
<Content
aria-hidden={isCollapsed}
id={`content_${resource.id}`}
role="region"
tabindex="-1"
ref={this.contentContainer}
style={{ '--maxHeight': `${contentHeight}px` }}
>
<Description source={resource.description} />
{resource.apps && (
<React.Fragment>
<Divider />
<Apps
resource={resource}
handleAppsExpand={this.handleAppsExpand}
/>
</React.Fragment>
)}
{resource.resources && (
<React.Fragment>
<Divider />
<Resources resource={resource} />
</React.Fragment>
)}
</Content>
{!isCollapsed && resource.resources && (
<React.Fragment>
<Divider />
<Resources resource={resource} />
</React.Fragment>
)}
</ResourceContent>
</CardContent>
</Card>

View file

@ -2,7 +2,7 @@
import styled, { css } from 'styled-components';
import Markdown from 'react-markdown';
import { theme } from '../theme';
import { Shadows, tint } from '../globals';
import { Shadows, tint, hexa } from '../globals';
export const Container = styled.div`
margin-bottom: 24px;
@ -69,35 +69,29 @@ export const CheckboxContainer = styled.div`
margin-top: 4px;
width: 32px;
}
`;
export const Checkbox = styled.span`
width: 32px;
height: 32px;
border-radius: 4px;
border: 1px solid
${props => (props.isChecked ? theme.bg.default : theme.border.default)};
background: ${props =>
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 {
${props => !props.isChecked && Shadows.default};
background: ${theme.bg.default};
background-image: ${props =>
props.isChecked
? 'radial-gradient(circle at top right, #a913de, #6ac9ff)'
: 'none'};
input[type='checkbox'] {
position: absolute;
}
&:after {
input[type='checkbox'] + label {
width: 32px;
height: 32px;
border-radius: 4px;
border: 1px solid ${theme.border.default};
background: ${theme.bg.wash};
cursor: pointer;
position: relative;
overflow: hidden;
text-indent: -1000px;
}
input[type='checkbox'] + label:hover {
${Shadows.default};
background: ${theme.bg.default};
}
input[type='checkbox'] + label::after {
content: '';
position: absolute;
display: block;
@ -105,11 +99,50 @@ export const Checkbox = styled.span`
top: 6px;
width: 6px;
height: 12px;
border: solid
${props => (props.isChecked ? theme.bg.default : theme.border.active)};
border: solid ${theme.border.active};
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
input[type='checkbox']:checked + label {
border: 1px solid ${theme.bg.default};
}
input[type='checkbox']:checked + label::after {
border: solid ${theme.bg.default};
border-width: 0 2px 2px 0;
}
/* This ::before pseudo-element is used to animate the gradient
which does not support transitions. */
input[type='checkbox'] + label::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: opacity ${theme.animations.default};
opacity: 0;
background-image: radial-gradient(circle at top right, #a913de, #6ac9ff);
box-shadow: inset 0 0 1px rgba(0, 0, 0, 0.4);
}
input[type='checkbox']:checked + label::before {
opacity: 1;
background-color: ${theme.success.default};
}
input[type='checkbox']:active + label,
input[type='checkbox']:focus + label {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.brand.default, 0.5)};
}
input[type='checkbox']:active:checked + label,
input[type='checkbox']:focus:checked + label {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.spectrum.default, 0.5)};
}
`;
export const ResourceContent = styled.div`
@ -129,12 +162,7 @@ export const ResourceContent = styled.div`
export const AppsContainer = styled.div``;
export const AppMeta = styled.div`
display: flex;
align-items: center;
`;
export const AppRowContainer = styled.a`
export const AppRowContainer = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
@ -170,6 +198,19 @@ export const AppRowContainer = styled.a`
}
`;
export const AppMeta = styled.a`
display: flex;
align-items: center;
padding-right: 6px;
border-radius: 4px;
&:active,
&:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;
export const AppIcon = styled.img`
width: 40px;
height: 40px;
@ -207,8 +248,7 @@ export const AppSourcesListItem = styled.li`
align-items: center;
justify-content: center;
color: ${theme.text.tertiary};
padding: 4px 10px;
border-radius: 4px;
padding: 2px 8px;
min-width: 56px;
transition: all 0.1s ease-in-out;
@ -217,6 +257,19 @@ export const AppSourcesListItem = styled.li`
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2px;
border-radius: 4px;
&:hover {
color: ${theme.text.default};
}
&:active,
&:focus {
color: ${theme.text.default};
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
}
.icon {
@ -225,18 +278,14 @@ export const AppSourcesListItem = styled.li`
left: 4px;
}
&:hover {
color: ${theme.text.default};
}
@media (max-width: 768px) {
padding: 4px;
min-width: 40px;
.icon {
position: relative;
top: 5px;
left: 7px;
top: 3px;
left: 5px;
}
&:hover {
@ -280,19 +329,28 @@ export const ResourceRowContainer = styled.a`
margin-right: 8px;
}
&:hover {
background: ${theme.bg.wash};
&:hover,
&:active,
&:focus {
color: ${theme.text.default};
}
&:active,
&:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
@media (max-width: 768px) {
width: calc(100% + 40px);
border-radius: 0;
border-radius: 4px;
align-items: flex-start;
margin-left: -24px;
padding-left: 24px;
&:hover {
&:hover,
&:active,
&:focus {
background: ${theme.bg.default}!important;
}
@ -309,7 +367,7 @@ export const ResourceName = styled.p`
font-weight: 400;
`;
export const Divider = styled.div`
export const Divider = styled.hr`
border-bottom: 1px solid ${tint(theme.bg.wash, -4)};
margin-top: 24px;
margin-bottom: 24px;
@ -321,7 +379,23 @@ export const Divider = styled.div`
}
`;
export const Uncollapse = styled.span`
export const Content = styled.div`
transition: max-height ${theme.animations.default},
opacity ${theme.animations.default}, visibility ${theme.animations.default};
max-height: 2000px;
max-height: var(--maxHeight);
opacity: 1;
visibility: visible;
&[aria-hidden='true'] {
max-height: 0;
opacity: 0;
visibility: hidden;
}
`;
export const Uncollapse = styled.button`
background: ${theme.bg.wash};
border-radius: 20px;
padding: 8px 16px;
@ -344,6 +418,13 @@ export const Uncollapse = styled.span`
color: ${theme.text.default};
background: ${tint(theme.bg.wash, -4)};
}
&:active,
&:focus {
transition: all 0.2s ease-in-out;
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;
export const OfferContainer = styled.a`
@ -379,6 +460,12 @@ export const OfferContainer = styled.a`
&:hover {
color: ${theme.text.secondary};
}
&:active,
&:focus {
box-shadow: inset 0 0 1px ${theme.border.active},
0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;
export const LeftBorder = styled.div`
@ -417,3 +504,18 @@ export const ExpandContainer = styled.div`
width: calc(100% + 40px);
}
`;
export const ExpandContent = styled.div`
transition: max-height ${theme.animations.default},
opacity ${theme.animations.default}, visibility ${theme.animations.default};
max-height: 2000px;
max-height: var(--maxHeight);
opacity: 1;
visibility: visible;
&[aria-hidden='true'] {
max-height: 0;
opacity: 0;
visibility: hidden;
}
`;

View file

@ -1,6 +1,7 @@
// @flow
import styled from 'styled-components';
import { theme } from '../theme';
import { hexa } from '../globals';
export const Container = styled.div`
margin-top: 128px;
@ -21,24 +22,32 @@ export const Description = styled.p`
color: ${theme.text.default};
margin-left: 4px;
}
a:active, a:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;
export const Icons = styled.div`
display: flex;
flex: 1 0 auto;
align-items: flex-start;
margin-left: -16px;
padding-bottom: 8px;
a {
color: ${theme.text.tertiary};
margin-right: 16px;
line-height: 1;
border-radius: 10px;
height: 32px;
}
a:hover {
a:hover, a:active, a:focus {
color: ${theme.text.default};
}
.icon {
margin-left: 16px;
a:active, a:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import Link from 'next/link';
import { Container, ButtonRowContainer, Label } from './style';
import { Container, ButtonRowContainer, Label, LogoLink } from './style';
import { PrimaryButton, GhostButton } from '../Button';
import Logo from './Logo';
@ -14,27 +14,30 @@ export default function Header(props: Props) {
return (
<Container showHeaderShadow={showHeaderShadow} data-cy="header">
<Link href="/">
<a style={{ display: 'flex', alignItems: 'center' }}>
<Label>Security Checklist</Label>
<Logo />
</a>
</Link>
<div>
<Link href="/">
<LogoLink href="/">
<Label>Security Checklist</Label>
<Logo />
</LogoLink>
</Link>
</div>
<ButtonRowContainer>
<Link href="/about">
<a>
<GhostButton>About</GhostButton>
</a>
<GhostButton as="a" href="/about">
About
</GhostButton>
</Link>
<a
<PrimaryButton
href="https://github.com/brianlovin/security-checklist"
target="_blank"
rel="noopener noreferrer"
as="a"
>
<PrimaryButton>Contribute</PrimaryButton>
</a>
Contribute
</PrimaryButton>
</ButtonRowContainer>
</Container>
);

View file

@ -1,6 +1,7 @@
// @flow
import styled from 'styled-components';
import { theme } from '../theme';
import { hexa } from '../globals';
export const Container = styled.div`
display: grid;
@ -18,10 +19,6 @@ export const Container = styled.div`
props.showHeaderShadow ? '0 4px 8px rgba(0,0,0,0.04)' : 'none'};
transition: all 0.2s ease-in-out;
a {
margin-left: 8px;
}
@media (max-width: 968px) {
padding: 8px 16px;
grid-template-columns: 1fr 1fr;
@ -41,6 +38,26 @@ export const ButtonRowContainer = styled.div`
justify-content: flex-end;
grid-area: actions;
align-items: center;
a {
margin-left: 8px;
}
`;
export const LogoLink = styled.a`
transition: all ${props => props.theme.animations.default};
display: inline-flex;
align-items: center;
border-radius: 6px;
&:hover {
transform: scale(1.2);
}
&:active, &:focus {
transform: scale(1.2);
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
`;
export const Label = styled.h1`

View file

@ -74,7 +74,7 @@ export const Glyph = ({ glyph }: GlyphProps): any => {
);
case 'ios':
return (
<g>
<g transform="translate(3.5 1.5)">
<path d="M16.6437 15.5861C16.3385 16.2848 15.9772 16.928 15.5586 17.5193C14.9881 18.3255 14.5209 18.8835 14.1609 19.1934C13.6027 19.702 13.0048 19.9625 12.3644 19.9773C11.9047 19.9773 11.3504 19.8477 10.705 19.5847C10.0576 19.323 9.46262 19.1934 8.91858 19.1934C8.34801 19.1934 7.73608 19.323 7.08155 19.5847C6.42601 19.8477 5.89793 19.9847 5.49417 19.9983C4.88012 20.0242 4.26806 19.7563 3.65713 19.1934C3.2672 18.8563 2.77947 18.2786 2.1952 17.4601C1.56832 16.586 1.05294 15.5725 0.649179 14.417C0.216767 13.1689 5.70276e-08 11.9603 5.70276e-08 10.7902C5.70276e-08 9.44984 0.292262 8.29382 0.877656 7.32509C1.33773 6.54696 1.94978 5.93315 2.71581 5.48255C3.48185 5.03195 4.30955 4.80232 5.20091 4.78763C5.68863 4.78763 6.32822 4.93713 7.12303 5.23095C7.9156 5.52576 8.42451 5.67526 8.64762 5.67526C8.81444 5.67526 9.37977 5.50045 10.3382 5.15194C11.2445 4.82874 12.0094 4.69492 12.636 4.74763C14.334 4.88343 15.6097 5.54675 16.4581 6.74177C14.9395 7.6536 14.1883 8.93072 14.2032 10.5691C14.2169 11.8452 14.6841 12.9071 15.6022 13.7503C16.0183 14.1417 16.483 14.4441 17 14.6589C16.8879 14.9812 16.7695 15.2898 16.6437 15.5861ZM12.7494 0.400111C12.7494 1.40034 12.3806 2.33425 11.6456 3.19867C10.7586 4.22629 9.68574 4.8201 8.5223 4.7264C8.50747 4.6064 8.49888 4.48011 8.49888 4.3474C8.49888 3.38718 8.9207 2.35956 9.66979 1.51934C10.0438 1.09392 10.5194 0.740188 11.0962 0.458011C11.6718 0.180044 12.2162 0.0263202 12.7282 8.94779e-09C12.7431 0.133712 12.7494 0.267436 12.7494 0.400099V0.400111Z" />
</g>
);
@ -95,7 +95,7 @@ export const Glyph = ({ glyph }: GlyphProps): any => {
);
case 'android':
return (
<g>
<g transform="translate(2 1)">
<path d="M1.98508 7.17365H1.92873C1.20629 7.17365 0.617249 7.76456 0.617249 8.48513V14.1951C0.617249 14.9185 1.20629 15.5075 1.92873 15.5075H1.98604C2.70848 15.5075 3.29752 14.9166 3.29752 14.1951V8.48508C3.29656 7.76456 2.70661 7.17365 1.98508 7.17365Z" />
<path d="M3.92507 16.399C3.92507 17.0623 4.46711 17.6024 5.13036 17.6024H6.41837V20.6866C6.41837 21.411 7.00928 22 7.72985 22H7.7862C8.5096 22 9.09956 21.41 9.09956 20.6866V17.6024H10.8996V20.6866C10.8996 21.411 11.4923 22 12.2129 22H12.2684C12.9917 22 13.5807 21.41 13.5807 20.6866V17.6024H14.8697C15.532 17.6024 16.074 17.0623 16.074 16.399V7.38312H3.92507V16.399Z" />
<path d="M13.0255 1.91553L14.0486 0.33634C14.1143 0.236737 14.0852 0.10055 13.9847 0.0357013C13.8851 -0.0300621 13.7489 -0.00279859 13.684 0.0995919L12.6234 1.73331C11.8277 1.40732 10.939 1.22414 10.0005 1.22414C9.06101 1.22414 8.17416 1.40732 7.37659 1.73331L6.31785 0.0995919C6.25304 -0.00279859 6.11585 -0.0300621 6.01534 0.0357013C5.91482 0.100506 5.88568 0.236737 5.95145 0.33634L6.97544 1.91553C5.1294 2.82023 3.88465 4.52533 3.88465 6.48125C3.88465 6.6015 3.89218 6.71987 3.9025 6.83729H16.0993C16.1097 6.71987 16.1162 6.6015 16.1162 6.48125C16.1163 4.52533 14.8706 2.82023 13.0255 1.91553ZM7.17273 4.74327C6.84861 4.74327 6.58556 4.48209 6.58556 4.15706C6.58556 3.83203 6.84861 3.57177 7.17273 3.57177C7.49871 3.57177 7.75989 3.83199 7.75989 4.15706C7.75989 4.48213 7.49684 4.74327 7.17273 4.74327ZM12.8273 4.74327C12.5032 4.74327 12.2401 4.48209 12.2401 4.15706C12.2401 3.83203 12.5032 3.57177 12.8273 3.57177C13.1523 3.57177 13.4135 3.83199 13.4135 4.15706C13.4135 4.48209 13.1523 4.74327 12.8273 4.74327Z" />

View file

@ -74,7 +74,11 @@ export default function Page(props: Props) {
<Header showHeaderShadow={showHeaderShadow} />
<InnerContainer>{children}</InnerContainer>
<Footer />
<ScrollToTop isVisible={scrollToTopVisible} onClick={scrollToTop}>
<ScrollToTop
isVisible={scrollToTopVisible}
onClick={scrollToTop}
type="button"
>
<Icon glyph="view-forward" size={32} />
</ScrollToTop>
</Container>

View file

@ -1,6 +1,6 @@
// @flow
import styled from 'styled-components';
import { tint } from '../globals';
import { hexa, tint } from '../globals';
import { theme } from '../theme';
export const Container = styled.div`
@ -47,7 +47,7 @@ export const SectionHeading = styled.div`
}
`;
export const Heading = styled.h3`
export const Heading = styled.h2`
font-size: 40px;
font-weight: 700;
color: ${theme.text.default};
@ -58,7 +58,7 @@ export const Heading = styled.h3`
}
`;
export const Subheading = styled.h4`
export const Subheading = styled.p`
font-size: 22px;
font-weight: 400;
color: ${theme.text.tertiary};
@ -69,6 +69,18 @@ export const Subheading = styled.h4`
color: ${theme.text.default};
font-weight: 500;
}
a:hover {
text-decoration: underline;
text-decoration-color: ${theme.text.tertiary};
}
a:active, a:focus {
box-shadow: 0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)};
}
& + & {
margin-top: 16px;
}
@media (max-width: 968px) {
max-width: 100%;
@ -113,8 +125,11 @@ export const ScrollToTop = styled.button`
transition: all 0.2s ease-in-out;
}
&:active {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.09);
&:active, &:focus {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.09),
0 0 0 1px ${theme.bg.default},
0 0 0 3px ${props => hexa(props.theme.brand.default, 0.5)};
outline: none;
transform: translateY(-2px);
transition: all 0.2s ease-in-out;
}

View file

@ -6,21 +6,21 @@ import { Container } from './style';
export default function ShareButtons() {
return (
<Container>
<a
<FacebookButton
href="https://www.facebook.com/sharer/sharer.php?u=https://securitycheckli.st"
target="_blank"
rel="noopener noreferrer"
>
<FacebookButton>Share</FacebookButton>
</a>
Share
</FacebookButton>
<a
<TwitterButton
href="http://twitter.com/share?text=Check out Security Checklist, a checklist to help people stay safe online&url=https://securitycheckli.st"
target="_blank"
rel="noopener noreferrer"
>
<TwitterButton>Tweet</TwitterButton>
</a>
Tweet
</TwitterButton>
<CopyLinkButton text="https://securitycheckli.st">
Share Link

View file

@ -13,10 +13,8 @@ export const Container = styled.div`
@media (max-width: 568px) {
grid-template-columns: 1fr;
grid-gap: 0;
button {
margin-top: 16px;
a, button {
width: 100%;
}
}

View file

@ -69,5 +69,17 @@ export default {
url:
'https://www.salon.com/2014/02/05/4_ways_google_is_destroying_privacy_and_collecting_your_data_partner/',
},
{
name: 'Privacy-friendly alternatives to Google that dont track you',
url: 'https://nomoregoogle.com/',
},
{
name: 'Opt out of global data surveillance programs like PRISM, XKeyscore and Tempora.',
url: 'https://prism-break.org/en/',
},
{
name: 'Knowledge and tools to protect your privacy against global mass surveillance',
url: 'https://www.privacytools.io/',
},
],
};

View file

@ -2,7 +2,7 @@ export const getItemFromStorage = key => {
if (!localStorage) return;
try {
return JSON.parse(localStorage.getItem(key));
return JSON.parse(localStorage.getItem(key) || false);
} catch (err) {}
};

101
package-lock.json generated
View file

@ -3679,9 +3679,9 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"eslint": {
"version": "5.12.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.1.tgz",
"integrity": "sha512-54NV+JkTpTu0d8+UYSA8mMKAG4XAsaOrozA9rCW7tgneg1mevcL7wIotPC+fZ0SkWwdhNqoXoxnQCTBp7UvTsg==",
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz",
"integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
@ -3713,7 +3713,6 @@
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"pluralize": "^7.0.0",
"progress": "^2.0.0",
"regexpp": "^2.0.1",
"semver": "^5.5.1",
@ -3752,9 +3751,9 @@
}
},
"slice-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz",
"integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
"integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
@ -3772,14 +3771,14 @@
}
},
"table": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/table/-/table-5.2.1.tgz",
"integrity": "sha512-qmhNs2GEHNqY5fd2Mo+8N1r2sw/rvTAAvBZTaTx+Y7PHLypqyrxr1MdIu0pLw6Xvl/Gi4ONu/sdceP8vvUjkyA==",
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/table/-/table-5.2.2.tgz",
"integrity": "sha512-f8mJmuu9beQEDkKHLzOv4VxVYlU68NpdzjbGPl69i4Hx0sTopJuNxuzJd17iV2h24dAfa93u794OnDA5jqXvfQ==",
"dev": true,
"requires": {
"ajv": "^6.6.1",
"lodash": "^4.17.11",
"slice-ansi": "2.0.0",
"slice-ansi": "^2.0.0",
"string-width": "^2.1.1"
}
}
@ -3943,9 +3942,9 @@
}
},
"eslint-plugin-jsx-a11y": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.0.tgz",
"integrity": "sha512-KpibpIdKT0nbDG7G/ILWoZoN5G9cj3h1IHARxzvHk5Nt2LQ7oPUutbO9QbT1FKNHLPZB6BaoA1ASSLOiJZVEUQ==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz",
"integrity": "sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w==",
"dev": true,
"requires": {
"aria-query": "^3.0.0",
@ -4032,9 +4031,9 @@
},
"dependencies": {
"acorn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz",
"integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==",
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.7.tgz",
"integrity": "sha512-HNJNgE60C9eOTgn974Tlp3dpLZdUr+SoxxDwPaY9J/kDNOLQTkaDgwBUXAF4SSsrAwD9RpdxuHK/EbuF+W9Ahw==",
"dev": true
}
}
@ -4564,9 +4563,9 @@
}
},
"flow-bin": {
"version": "0.91.0",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.91.0.tgz",
"integrity": "sha512-j+L+xNiUYnZZ27MjVI0y2c9474ZHOvdSQq0Tjwh56mEA7tfxYqp5Dcb6aZSwvs3tGMTjCrZow9aUlZf3OoRyDQ==",
"version": "0.92.1",
"resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.92.1.tgz",
"integrity": "sha512-F5kC5oQOR2FXROAeybJHFqgZP+moKV9fa/53QK4Q4WayTQHdA0KSl48KD1gP0A9mioRLiKUegTva/7I15cX3Iw==",
"dev": true
},
"flush-write-stream": {
@ -5666,26 +5665,32 @@
}
},
"inquirer": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz",
"integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==",
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
"integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
"dev": true,
"requires": {
"ansi-escapes": "^3.0.0",
"chalk": "^2.0.0",
"ansi-escapes": "^3.2.0",
"chalk": "^2.4.2",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^3.0.0",
"external-editor": "^3.0.3",
"figures": "^2.0.0",
"lodash": "^4.17.10",
"lodash": "^4.17.11",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rxjs": "^6.1.0",
"rxjs": "^6.4.0",
"string-width": "^2.1.0",
"strip-ansi": "^5.0.0",
"through": "^2.3.6"
},
"dependencies": {
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
"dev": true
},
"ansi-regex": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
@ -5693,9 +5698,9 @@
"dev": true
},
"rxjs": {
"version": "6.3.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
"integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
"integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
@ -6268,9 +6273,9 @@
}
},
"lint-staged": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.1.tgz",
"integrity": "sha512-6C9tmmCedjDYQMzHydT5mXRtmEgpGUQDoIl+Ser8cfI/n9grsRUsuG2jd1BWqGf62OV+BV+6n/Drt82uYTCgJg==",
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.3.tgz",
"integrity": "sha512-6TGkikL1B+6mIOuSNq2TV6oP21IhPMnV8q0cf9oYZ296ArTVNcbFh1l1pfVOHHbBIYLlziWNsQ2q45/ffmJ4AA==",
"dev": true,
"requires": {
"@iamstarkov/listr-update-renderer": "0.4.1",
@ -7963,9 +7968,9 @@
"dev": true
},
"prettier": {
"version": "1.16.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.3.tgz",
"integrity": "sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==",
"version": "1.16.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz",
"integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==",
"dev": true
},
"prettier-eslint": {
@ -8412,14 +8417,14 @@
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"react": {
"version": "16.8.0-alpha.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0-alpha.0.tgz",
"integrity": "sha512-B/nJJkNqV4JAJQ1M0Q38ZvhZ6BoYsjyZq29hbsIIPYayn/0DcYhu7tExpfzdtTC0SnUcA+RnDBTlXTEJ5b9PEg==",
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.1.tgz",
"integrity": "sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.0-alpha.0"
"scheduler": "^0.13.1"
}
},
"react-clipboard.js": {
@ -8432,14 +8437,14 @@
}
},
"react-dom": {
"version": "16.8.0-alpha.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.0-alpha.0.tgz",
"integrity": "sha512-OGfLUXGxtMgqLB1BqnB9flVCvc8VvAnhQYeFr+EUzwiODcD34dPrxtfa7ccK4dwzcPi8HRLSyBcaQi2OvVoUww==",
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.1.tgz",
"integrity": "sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.0-alpha.0"
"scheduler": "^0.13.1"
}
},
"react-error-overlay": {
@ -8895,9 +8900,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"scheduler": {
"version": "0.13.0-alpha.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.0-alpha.0.tgz",
"integrity": "sha512-+DbRwO53hlLso4gzrSIqcfNe8eRMMaV4gd4eySGisbyLBrp5unTpAMR0UAbrqlQLTGhnOFNoQAfaZTz4iPj1RQ==",
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz",
"integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"

View file

@ -37,18 +37,18 @@
"babel-plugin-styled-components": "^1.10.0",
"babel-plugin-transform-define": "^1.3.1",
"cypress": "^3.1.5",
"eslint": "^5.12.1",
"eslint": "^5.13.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^4.0.0",
"eslint-config-react-app": "^3.0.6",
"eslint-plugin-flowtype": "^3.2.1",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "^7.12.4",
"flow-bin": "^0.91.0",
"lint-staged": "^8.1.1",
"prettier": "^1.16.3",
"flow-bin": "^0.92.1",
"lint-staged": "^8.1.3",
"prettier": "^1.16.4",
"prettier-eslint": "^8.8.2"
},
"lint-staged": {

View file

@ -51,8 +51,6 @@ export default function About() {
.
</Subheading>
<div style={{ padding: '8px' }} />
<Subheading>
The code that powers this website is{' '}
<a