1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-22 17:23:50 +01:00
This commit is contained in:
Matthias Kretschmann 2021-03-14 01:34:04 +01:00
parent c35e26ea14
commit 273adeecb4
Signed by: m
GPG Key ID: 606EEEF3C479A91F
44 changed files with 32751 additions and 248 deletions

32669
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,8 @@
"gatsby-plugin-sharp": "^3.0.1",
"gatsby-plugin-sitemap": "^3.0.0",
"gatsby-plugin-svgr": "^3.0.0-beta.0",
"gatsby-plugin-webpack-size": "^1.0.0",
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.21",
"gatsby-plugin-webpack-size": "^2.0.1",
"gatsby-redirect-from": "^0.3.0",
"gatsby-remark-autolink-headers": "^3.0.0",
"gatsby-remark-breaks": "^1.0.0",
@ -99,7 +100,7 @@
"@types/shortid": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.17.0",
"@welldone-software/why-did-you-render": "^6.1.0",
"@welldone-software/why-did-you-render": "^6.1.1",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-graphql": "^4.0.0",
@ -108,10 +109,9 @@
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-testing-library": "^3.10.1",
"fs-extra": "^9.1.0",
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.21",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"markdownlint-cli": "^0.26.0",
"markdownlint-cli": "^0.27.1",
"node-iptc": "^1.0.5",
"npm-run-all": "^4.1.5",
"ora": "^5.3.0",
@ -121,10 +121,11 @@
"stylelint": "^13.12.0",
"stylelint-config-css-modules": "^2.2.0",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^20.0.0",
"stylelint-config-standard": "^21.0.0",
"stylelint-prettier": "^1.2.0",
"ts-node": "^9.1.1",
"typescript": "^4.2.3"
"typescript": "^4.2.3",
"typescript-plugin-css-modules": "^3.2.0"
},
"repository": {
"type": "git",

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import Typekit from './atoms/Typekit'
import Header from './organisms/Header'
import Footer from './organisms/Footer'
import * as styles from './Layout.module.css'
import { document, content } from './Layout.module.css'
// if (process.env.NODE_ENV !== 'production') {
// // eslint-disable-next-line
@ -16,8 +16,8 @@ export default function Layout({ children }: { children: any }): ReactElement {
<Typekit />
<Header />
<main className={styles.document} id="document">
<div className={styles.content}>{children}</div>
<main className={document} id="document">
<div className={content}>{children}</div>
</main>
<Footer />

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import remark from 'remark'
import remarkReact from 'remark-react'
import * as styles from './Changelog.module.css'
import { title, content, source } from './Changelog.module.css'
import { GitHub, GitHubRepo } from '../../@types/GitHub'
export function PureChangelog({
@ -30,20 +30,20 @@ export function PureChangelog({
const filePathDisplay = `${owner.login}/${repo}:CHANGELOG.md`
return (
<div className={styles.changelog}>
<h2 className={styles.title} id="changelog">
<>
<h2 className={title} id="changelog">
Changelog
</h2>
<div className={styles.content}>
<div className={content}>
{changelogHtml}
<p className={styles.source}>
<p className={source}>
sourced from{' '}
<a href={filePathUrl}>
<code>{filePathDisplay}</code>
</a>
</p>
</div>
</div>
</>
)
}

View File

@ -6,5 +6,6 @@
}
.wide {
composes: container;
max-width: none;
}

View File

@ -1,12 +1,12 @@
import React, { ReactElement } from 'react'
import loadable from '@loadable/component'
import * as styles from './Copy.module.css'
import { copied, button } from './Copy.module.css'
import Icon from './Icon'
const Clipboard = loadable(() => import('react-clipboard.js'))
const onCopySuccess = (e: any) => {
e.trigger.classList.add(styles.copied)
e.trigger.classList.add(copied)
}
export default function Copy({ text }: { text: string }): ReactElement {
@ -15,7 +15,7 @@ export default function Copy({ text }: { text: string }): ReactElement {
data-clipboard-text={text}
button-title="Copy to clipboard"
onSuccess={(e: ClipboardJS.Event) => onCopySuccess(e)}
className={styles.button}
className={button}
>
<Icon name="Copy" />
</Clipboard>

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import ExifMap from './ExifMap'
import * as styles from './Exif.module.css'
import { exif as styleExif, data, map } from './Exif.module.css'
import { Exif as ExifMeta } from '../../@types/Image'
import Icon from './Icon'
@ -31,8 +31,8 @@ export default function Exif({ exif }: { exif: ExifMeta }): ReactElement {
} = exif.formatted
return (
<aside className={styles.exif}>
<div className={styles.data}>
<aside className={styleExif}>
<div className={data}>
{model && <ExifData title="Camera model" value={model} icon="Camera" />}
{focalLength && (
<ExifData title="Focal length" value={focalLength} icon="Crosshair" />
@ -49,7 +49,7 @@ export default function Exif({ exif }: { exif: ExifMeta }): ReactElement {
{iso && <ExifData title="ISO" value={iso} icon="Maximize" />}
</div>
{gps && gps.latitude && (
<div className={styles.map}>
<div className={map}>
<ExifMap gps={gps} />
</div>
)}

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react'
import * as styles from './Hamburger.module.css'
import { button, hamburger, line } from './Hamburger.module.css'
export default function Hamburger({
onClick
@ -7,16 +7,11 @@ export default function Hamburger({
onClick(): void
}): ReactElement {
return (
<button
type="button"
title="Menu"
className={styles.button}
onClick={onClick}
>
<span className={styles.hamburger}>
<span className={styles.line} />
<span className={styles.line} />
<span className={styles.line} />
<button type="button" title="Menu" className={button} onClick={onClick}>
<span className={hamburger}>
<span className={line} />
<span className={line} />
<span className={line} />
</span>
</button>
)

View File

@ -27,7 +27,7 @@ import {
import { ReactComponent as Jsonfeed } from '../../images/jsonfeed.svg'
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
import { ReactComponent as Stopwatch } from '../../images/stopwatch.svg'
import * as styles from './Icon.module.css'
import { icon } from './Icon.module.css'
const components: any = {
Download: ArrowDownCircle,
@ -55,12 +55,12 @@ const components: any = {
Crosshair
}
const Icon = ({ name }: { name: string }): ReactElement => {
const Icon = ({ name, ...props }: { name: string }): ReactElement => {
const IconMapped = components[name]
// const IconFeather = (Feather as any)[name]
if (!IconMapped) return null
return <IconMapped className={styles.icon} />
return <IconMapped className={icon} {...props} />
}
export default Icon

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import * as styles from './Input.module.css'
import { input } from './Input.module.css'
export default function Input({ className, ...props }: any): ReactElement {
return <input className={`${styles.input} ${className || ''}`} {...props} />
return <input className={`${input} ${className || ''}`} {...props} />
}

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import { QRCode } from 'react-qr-svg'
import * as styles from './Qr.module.css'
import { qr, code } from './Qr.module.css'
import Copy from './Copy'
export default function Qr({
@ -19,10 +19,10 @@ export default function Qr({
level="Q"
style={{ width: 120 }}
value={address}
className={styles.qr}
className={qr}
/>
<pre className={styles.code}>
<pre className={code}>
<code>{address}</code>
<Copy text={address} />
</pre>

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import * as styles from './Tag.module.css'
import { tag, count as styleCount } from './Tag.module.css'
export default function Tag({
name,
@ -14,9 +14,9 @@ export default function Tag({
style?: any
}): ReactElement {
return (
<Link className={styles.tag} to={url} style={style}>
<Link className={tag} to={url} style={style}>
{name}
{count && <span className={styles.count}>{count}</span>}
{count && <span className={styleCount}>{count}</span>}
</Link>
)
}

View File

@ -2,7 +2,7 @@ import React, { ReactElement, useState } from 'react'
import { Helmet } from 'react-helmet'
import { Link } from 'gatsby'
import Hamburger from '../atoms/Hamburger'
import * as styles from './Menu.module.css'
import { menu as styleMenu } from './Menu.module.css'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import { MenuItem } from '../../@types/Site'
@ -28,7 +28,7 @@ export default function Menu(): ReactElement {
<html className={menuOpen ? 'has-menu-open' : undefined} lang="en" />
</Helmet>
<Hamburger onClick={toggleMenu} />
<nav className={styles.menu}>
<nav className={styleMenu}>
<ul>{MenuItems}</ul>
</nav>
</>

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import Icon from '../atoms/Icon'
import * as styles from './Networks.module.css'
import { link as styleLink } from './Networks.module.css'
function NetworkIcon({ link }: { link: string }) {
let IconComp
@ -28,7 +28,7 @@ export default function IconLinks({
return (
<p>
{links.map((link: string) => (
<a key={link} className={styles.link} href={link} title={link}>
<a key={link} className={styleLink} href={link} title={link}>
<NetworkIcon link={link} />
</a>
))}

View File

@ -1,9 +1,13 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import * as styles from './Pagination.module.css'
import shortid from 'shortid'
import { PageContext } from '../../@types/Post'
import Icon from '../atoms/Icon'
import {
current as styleCurrent,
number,
pagination
} from './Pagination.module.css'
const PageNumber = ({
i,
@ -14,7 +18,7 @@ const PageNumber = ({
slug: string
current?: boolean
}) => {
const classes = current ? styles.current : styles.number
const classes = current ? styleCurrent : number
const link = i === 0 ? slug : `${slug}page/${i + 1}`
return (
@ -62,7 +66,7 @@ export default function Pagination({
const isLast = currentPageNumber === numPages
return (
<div className={styles.pagination}>
<div className={pagination}>
{!isFirst && <PrevNext prevPagePath={prevPagePath} />}
{Array.from({ length: numPages }, (_, i) => (
<PageNumber

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import Time from '../atoms/Time'
import * as styles from './PostDate.module.css'
import { time } from './PostDate.module.css'
export default function PostDate({
date,
@ -10,7 +10,7 @@ export default function PostDate({
updated?: string
}): ReactElement {
return (
<div className={styles.time}>
<div className={time}>
<Time date={date} />
{updated && ' • updated '}
{updated && <Time date={updated} />}

View File

@ -1,7 +1,7 @@
import React, { ReactElement, useState } from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import PostTeaser from './PostTeaser'
import * as styles from './RelatedPosts.module.css'
import { relatedPosts, title, button } from './RelatedPosts.module.css'
import { Post, Frontmatter } from '../../@types/Post'
import { PhotoThumb } from '../templates/Photos'
@ -78,10 +78,10 @@ export default function RelatedPosts({
}
return (
<aside className={styles.relatedPosts}>
<h1 className={styles.title}>
<aside className={relatedPosts}>
<h1 className={title}>
Related {isPhotos ? 'Photos' : 'Posts'}{' '}
<button className={styles.button} onClick={() => refreshPosts()}>
<button className={button} onClick={() => refreshPosts()}>
Refresh
</button>
</h1>

View File

@ -1,14 +1,9 @@
import React, { ReactElement } from 'react'
import * as styles from './SearchButton.module.css'
import { searchButton } from './SearchButton.module.css'
import Icon from '../../atoms/Icon'
const SearchButton = (props: any): ReactElement => (
<button
type="button"
title="Search"
className={styles.searchButton}
{...props}
>
<button type="button" title="Search" className={searchButton} {...props}>
<Icon name="Search" />
</button>
)

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import Input from '../../atoms/Input'
import Icon from '../../atoms/Icon'
import * as styles from './SearchInput.module.css'
import { searchInput, searchInputClose } from './SearchInput.module.css'
export default function SearchInput({
value,
@ -15,7 +15,7 @@ export default function SearchInput({
return (
<>
<Input
className={styles.searchInput}
className={searchInput}
type="search"
placeholder="Search everything"
autoFocus // eslint-disable-line
@ -23,7 +23,7 @@ export default function SearchInput({
onChange={onChange}
/>
<button
className={styles.searchInputClose}
className={searchInputClose}
onClick={onToggle}
title="Close search"
>

View File

@ -1,5 +1,9 @@
import React, { ReactElement } from 'react'
import * as styles from './SearchResultsEmpty.module.css'
import {
empty,
emptyMessage,
emptyMessageText
} from './SearchResultsEmpty.module.css'
import { Results } from './SearchResults'
const SearchResultsEmpty = ({
@ -9,9 +13,9 @@ const SearchResultsEmpty = ({
searchQuery: string
results: Results[]
}): ReactElement => (
<div className={styles.empty}>
<header className={styles.emptyMessage}>
<p className={styles.emptyMessageText}>
<div className={empty}>
<header className={emptyMessage}>
<p className={emptyMessageText}>
{searchQuery.length > 0 && results.length === 0
? 'No results found'
: 'Awaiting your input'}

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import { getSrc } from 'gatsby-plugin-image'
import IconLinks from './Networks'
import * as styles from './Vcard.module.css'
import { avatar as styleAvatar, description } from './Vcard.module.css'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
const query = graphql`
@ -34,13 +34,13 @@ export default function Vcard(): ReactElement {
return (
<>
<img
className={styles.avatar}
className={styleAvatar}
src={avatar}
width="80"
height="80"
alt="avatar"
/>
<p className={styles.description}>
<p className={description}>
Blog of designer &amp; developer{' '}
<a className="fn" rel="author" href={uri}>
{name}

View File

@ -2,7 +2,11 @@ import React, { ReactElement, useState, useEffect } from 'react'
import { toDataUrl } from 'ethereum-blockies'
import { formatEther } from '@ethersproject/units'
import useWeb3 from '../../../hooks/use-web3'
import * as styles from './Account.module.css'
import {
accountWrap,
blockies as styleBlockies,
balance
} from './Account.module.css'
export default function Account(): ReactElement {
const { library, account } = useWeb3()
@ -26,12 +30,12 @@ export default function Account(): ReactElement {
ethBalance && `Ξ${parseFloat(formatEther(ethBalance)).toPrecision(4)}`
return (
<div className={styles.accountWrap} title={account}>
<span className={styles.account}>
<img className={styles.blockies} src={blockies} alt="Blockies" />
<div className={accountWrap} title={account}>
<span>
<img className={styleBlockies} src={blockies} alt="Blockies" />
{accountDisplay}
</span>
<span className={styles.balance}>{balanceDisplay}</span>
<span className={balance}>{balanceDisplay}</span>
</div>
)
}

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react'
import * as styles from './Alert.module.css'
import { success, error, alert } from './Alert.module.css'
export function getTransactionMessage(
transactionHash?: string
@ -24,11 +24,7 @@ const constructMessage = (
: message && message.text
const classes = (status: string) =>
status === 'success'
? styles.success
: status === 'error'
? styles.error
: styles.alert
status === 'success' ? success : status === 'error' ? error : alert
export default function Alert({
transactionHash,

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect, ReactElement } from 'react'
import fetch from 'node-fetch'
import * as styles from './Conversion.module.css'
import { conversion as styleConversion } from './Conversion.module.css'
export async function getFiat(
amount: number
@ -43,7 +43,7 @@ export default function Conversion({
}, [amount])
return (
<div className={styles.conversion}>
<div className={styleConversion}>
<span>{dollar !== '0.00' && `= $ ${dollar}`}</span>
<span>{euro !== '0.00' && `= € ${euro}`}</span>
</div>

View File

@ -2,7 +2,7 @@ import React, { ReactElement, useState } from 'react'
import Input from '../../atoms/Input'
import Account from './Account'
import Conversion from './Conversion'
import * as styles from './InputGroup.module.css'
import { inputGroup, input, currency } from './InputGroup.module.css'
export default function InputGroup({
sendTransaction
@ -18,8 +18,8 @@ export default function InputGroup({
return (
<div>
<Account />
<div className={styles.inputGroup}>
<div className={styles.input}>
<div className={inputGroup}>
<div className={input}>
<Input
type="number"
value={amount}
@ -27,7 +27,7 @@ export default function InputGroup({
min="0"
step="0.01"
/>
<div className={styles.currency}>
<div className={currency}>
<span>ETH</span>
</div>
</div>

View File

@ -3,7 +3,7 @@ import { parseEther } from '@ethersproject/units'
import useWeb3, { connectors, getErrorMessage } from '../../../hooks/use-web3'
import InputGroup from './InputGroup'
import Alert, { getTransactionMessage } from './Alert'
import * as styles from './index.module.css'
import { web3 } from './index.module.css'
export default function Web3Donation({
address
@ -67,7 +67,7 @@ export default function Web3Donation({
}
return (
<div className={styles.web3}>
<div className={web3}>
{!active && !message ? (
<button className="link" onClick={() => activate(connectors.MetaMask)}>
Activate Web3

View File

@ -3,14 +3,14 @@ import { Link } from 'gatsby'
import Icon from '../atoms/Icon'
import Vcard from '../molecules/Vcard'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import * as styles from './Footer.module.css'
import { copyright, btc, footer } from './Footer.module.css'
function Copyright() {
const { name, uri, github } = useSiteMetadata().author
const year = new Date().getFullYear()
return (
<section className={styles.copyright}>
<section className={copyright}>
<p>
&copy; 2005&ndash;
{year + ' '}
@ -21,7 +21,7 @@ function Copyright() {
<Icon name="GitHub" />
View source
</a>
<Link to="/thanks" className={styles.btc}>
<Link to="/thanks" className={btc}>
<Icon name="Bitcoin" />
Say Thanks
</Link>
@ -33,7 +33,7 @@ function Copyright() {
export default class Footer extends PureComponent {
render(): ReactElement {
return (
<footer role="contentinfo" className={styles.footer}>
<footer role="contentinfo" className={footer}>
<Vcard />
<Copyright />
</footer>

View File

@ -4,20 +4,20 @@ import Search from '../molecules/Search'
import Menu from '../molecules/Menu'
import ThemeSwitch from '../molecules/ThemeSwitch'
import { ReactComponent as Logo } from '../../images/logo.svg'
import * as styles from './Header.module.css'
import { header, headerContent, title, logo, nav } from './Header.module.css'
export default class Header extends PureComponent {
render(): ReactElement {
return (
<header role="banner" className={styles.header}>
<div className={styles.headerContent}>
<h1 className={styles.title}>
<header role="banner" className={header}>
<div className={headerContent}>
<h1 className={title}>
<Link to="/">
<Logo className={styles.logo} /> kremalicious
<Logo className={logo} /> kremalicious
</Link>
</h1>
<nav role="navigation" className={styles.nav}>
<nav role="navigation" className={nav}>
<ThemeSwitch />
<Search />
<Menu />

View File

@ -2,9 +2,9 @@ import React, { ReactElement } from 'react'
import { graphql } from 'gatsby'
import { Post, PageContext } from '../../@types/Post'
import Pagination from '../molecules/Pagination'
import * as styles from './Archive.module.css'
import PostTeaser from '../molecules/PostTeaser'
import Page from './Page'
import { posts } from './Archive.module.css'
export default function Archive({
data,
@ -40,7 +40,7 @@ export default function Archive({
post={page}
pathname={pageContext.slug}
>
<div className={styles.posts}>{PostsList}</div>
<div className={posts}>{PostsList}</div>
{numPages > 1 && <Pagination pageContext={pageContext} />}
</Page>
)

View File

@ -2,7 +2,7 @@ import React, { ReactElement, ReactNode } from 'react'
import { Helmet } from 'react-helmet'
import { Post } from '../../@types/Post'
import SEO from '../atoms/SEO'
import * as styles from './Page.module.css'
import { pagetitle } from './Page.module.css'
export default function Page({
title,
@ -22,7 +22,7 @@ export default function Page({
<Helmet title={title} />
<SEO slug={pathname} postSEO post={post} />
<h1 className={styles.pagetitle}>{title}</h1>
<h1 className={pagetitle}>{title}</h1>
{section ? <section className={section}>{children}</section> : children}
</>
)

View File

@ -1,10 +1,10 @@
import React, { ReactElement } from 'react'
import { graphql, Link, PageProps } from 'gatsby'
import Page from './Page'
import { Post, PageContext } from '../../@types/Post'
import { Image } from '../atoms/Image'
import { photo as stylePhoto, photos as stylePhotos } from './Photos.module.css'
import Pagination from '../molecules/Pagination'
import Page from './Page'
import { photo as stylePhoto, photos as stylePhotos } from './Photos.module.css'
export const PhotoThumb = ({ photo }: { photo: Post }): ReactElement => {
const { title, image } = photo.frontmatter

View File

@ -1,30 +1,22 @@
import React, { ReactElement } from 'react'
import { useSiteMetadata } from '../../../hooks/use-site-metadata'
import * as styles from './Actions.module.css'
import { action, actionTitle, actionText, actions } from './Actions.module.css'
import Icon from '../../atoms/Icon'
interface ActionProps {
title: string
text: string
icon: string
url?: string
onClick?(): void
}
const IconComp = ({ text }: { text: string }) =>
text.includes('GitHub') ? (
<Icon name="GitHub" />
) : text.includes('Bitcoin') ? (
<Icon name="Bitcoin" />
) : (
<Icon name="Twitter" />
)
const Action = ({ title, text, url, onClick }: ActionProps) => {
const Action = ({ title, text, url, icon, onClick }: ActionProps) => {
return (
<a className={styles.action} href={url} onClick={onClick}>
<IconComp text={text} />
<h1 className={styles.actionTitle}>{title}</h1>
<p className={styles.actionText}>{text}</p>
<a className={action} href={url} onClick={onClick}>
<Icon name={icon} />
<h1 className={actionTitle}>{title}</h1>
<p className={actionText}>{text}</p>
</a>
)
}
@ -40,21 +32,24 @@ export default function PostActions({
const urlTwitter = `https://twitter.com/intent/tweet?text=@kremalicious&url=${siteUrl}${slug}`
return (
<aside className={styles.actions}>
<aside className={actions}>
<Action
title="Have a comment?"
text="Hit me up @kremalicious"
url={urlTwitter}
icon="Twitter"
/>
<Action
title="Found something useful?"
text="Say thanks with BTC or ETH"
url="/thanks"
icon="Bitcoin"
/>
<Action
title="Edit on GitHub"
text="Contribute to this post"
url={githubLink}
icon="GitHub"
/>
</aside>
)

View File

@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import Changelog from '../../atoms/Changelog'
import { Post } from '../../../@types/Post'
import PostToc from './Toc'
import * as styles from './Content.module.css'
import { content as styleContent } from './Content.module.css'
export default function PostContent({ post }: { post: Post }): ReactElement {
const separator = '<!-- more -->'
@ -27,7 +27,7 @@ export default function PostContent({ post }: { post: Post }): ReactElement {
)}
<div
dangerouslySetInnerHTML={{ __html: content }}
className={styles.content}
className={styleContent}
/>
{changelog && <Changelog repo={changelog} />}
</>

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react'
import * as styles from './Lead.module.css'
import { lead as styleLead } from './Lead.module.css'
import { Post } from '../../../@types/Post'
// Extract lead paragraph from content
@ -23,7 +23,7 @@ const PostLead = ({
return (
<div
className={`${styles.lead} ${className && className}`}
className={`${styleLead} ${className && className}`}
dangerouslySetInnerHTML={{ __html: lead }}
/>
)

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import stylesPostMore from './More.module.css'
import * as styles from './LinkActions.module.css'
import { postMore } from './More.module.css'
import { postLinkActions } from './LinkActions.module.css'
import Icon from '../../atoms/Icon'
const PostLinkActions = ({
@ -11,8 +11,8 @@ const PostLinkActions = ({
linkurl?: string
slug: string
}): ReactElement => (
<aside className={styles.postLinkActions}>
<a className={stylesPostMore.postMore} href={linkurl}>
<aside className={postLinkActions}>
<a className={postMore} href={linkurl}>
Go to source <Icon name="ExternalLink" />
</a>
<Link to={slug} rel="tooltip" title="Permalink">

View File

@ -3,7 +3,13 @@ import { Link } from 'gatsby'
import slugify from 'slugify'
import Tag from '../../atoms/Tag'
import { useSiteMetadata } from '../../../hooks/use-site-metadata'
import * as styles from './Meta.module.css'
import {
entryMeta,
byline,
by,
type as styleType,
tags as styleTags
} from './Meta.module.css'
import { Post } from '../../../@types/Post'
import shortid from 'shortid'
import PostDate from '../../molecules/PostDate'
@ -14,9 +20,9 @@ export default function PostMeta({ post }: { post: Post }): ReactElement {
const { date, type } = post.fields
return (
<footer className={styles.entryMeta}>
<div className={styles.byline}>
<span className={styles.by}>by</span>
<footer className={entryMeta}>
<div className={byline}>
<span className={by}>by</span>
<a className="fn" rel="author" href={siteMeta.author.uri}>
{author || siteMeta.author.name}
</a>
@ -25,13 +31,13 @@ export default function PostMeta({ post }: { post: Post }): ReactElement {
<PostDate date={date} updated={updated} />
{type && type === 'photo' && (
<div className={styles.type}>
<div className={styleType}>
<Link to={`/${slugify(type)}s/`}>{type}s</Link>
</div>
)}
{tags && (
<div className={styles.tags}>
<div className={styleTags}>
{tags.map((tag: string) => {
const url = `/archive/${slugify(tag)}/`
return <Tag key={shortid.generate()} name={tag} url={url} />

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import Icon from '../../atoms/Icon'
import * as styles from './More.module.css'
import { postMore } from './More.module.css'
const PostMore = ({
to,
@ -10,7 +10,7 @@ const PostMore = ({
to: string
children: string
}): ReactElement => (
<Link className={styles.postMore} to={to}>
<Link className={postMore} to={to}>
{children}
<Icon name="ChevronRight" />
</Link>

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import Icon from '../../atoms/Icon'
import * as styles from './PrevNext.module.css'
import { prevnext, label, title } from './PrevNext.module.css'
interface Node {
title: string
@ -14,21 +14,21 @@ interface PrevNextProps {
}
const PrevNext = ({ prev, next }: PrevNextProps): ReactElement => (
<nav className={styles.prevnext}>
<nav className={prevnext}>
<div>
{prev && (
<Link to={prev.slug}>
<Icon name="ChevronLeft" />
<p className={styles.label}>Newer</p>
<h3 className={styles.title}>{prev.title}</h3>
<p className={label}>Newer</p>
<h3 className={title}>{prev.title}</h3>
</Link>
)}
</div>
<div>
{next && (
<Link to={next.slug}>
<p className={styles.label}>Older</p>
<h3 className={styles.title}>{next.title}</h3>
<p className={label}>Older</p>
<h3 className={title}>{next.title}</h3>
<Icon name="ChevronRight" />
</Link>
)}

View File

@ -3,11 +3,11 @@
margin-bottom: calc(var(--spacer) / 8);
}
.title__link {
.titleLink {
font-size: var(--font-size-h3);
}
.title__link svg {
.titleLink svg {
width: var(--font-size-base);
height: var(--font-size-base);
display: inline-block;

View File

@ -1,5 +1,9 @@
import React, { ReactElement } from 'react'
import * as styles from './Title.module.css'
import {
title as styleTitle,
titleLink,
linkurl as styleLinkurl
} from './Title.module.css'
import Icon from '../../atoms/Icon'
import PostDate from '../../molecules/PostDate'
@ -20,20 +24,16 @@ export default function PostTitle({
return linkurl ? (
<>
<h1
className={`${styles.title} ${styles.title__link} ${
className && className
}`}
>
<h1 className={`${styleTitle} ${titleLink} ${className && className}`}>
<a href={linkurl} title={`Go to source: ${linkurl}`}>
{title} <Icon name="ExternalLink" />
</a>
</h1>
<div className={styles.linkurl}>{linkHostname}</div>
<div className={styleLinkurl}>{linkHostname}</div>
</>
) : (
<>
<h1 className={`${styles.title} ${className && className}`}>{title}</h1>
<h1 className={`${styleTitle} ${className && className}`}>{title}</h1>
{date && <PostDate date={date} updated={updated} />}
</>
)

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react'
import * as styles from './Toc.module.css'
import { toc } from './Toc.module.css'
const PostToc = ({
tableOfContents
@ -8,7 +8,7 @@ const PostToc = ({
}): ReactElement => {
return (
<nav
className={styles.toc}
className={toc}
dangerouslySetInnerHTML={{ __html: tableOfContents }}
/>
)

View File

@ -12,7 +12,7 @@ import PostActions from './Actions'
import PostLinkActions from './LinkActions'
import PostMeta from './Meta'
import PrevNext from './PrevNext'
import * as styles from './index.module.css'
import { hentry, image as styleImage } from './index.module.css'
import { Image } from '../../atoms/Image'
export default function Post({
@ -37,7 +37,7 @@ export default function Post({
<SEO slug={slug} post={post} postSEO />
<article className={styles.hentry}>
<article className={hentry}>
<header>
<PostTitle
linkurl={linkurl}
@ -52,7 +52,7 @@ export default function Post({
{image && (
<Image
className={styles.image}
className={styleImage}
image={(image as any).childImageSharp.gatsbyImageData}
alt={title}
/>

View File

@ -1,5 +1,5 @@
.section {
composes: container.wide from '../components/atoms/Container.module.css';
composes: wide from '../components/atoms/Container.module.css';
}
.section:not(:first-child) {

View File

@ -10,7 +10,8 @@
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"noImplicitAny": true
"noImplicitAny": true,
"plugins": [{ "name": "typescript-plugin-css-modules" }]
},
"exclude": ["node_modules", "public", ".cache", "*.js"],
"include": ["./src/**/*", "./scripts/*.ts", "./jest/**/*"]