Website optimization

- Remove AnyLink and ReactCSSTransitionReplace
- Clean up GraphQL queries
- Improve getTextualContent method in website utilities
- Convert ttf fonts to woff2
- Add title attribute to menu items
This commit is contained in:
Angelos Chalaris
2019-08-27 20:14:07 +03:00
parent c643adda83
commit 5e8dd9a9b3
31 changed files with 55 additions and 239 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/NotoSans-Light.woff2 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -9,7 +9,6 @@ module.exports = {
},
plugins: [
`gatsby-plugin-sitemap`,
`gatsby-plugin-transition-link`,
{
resolve: `gatsby-source-filesystem`,
options: {

View File

@ -55,7 +55,6 @@
"gatsby-plugin-sass": "^2.1.3",
"gatsby-plugin-sharp": "^2.2.7",
"gatsby-plugin-sitemap": "^2.2.8",
"gatsby-plugin-transition-link": "^1.12.4",
"gatsby-remark-copy-linked-files": "^2.1.3",
"gatsby-remark-images": "^3.1.6",
"gatsby-remark-prismjs": "^3.3.3",
@ -63,7 +62,6 @@
"gatsby-transformer-json": "^2.2.2",
"gatsby-transformer-remark": "^2.6.6",
"gatsby-transformer-sharp": "^2.2.3",
"gsap": "^2.1.3",
"jest": "^24.9.0",
"kleur": "^3.0.3",
"markdown-builder": "^0.9.0",
@ -73,7 +71,6 @@
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-copy-to-clipboard": "^5.0.1",
"react-css-transition-replace": "^3.0.3",
"react-dom": "^16.8.6",
"react-helmet": "^5.2.1",
"react-redux": "^7.1.0",

View File

@ -1,8 +1,6 @@
import React from 'react';
import { graphql, useStaticQuery } from 'gatsby';
import { graphql, useStaticQuery, Link } from 'gatsby';
import { connect } from 'react-redux';
import AniLink from 'gatsby-plugin-transition-link/AniLink';
import ReactCSSTransitionReplace from 'react-css-transition-replace';
import config from '../../../config';
import { toggleDarkMode } from '../state/app';
@ -52,67 +50,50 @@ const Shell = ({
}
}
`);
let viewportWidth = typeof window !== 'undefined' && window.innerWidth;
return (
<div className={isDarkMode ? 'page-container dark' : 'page-container'}>
{/* Menu */}
<header className='menu'>
<AniLink
cover
direction={viewportWidth < 600 ? 'up' : 'right'}
bg={isDarkMode ? '#434E76' : '#FFFFFF'}
<Link
to='/search'
aria-label='Search'
aria-label='Search'title='Search'
className={isSearch ? 'menu-button active' : 'menu-button'}
>
<SearchIcon />
</AniLink>
<AniLink
cover
direction={viewportWidth < 600 ? 'up' : 'right'}
bg={isDarkMode ? '#434E76' : '#FFFFFF'}
</Link>
<Link
to='/list'
aria-label='Snippet list'
aria-label='Snippet list' title='Snippet list'
className={isList ? 'menu-button active' : 'menu-button'}
>
<ListIcon />
</AniLink>
</Link>
{/* eslint-disable-next-line */}
<a target='_blank'
rel='noopener'
href={config.repositoryUrl}
aria-label='View on GitHub'
aria-label='View on GitHub' title='View on GitHub'
className='menu-button'
>
<GithubIcon />
</a>
{/*
The use of React.Fragment here will cause a lot of errors to show up in webber:dev.
Truth is, this is the only decent way I found to deal with this module's odd behavior.
Keep as is, unless you can provide a better solution, in which case please send a PR.
*/}
<ReactCSSTransitionReplace
transitionName='cross-fade'
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
component='button'
<button
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
className='menu-button'
childComponent={React.Fragment}
>
title={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
className='menu-button'>
{isDarkMode ? (
<LightModeIcon
key='lmit'
onClick={() => dispatch(toggleDarkMode(!isDarkMode))}
/>
) : (
<DarkModeIcon
key='dmit'
onClick={() => dispatch(toggleDarkMode(!isDarkMode))}
/>
)}
</ReactCSSTransitionReplace>
<DarkModeIcon
key='dmit'
onClick={() => dispatch(toggleDarkMode(!isDarkMode))}
/>
)}
</button>
</header>
{/* Content */}
<div className='content'>

View File

@ -1,14 +1,14 @@
import React from 'react';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import config from '../../../config';
import { Link } from 'gatsby';
import { getTextualContent, getCodeBlocks, optimizeAllNodes } from '../util';
// import ClipboardIcon from './SVGs/ClipboardIcon';
// import ShareIcon from './SVGs/ShareIcon';
import AniLink from 'gatsby-plugin-transition-link/AniLink';
import CollapseOpenIcon from './SVGs/CollapseOpenIcon';
import CollapseClosedIcon from './SVGs/CollapseClosedIcon';
import ReactCSSTransitionReplace from 'react-css-transition-replace';
// import ReactCSSTransitionReplace from 'react-css-transition-replace';
// ===================================================
// Snippet Card HOC - check components below for more
@ -96,18 +96,18 @@ const FullCard = ({ snippetData, difficulty, isDarkMode }) => {
>
{examplesOpen ? <CollapseOpenIcon /> : <CollapseClosedIcon />}Examples
</button>
<ReactCSSTransitionReplace
{/* <ReactCSSTransitionReplace
transitionName='roll-up'
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
>
> */}
{examplesOpen && (
<pre
className='section card-examples language-js'
dangerouslySetInnerHTML={{ __html: cardExamplesHtml }}
/>
)}
</ReactCSSTransitionReplace>
{/* </ReactCSSTransitionReplace> */}
</div>
</div>
);
@ -118,27 +118,18 @@ const FullCard = ({ snippetData, difficulty, isDarkMode }) => {
// ===================================================
const ShortCard = ({
snippetData,
withCode = false,
archived = false,
difficulty,
isDarkMode
}) => {
let cardCodeHtml;
if(withCode)
cardCodeHtml = `${optimizeAllNodes(
getCodeBlocks(snippetData.html).code,
)}`;
return (
<div className='card short'>
<CardCorner difficulty={difficulty} />
<h4 className='card-title'>
<AniLink
paintDrip
<Link
to={archived ? `/archive/${snippetData.id}` : `/snippet/${snippetData.id}`}
hex={isDarkMode ? '#434E76' : '#FFFFFF'}
>
{snippetData.title}
</AniLink>
</Link>
</h4>
<div
className='card-description'
@ -146,32 +137,6 @@ const ShortCard = ({
__html: `${getTextualContent(snippetData.html, true)}`,
}}
/>
{ withCode ? <div className='card-bottom'>
<CopyToClipboard
text={snippetData.code}
onCopy={() => {
let tst = document.createElement('div');
tst.classList = 'toast';
tst.innerHTML = 'Snippet copied to clipboard!';
document.body.appendChild(tst);
setTimeout(function() {
tst.style.opacity = 0;
setTimeout(function() {
document.body.removeChild(tst);
}, 300);
}, 1700);
}}
>
<button
className='button button-a button-copy'
aria-label='Copy to clipboard'
/>
</CopyToClipboard>
<pre
className={`card-code language-${config.language}`}
dangerouslySetInnerHTML={{ __html: cardCodeHtml }}
/>
</div> : ''}
</div>
);
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import AniLink from 'gatsby-plugin-transition-link/AniLink';
import { Link } from 'gatsby';
import Shell from '../components/Shell';
import Meta from '../components/Meta';
@ -21,10 +21,8 @@ const NotFoundPage = ({ isDarkMode }) => (
<p className='empty-page-subtext'>
Seems like you have reached a page that does not exist.
</p>
<AniLink
paintDrip
<Link
to='/'
hex={isDarkMode ? '#434E76' : '#FFFFFF'}
className='button button-a button-home'
>
<svg
@ -43,7 +41,7 @@ const NotFoundPage = ({ isDarkMode }) => (
<polyline points='9 22 9 12 15 12 15 22'></polyline>
</svg>
&nbsp;&nbsp;Go home
</AniLink>
</Link>
</div>
</Shell>
</>

View File

@ -5,9 +5,7 @@ import { pushNewPage } from '../state/app';
import Meta from '../components/Meta';
import Shell from '../components/Shell';
import SnippetCard from '../components/SnippetCard';
import { getRawCodeBlocks as getCodeBlocks } from '../util';
import SnippetCard from '../components/SnippetCard'
// ===================================================
// Individual snippet category/tag page
@ -35,7 +33,6 @@ const ArchivePage = props => {
snippetData={{
title: node.frontmatter.title,
html: node.html,
code: getCodeBlocks(node.rawMarkdownBody).code,
tags: node.frontmatter.tags.split(',').map(v => v.trim()),
id: node.fields.slug.slice(1),
}}

View File

@ -8,8 +8,6 @@ import Meta from '../components/Meta';
import Search from '../components/Search';
import SnippetCard from '../components/SnippetCard';
import { getRawCodeBlocks as getCodeBlocks } from '../util';
// ===================================================
// Home page (splash and search)
// ===================================================
@ -20,13 +18,7 @@ const IndexPage = props => {
v => v.node.frontmatter.title === snippet.title,
).node.html,
tags: snippet.attributes.tags,
text: snippet.attributes.text,
id: snippet.id,
code: getCodeBlocks(
props.data.allMarkdownRemark.edges.find(
v => v.node.frontmatter.title === snippet.title,
).node.rawMarkdownBody,
).code,
id: snippet.id
}));
const [searchQuery, setSearchQuery] = React.useState(props.searchQuery);
@ -129,26 +121,15 @@ export const indexPageQuery = graphql`
title
attributes {
tags
text
}
}
}
allMarkdownRemark(
limit: 1000
sort: { fields: [frontmatter___title], order: ASC }
) {
totalCount
allMarkdownRemark {
edges {
node {
id
html
rawMarkdownBody
fields {
slug
}
frontmatter {
title
tags
}
}
}

View File

@ -1,5 +1,5 @@
import React from 'react';
import { graphql } from 'gatsby';
import { graphql, Link } from 'gatsby';
import { connect } from 'react-redux';
import { pushNewPage } from '../state/app';
import { capitalize } from '../util';
@ -7,10 +7,8 @@ import config from '../../../config';
import Shell from '../components/Shell';
import Meta from '../components/Meta';
import AniLink from 'gatsby-plugin-transition-link/AniLink';
import SnippetCard from '../components/SnippetCard';
import { getRawCodeBlocks as getCodeBlocks } from '../util';
import SimpleCard from '../components/SimpleCard';
// ===================================================
@ -23,16 +21,7 @@ const ListPage = props => {
v => v.node.frontmatter.title === snippet.title,
).node.html,
tags: snippet.attributes.tags,
text: snippet.attributes.text,
id: snippet.id,
archived: props.data.allMarkdownRemark.edges.find(
v => v.node.frontmatter.title === snippet.title,
).node.fileAbsolutePath.indexOf(config.snippetArchivePath) !== -1,
code: getCodeBlocks(
props.data.allMarkdownRemark.edges.find(
v => v.node.frontmatter.title === snippet.title,
).node.rawMarkdownBody,
).code,
}));
const archivedSnippets = props.data.snippetsArchiveDataJson.data.map(snippet => ({
title: snippet.title,
@ -40,16 +29,7 @@ const ListPage = props => {
v => v.node.frontmatter.title === snippet.title,
).node.html,
tags: snippet.attributes.tags,
text: snippet.attributes.text,
id: snippet.id,
archived: props.data.allMarkdownRemark.edges.find(
v => v.node.frontmatter.title === snippet.title,
).node.fileAbsolutePath.indexOf(config.snippetArchivePath) !== -1,
code: getCodeBlocks(
props.data.allMarkdownRemark.edges.find(
v => v.node.frontmatter.title === snippet.title,
).node.rawMarkdownBody,
).code,
}));
const tags = snippets.reduce((acc, snippet) => {
if (!snippet.tags) return acc;
@ -91,57 +71,47 @@ const ListPage = props => {
{tags.sort((a,b) => a.localeCompare(b)).map(tag => (
<>
<h3 className='tag-title' key={`tag_title_${tag}`}>
<AniLink
<Link
key={`tag_link_${tag}`}
paintDrip
to={`/tag/${tag}`}
hex={props.isDarkMode ? '#434E76' : '#FFFFFF'}
>
{capitalize(tag)}
</AniLink>
</Link>
</h3>
{snippets
.filter(snippet => !snippet.archived)
.filter(snippet => snippet.tags[0] === tag)
.map(snippet => (
<SnippetCard
key={`snippet_${snippet.id}`}
short
snippetData={snippet}
isDarkMode={props.isDarkMode}
/>
))}
</>
))}
<h3 className='tag-title'><AniLink
paintDrip
<h3 className='tag-title'><Link
to={`/archive`}
hex={props.isDarkMode ? '#434E76' : '#FFFFFF'}
>
Archived snippets
</AniLink></h3>
</Link></h3>
{archivedSnippets
.filter(snippet => snippet.archived)
.map(snippet => (
<SnippetCard
key={`a_snippet_${snippet.id}`}
short
archived
snippetData={snippet}
isDarkMode={props.isDarkMode}
/>
))}
<br/>
{staticPages.map(page => (
<SimpleCard
title={(
<AniLink
paintDrip
<Link
to={`/${page.url}`}
hex={props.isDarkMode ? '#434E76' : '#FFFFFF'}
>
{page.title}
</AniLink>
</Link>
)}
>
<p>{page.description}</p>
@ -170,7 +140,6 @@ export const listPageQuery = graphql`
title
attributes {
tags
text
}
}
}
@ -180,28 +149,16 @@ export const listPageQuery = graphql`
title
attributes {
tags
text
}
}
}
allMarkdownRemark(
limit: 1000
sort: { fields: [frontmatter___title], order: ASC }
) {
totalCount
allMarkdownRemark {
edges {
node {
id
html
rawMarkdownBody
fields {
slug
}
frontmatter {
title
tags
}
fileAbsolutePath
}
}
}

View File

@ -8,8 +8,6 @@ import Meta from '../components/Meta';
import Search from '../components/Search';
import SnippetCard from '../components/SnippetCard';
import { getRawCodeBlocks as getCodeBlocks } from '../util';
// ===================================================
// Search page
// ===================================================
@ -20,13 +18,7 @@ const SearchPage = props => {
v => v.node.frontmatter.title === snippet.title,
).node.html,
tags: snippet.attributes.tags,
text: snippet.attributes.text,
id: snippet.id,
code: getCodeBlocks(
props.data.allMarkdownRemark.edges.find(
v => v.node.frontmatter.title === snippet.title,
).node.rawMarkdownBody,
).code,
}));
const [searchQuery, setSearchQuery] = React.useState(props.searchQuery);
@ -116,26 +108,15 @@ export const searchPageQuery = graphql`
title
attributes {
tags
text
}
}
}
allMarkdownRemark(
limit: 1000
sort: { fields: [frontmatter___title], order: ASC }
) {
totalCount
allMarkdownRemark {
edges {
node {
id
node {
html
rawMarkdownBody
fields {
slug
}
frontmatter {
title
tags
}
}
}

View File

@ -126,7 +126,7 @@
}
// Tags
.tag {
:not(.token).tag {
transition: 0.3s ease all;
border: 2px solid var(--tag-border-color);
border-radius: 0.25rem;
@ -143,8 +143,3 @@
margin-top: 0.375rem;
}
}
// Animation for card example
.roll-up-height {
transition: height 0.3s ease-in-out;
}

View File

@ -28,7 +28,7 @@
font-style: normal;
font-weight: 300;
font-display: swap;
src: local('Noto Sans Light'), local('NotoSans-Light'), url(../../../assets/NotoSans-Light.ttf) format('truetype');
src: local('Noto Sans Light'), local('NotoSans-Light'), url(../../../assets/NotoSans-Light.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -36,7 +36,7 @@
font-style: italic;
font-weight: 300;
font-display: swap;
src: local('Noto Sans LightItalic'), local('NotoSans-LightItalic'), url(../../../assets/NotoSans-LightItalic.ttf) format('truetype');
src: local('Noto Sans LightItalic'), local('NotoSans-LightItalic'), url(../../../assets/NotoSans-LightItalic.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -44,7 +44,7 @@
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Noto Sans'), local('NotoSans-Regular'), url(../../../assets/NotoSans-Regular.ttf) format('truetype');
src: local('Noto Sans'), local('NotoSans-Regular'), url(../../../assets/NotoSans-Regular.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -52,7 +52,7 @@
font-style: italic;
font-weight: 400;
font-display: swap;
src: local('Noto Sans Italic'), local('NotoSans-Italic'), url(../../../assets/NotoSans-Italic.ttf) format('truetype');
src: local('Noto Sans Italic'), local('NotoSans-Italic'), url(../../../assets/NotoSans-Italic.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -60,7 +60,7 @@
font-style: normal;
font-weight: 500;
font-display: swap;
src: local('Noto Sans Medium'), local('NotoSans-Medium'), url(../../../assets/NotoSans-Medium.ttf) format('truetype');
src: local('Noto Sans Medium'), local('NotoSans-Medium'), url(../../../assets/NotoSans-Medium.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -68,7 +68,7 @@
font-style: italic;
font-weight: 500;
font-display: swap;
src: local('Noto Sans Medium Italic'), local('NotoSans-MediumItalic'), url(../../../assets/NotoSans-MediumItalic.ttf) format('truetype');
src: local('Noto Sans Medium Italic'), local('NotoSans-MediumItalic'), url(../../../assets/NotoSans-MediumItalic.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -76,7 +76,7 @@
font-style: normal;
font-weight: 600;
font-display: swap;
src: local('Noto Sans SemiBold'), local('NotoSans-SemiBold'), url(../../../assets/NotoSans-SemiBold.ttf) format('truetype');
src: local('Noto Sans SemiBold'), local('NotoSans-SemiBold'), url(../../../assets/NotoSans-SemiBold.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
@ -84,6 +84,6 @@
font-style: italic;
font-weight: 600;
font-display: swap;
src: local('Noto Sans SemiBold Italic'), local('NotoSans-SemiBoldItalic'), url(../../../assets/NotoSans-SemiBoldItalic.ttf) format('truetype');
src: local('Noto Sans SemiBold Italic'), local('NotoSans-SemiBoldItalic'), url(../../../assets/NotoSans-SemiBoldItalic.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@ -63,23 +63,3 @@
}
}
}
// Animate transition between light and dark mode
.cross-fade-leave {
transform: scale(1);
}
.cross-fade-leave.cross-fade-leave-active {
transition: all 0.3s cubic-bezier(0.47, 0, 0.745, 0.715);
transform: scale(0.1);
}
.cross-fade-enter {
transform: scale(0.1);
}
.cross-fade-enter.cross-fade-enter-active {
transition: all 0.3s cubic-bezier(0.215, 0.610, 0.355, 1);
transform: scale(0.95)
}
.cross-fade-height {
height: 61px;
transition: height 0.15s ease-in-out 0.15s;
}

View File

@ -1,12 +1,11 @@
import React from 'react';
import { graphql } from 'gatsby';
import { graphql, Link } from 'gatsby';
import { connect } from 'react-redux';
import Meta from '../components/Meta';
import Shell from '../components/Shell';
import SnippetCard from '../components/SnippetCard';
import BackArrowIcon from '../components/SVGs/BackArrowIcon';
import AniLink from 'gatsby-plugin-transition-link/AniLink';
// ===================================================
// Individual snippet page template
@ -21,16 +20,13 @@ const SnippetPage = props => {
<>
<Meta title={post.frontmatter.title} description={post.excerpt} />
<Shell>
<AniLink
<Link
className='link-back'
to={`${props.lastPageUrl}`}
cover
direction='right'
bg={props.isDarkMode ? '#434E76' : '#FFFFFF'}
>
<BackArrowIcon />
&nbsp;&nbsp;Back to {props.lastPageTitle}
</AniLink>
</Link>
<SnippetCard
snippetData={{
title: postData.title,

View File

@ -34,7 +34,6 @@ const TagRoute = props => {
snippetData={{
title: node.frontmatter.title,
html: node.html,
code: getCodeBlocks(node.rawMarkdownBody).code,
tags: node.frontmatter.tags.split(',').map(v => v.trim()),
id: node.fields.slug.slice(1),
}}
@ -68,7 +67,6 @@ export const tagPageQuery = graphql`
node {
id
html
rawMarkdownBody
fields {
slug
}

View File

@ -7,19 +7,10 @@ const capitalize = (str, lowerRest = false) =>
// Get the textual content in a gatsby page
const getTextualContent = (str, noExplain = false) => {
const regex = /([\s\S]*?)<div class="gatsby-highlight"/g;
const results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) regex.lastIndex += 1;
m.forEach((match, groupIndex) => {
results.push(match);
});
}
const result = str.slice(0, str.indexOf('<div class="gatsby-highlight"'));
if (noExplain)
return results[1].slice(0, results[1].lastIndexOf('<p>'));
return results[1];
return result.slice(0, result.lastIndexOf('<p>'));
return result;
};
// Gets the code blocks in a gatsby page