1
0
mirror of https://github.com/kremalicious/blog.git synced 2025-02-14 21:10:25 +01:00

Merge pull request #498 from kremalicious/feature/css-modules-plugin-image

Gatsby v3
This commit is contained in:
Matthias Kretschmann 2021-03-22 23:39:46 +01:00 committed by GitHub
commit f976a5581a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
195 changed files with 12362 additions and 14868 deletions

View File

@ -5,7 +5,7 @@
"stylelint-prettier/recommended"
],
"plugins": ["stylelint-prettier"],
"syntax": "scss",
"syntax": "css",
"rules": {
"prettier/prettier": true,
"property-no-unknown": [

View File

@ -42,9 +42,5 @@ module.exports = {
title: '/Uses',
link: '/uses'
}
],
darkModeConfig: {
classNameDark: 'dark',
classNameLight: 'light'
}
]
}

View File

@ -29,7 +29,7 @@ These icons are free for your personal use and include icons for all file types
Get them and have fun.
<p class="content-download">
<a class="icon-download" href="../media/aperturefiletypes_by_kremalicious.zip">Download</a>
<a class="icon-download btn btn-primary" href="../media/aperturefiletypes_by_kremalicious.zip">Download</a>
</p>
And don't forget to read my article about [how to change the generic image icons in Mac OS X Leopard](/changing-the-image-icons-in-mac-os-x-leopard/).

View File

@ -19,5 +19,5 @@ I have added my first wallpaper to the Goodies section on this website. It's a s
You can get the wallpaper by browsing [my Goodies section](http://www.kremalicious.com/goodies/) and clicking on the download link for the wallpaper. The download package also includes a custom folder icon (512px as .icns, .png, .ico) with the Chives wallpaper on it.
<p class="content-download">
<a class="icon-download" href="../media/chives_by_kremalicious.zip">Download Chives Wallpaper</a>
<a class="icon-download btn btn-primary" href="../media/chives_by_kremalicious.zip">Download Chives Wallpaper</a>
</p>

View File

@ -92,7 +92,7 @@ Brief description of Daguerres role in the invention of photography process by T
This icon package was exclusively announced first on [MacThemes](http://macthemes2.net) and you can download it from the [Goodies section on this website](/goodies/) or directly via this link:
<p class="content-download">
<a class="btn icon-download" href="../media/niepces_camera_obscura_by_kremalicious.zip">Download</a>
<a class="btn btn-primary icon-download" href="../media/niepces_camera_obscura_by_kremalicious.zip">Download</a>
</p>
In addition to these icons you can download the [associated wallpapers for your desktop or your iPhone](http://www.kremalicious.com/2008/06/new-goodie-niepces-camera-obscura-wallpaper-pack/).

View File

@ -19,7 +19,7 @@ In addition to my Niépce's Camera Obscura icons for Aperture and iPhoto I have
All Wallpapers are using a custom designed background which imitates the look of a metal plate like it was used in Niépce's experiments although it wasn't golden. To make it more Mac style I have added a stenciled dots pattern (which is a commonly used reference to the front design of the MacPro).
<p class="content-download">
<a class="icon-download" href="../media/niepces_camera_obscura_wallpaper_pack_by_kremalicious.zip">Download Niépce's Camera Obscura Wallpaper</a>
<a class="icon-download btn btn-primary" href="../media/niepces_camera_obscura_wallpaper_pack_by_kremalicious.zip">Download Niépce's Camera Obscura Wallpaper</a>
</p>
Here are the details:

View File

@ -376,7 +376,7 @@ Here you can see the icons included in the Server Displays icon pack:
Because I've just modified Apple's standard icons these icons are just available via this blog post and they will not show up in my Goodies section. Just download the whole package directly via this link:
<p class="content-download">
<a class="icon-download" href="../media/server_displays_by_kremalicious.zip">Download Server Display Icons <span>zip</span></a>
<a class="icon-download btn btn-primary" href="../media/server_displays_by_kremalicious.zip">Download Server Display Icons <span>zip</span></a>
</p>
### How to use the icons

View File

@ -20,7 +20,7 @@ As always these desktop icons are free for you personal and non-commercial use.
The whole package includes 7 icons either packed in a nice tagged iContainer for use with Candybar or in Mac + Win + Linux compatible formats. If you have such an Icy Box case grab the icons. Have fun!
<p class="content-download">
<a class="icon-download" href="../media/icybox_by_kremalicious.zip">Download Icy Box Icons <span>zip</span></a>
<a class="icon-download btn btn-primary" href="../media/icybox_by_kremalicious.zip">Download Icy Box Icons <span>zip</span></a>
</p>
- Replacement icons for the silver and black Icy Box external aluminium case with USB interface

View File

@ -18,6 +18,6 @@ Show your geeky love for extraterrestrial universities! This is a simple Futuram
As usual you can [grab the whole zip package from my Goodies page](http://www.kremalicious.com/goodies/#wall) and have fun.
<p class="content-download">
<a class="icon-download" href="../media/mars-u-wall-by-kremalicious.zip">Download Futurama Mars U Wallpaper <span>zip</span></a>
<p class="content-download ">
<a class="icon-download btn btn-primary" href="../media/mars-u-wall-by-kremalicious.zip">Download Futurama Mars U Wallpaper <span>zip</span></a>
</p>

View File

@ -22,5 +22,5 @@ Ive just released my own coffee cup icon, enjoy:
## Download
<p class="content-download">
<a class="icon-download" href="../media/coffee_cup_by_kremalicious.zip">Download Coffee Cup Icons</a>
<a class="icon-download btn btn-primary" href="../media/coffee_cup_by_kremalicious.zip">Download Coffee Cup Icons</a>
</p>

View File

@ -29,5 +29,5 @@ Simple, high-resolution Futurama tribute wallpaper pack inspired by the latest F
Seriously, the pink versions are burning my eyes but the pink is a good reference to the events in the recent movie.
<p class="content-download">
<a class="icon-download" href="../media/out-of-whale-oil-wall-by-kremalicious.zip">Download</a>
<a class="icon-download btn btn-primary" href="../media/out-of-whale-oil-wall-by-kremalicious.zip">Download</a>
</p>

View File

@ -16,7 +16,7 @@ Here's a quick twitter icon for use on your websites which is kind of a by-produ
This icon comes in various formats (PNG, ICNS, iContainer) and in 4 different sizes (128px, 48px, 32px, 16px) with each icon size redrawn (of course). Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab these icons while they're hot.
<p class="content-download">
<a class="icon-download" href="../media/twitter-crisp-by-kremalicious.zip">Download</a>
<a class="icon-download btn btn-primary" href="../media/twitter-crisp-by-kremalicious.zip">Download</a>
</p>
Use them on any web project you like and/or [convert them into a send to twitter link](http://kremalicious.com/ultimate-coda-wordpress-share-link-bonanza/). Have fun!

View File

@ -26,7 +26,7 @@ The icon comes in various formats (iContainer, icns, png) in sizes from 512px-16
Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab this icon while it's hot.
<p class="content-download">
<a class="icon-download" href="../media/adiumeetie-by-kremalicious.zip">Download</a>
<a class="icon-download btn btn-primary" href="../media/adiumeetie-by-kremalicious.zip">Download</a>
</p>
## Adium Icon Usage

View File

@ -20,7 +20,7 @@ tags:
Just head over [to my Goodies page](http://www.kremalicious.com/goodies/) or click the following download button and grab these replacement icons.
<p class="content-download">
<a class="icon-download" href="../media/delibar-by-kremalicious.zip">Download</a>
<a class="icon-download btn btn-primary" href="../media/delibar-by-kremalicious.zip">Download</a>
</p>
## Icon Usage

View File

@ -27,7 +27,7 @@ The homescreen icons pixels in the 16px version needed all more vivid colors to
The icons come in various formats: iContainer, ICNS, ICO and PNG files for each size. Just click the following download button to grab the whole pack:
<p class="content-download">
<a class="icon-download" href="../media/ipixelpad_by_kremalicious.zip">Download</a>
<a class="icon-download btn btn-primary" href="../media/ipixelpad_by_kremalicious.zip">Download</a>
</p>
## License

View File

@ -23,6 +23,6 @@ The wallpaper comes in four versions with two color variations and two text vari
You can grab the full zip-package with versions for Desktop, iPad, iPhone & Android included:
<p class="content-download">
<a class="btn-primary icon-download" href="../media/momcorp_wall_by_kremalicious.zip">Download</a>
<a href="http://krlc.us/givecoffee" class="icon-heart">Donate</a>
<a class="btn btn-primary icon-download" href="../media/momcorp_wall_by_kremalicious.zip">Download</a>
<a href="http://krlc.us/givecoffee" class="btn icon-heart">Donate</a>
</p>

View File

@ -24,7 +24,7 @@ The plugin is localized in english, german & spanish (thanks to Andrew Kurtis fr
You can just install the plugin via the automatic backend installer under _Plugins > Add New_, activate and enjoy the red badges.
<p class="content-download">
<a href="http://wordpress.org/extend/plugins/badged" class="btn-primary icon-wordpress">Plugin Page</a> <a class="btn-primary icon-github" href="https://github.com/kremalicious/Badged">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart btn">Donate</a>
<a href="http://wordpress.org/extend/plugins/badged" class="btn btn-primary icon-wordpress">Plugin Page</a> <a class="btn btn-primary icon-github" href="https://github.com/kremalicious/Badged">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart btn">Donate</a>
</p>
The plugin is hosted on GitHub and will always be mirrored in the WordPress plugins directory. But in case you want to install the plugin manually:

View File

@ -33,7 +33,7 @@ So if you value quality and want pixel perfect icons in your admin area you need
Ive put the template along with the implementation examples from the next section on [github](https://github.com/kremalicious/wp-icons-template). You can just download the whole package right away:
<p class="content-download">
<a href="https://github.com/kremalicious/wp-icons-template/zipball/master" class="btn-primary icon-download">Download</a> <a href="https://github.com/kremalicious/wp-icons-template" class="icon-github">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart">Donate</a>
<a href="https://github.com/kremalicious/wp-icons-template/zipball/master" class="btn btn-primary icon-download">Download</a> <a href="https://github.com/kremalicious/wp-icons-template" class="icon-github btn btn-primary">GitHub</a> <a href="http://krlc.us/givecoffee" class="icon-heart btn">Donate</a>
</p>
### Usage

View File

@ -21,8 +21,8 @@ The above picture might be blurry depending on the device you're using so here's
They are completely styled with CSS3 so they're sharp on all screens no matter how high the dpi. Have a look at the [full demo](http://lab.kremalicious.com/kbdfun/) or grab the project folder with the CSS & LESS files from GitHub. The code is under the MIT license so you're free to use it in any personal or commercial project.
<p class="content-download">
<a class="btn-primary icon-compass" href="http://lab.kremalicious.com/kbdfun/">Demo</a>
<a class="icon-github" href="https://github.com/kremalicious/kbdfun/">Github</a>
<a class="btn btn-primary icon-compass" href="http://lab.kremalicious.com/kbdfun/">Demo</a>
<a class="icon-github btn" href="https://github.com/kremalicious/kbdfun/">Github</a>
</p>
## Usage

View File

@ -30,7 +30,7 @@ The full size I designed the wallpaper in is 3200x2048px. I don't know why Apple
Download the whole package with all the sizes included or grab the individual ones from the list, all linked to the image files:
<p class="content-download">
<a class="icon-download" href="../media/project-purple-kremalicious.zip">Download <span> zip</span></a>
<a class="icon-download btn btn-primary" href="../media/project-purple-kremalicious.zip">Download <span> zip</span></a>
</p>
- [Desktop/rMBP/iPad 3 (3200x2048)](../media/project-purple-kremalicious.png)

View File

@ -15,9 +15,9 @@ tags:
The badges provided by all app store providers just don't play well together with their varying typography and different sizing. So let's make them all visually unified, infinitely scalable, with pure text for easier localization and some web interaction styles. And while were at it: different sizes with the same markup by using some modifier classes.
<p class="content-download">
<a class="btn-primary icon-compass" href="https://lab.kremalicious.com/appstorebadges/">Demo</a>
<a class="icon-github" href="https://github.com/kremalicious/appstorebadges/">GitHub</a>
<a class="icon-code" href="http://codepen.io/kremalicious/details/EVVraP/">Codepen</a>
<a class="btn btn-primary icon-compass" href="https://lab.kremalicious.com/appstorebadges/">Demo</a>
<a class="icon-github btn" href="https://github.com/kremalicious/appstorebadges/">GitHub</a>
<a class="icon-code btn" href="http://codepen.io/kremalicious/details/EVVraP/">Codepen</a>
</p>
## Styling

View File

@ -41,5 +41,5 @@ hpm install hyper-mac-pro
Head over to GitHub to take a peek into the code or report some issues.
<p class="content-download">
<a class="icon-github btn-primary" href="https://github.com/kremalicious/hyper-mac-pro">GitHub</a>
<a class="icon-github btn btn-primary" href="https://github.com/kremalicious/hyper-mac-pro">GitHub</a>
</p>

View File

@ -59,5 +59,5 @@ plugins: [
Head over to GitHub for more documentation, take a peek into the code, or to report some bugs.
<p class="content-download">
<a class="icon-github btn-primary" href="https://github.com/kremalicious/gatsby-plugin-matomo">GitHub</a>
<a class="icon-github btn btn-primary" href="https://github.com/kremalicious/gatsby-plugin-matomo">GitHub</a>
</p>

View File

@ -110,5 +110,5 @@ plugins: [
Head over to GitHub for more documentation, take a peek into the code, or to report some bugs.
<p class="content-download">
<a class="icon-github btn-primary" href="https://github.com/kremalicious/gatsby-redirect-from">GitHub</a>
<a class="icon-github btn btn-primary" href="https://github.com/kremalicious/gatsby-redirect-from">GitHub</a>
</p>

View File

@ -1,4 +1,4 @@
import './src/styles/global.scss'
import './src/global/global.css'
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
export const wrapPageElement = wrapPageElementWithLayout

View File

@ -23,6 +23,7 @@ module.exports = {
},
plugins: [
...sources,
'gatsby-plugin-image',
{
resolve: 'gatsby-plugin-sharp',
options: {
@ -77,7 +78,7 @@ module.exports = {
},
injectStyles: false,
extensions: [
`${__dirname}/vendor/nord-visual-studio-code-0.15.0.vsix`,
'nord-visual-studio-code',
`${__dirname}/vendor/polar-0.0.6.vsix`
],
languageAliases: {}
@ -86,14 +87,6 @@ module.exports = {
]
}
},
{
resolve: 'gatsby-plugin-sass',
options: {
sassOptions: {
includePaths: [`${__dirname}/node_modules`, `${__dirname}/src/styles`]
}
}
},
{
resolve: 'gatsby-plugin-svgr',
options: {
@ -227,13 +220,6 @@ module.exports = {
exclude: ['/archive', '/archive/**/*', '/thanks', '/tags']
}
},
{
resolve: 'gatsby-plugin-use-dark-mode',
options: {
...siteConfig.darkModeConfig,
minify: true
}
},
'gatsby-plugin-react-helmet',
'gatsby-plugin-catch-links',
'gatsby-redirect-from',

View File

@ -64,7 +64,7 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
}
archive: allMarkdownRemark(
filter: { fields: { type: { ne: "photo" } } }
filter: { fields: { type: { nin: "photo" } } }
) {
edges {
node {
@ -95,15 +95,15 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
// Generate post pages
generatePostPages(createPage, all)
// Generate archive pages
generateArchivePages(createPage, archiveLength)
// Generate photos archive pages
generatePhotosPages(createPage, photosLength)
// Generate tag pages
generateTagPages(createPage, tags)
// Generate archive pages
generateArchivePages(createPage, archiveLength)
// Create manual redirects
generateRedirectPages(createRedirect)
}

View File

@ -47,7 +47,7 @@ exports.generatePostPages = (createPage, posts) => {
})
}
function generateIndexPages(createPage, length, slug, template) {
function generateIndexPages(createPage, length, slug, template, tag) {
const numPages = Math.ceil(length / itemsPerPage)
Array.from({ length: numPages }).forEach((_, i) => {
@ -67,7 +67,8 @@ function generateIndexPages(createPage, length, slug, template) {
numPages: numPages,
currentPageNumber: i + 1,
prevPagePath,
nextPagePath
nextPagePath,
...(tag && { tag })
}
})
})
@ -90,7 +91,8 @@ exports.generateTagPages = (createPage, tags) => {
createPage,
totalCount,
`/archive/${tag}/`,
archiveTemplate
archiveTemplate,
tag
)
})
}

View File

@ -44,10 +44,10 @@ module.exports = [
url: 'https://api.github.com/graphql',
headers: {
Authorization: `bearer ${process.env.GATSBY_GITHUB_TOKEN}`
},
}
// Additional options to pass to node-fetch
fetchOptions: {},
refetchInterval: 300 // 5 min.
// fetchOptions: {},
// refetchInterval: 300 // 5 min.
}
}
]

20165
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,16 +8,15 @@
"main": "index.js",
"scripts": {
"start": "gatsby develop --host 0.0.0.0",
"build": "gatsby build && npm run copy",
"build": "gatsby build",
"ssr": "npm run build && serve -s public/",
"test": "npm run lint && jest -c jest/jest.config.js --coverage --silent",
"test:watch": "npm run lint && jest -c jest/jest.config.js --coverage --watch",
"copy": "cp -R content/media/ public",
"lint": "run-p --continue-on-error lint:js lint:css lint:md",
"lint:js": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.tsx .",
"lint:css": "stylelint 'src/**/*.{css,scss}'",
"lint:css": "stylelint 'src/**/*.css'",
"lint:md": "markdownlint './**/*.{md,markdown}' --ignore './{node_modules,public,.cache,.git,coverage}/**/*'",
"format": "prettier --ignore-path .gitignore --write '**/*.{js,jsx,ts,tsx,md,json,css,scss}'",
"format": "prettier --ignore-path .gitignore --write '**/*.{js,jsx,ts,tsx,md,json,css}'",
"tsc": "tsc --noEmit",
"deploy": "./scripts/deploy-s3.sh",
"new": "ts-node ./scripts/new.ts"
@ -29,106 +28,104 @@
"not op_mini all"
],
"dependencies": {
"@ethersproject/providers": "^5.0.23",
"@ethersproject/units": "^5.0.10",
"@ethersproject/providers": "^5.0.24",
"@ethersproject/units": "^5.0.11",
"@loadable/component": "^5.14.1",
"@web3-react/core": "^6.1.9",
"@web3-react/injected-connector": "^6.0.7",
"classnames": "^2.2.6",
"date-fns": "^2.17.0",
"date-fns": "^2.19.0",
"dms2dec": "^1.1.0",
"ethereum-blockies": "github:MyEtherWallet/blockies",
"fast-exif": "^1.0.1",
"feather-icons": "^4.28.0",
"fraction.js": "^4.0.13",
"gatsby": "^2.32.8",
"gatsby-image": "^2.11.0",
"gatsby-plugin-catch-links": "^2.10.0",
"gatsby-plugin-feed": "^2.13.1",
"gatsby": "^3.1.1",
"gatsby-plugin-catch-links": "^3.1.0",
"gatsby-plugin-feed": "^3.1.0",
"gatsby-plugin-image": "^1.1.1",
"gatsby-plugin-lunr": "^1.5.2",
"gatsby-plugin-manifest": "^2.12.0",
"gatsby-plugin-manifest": "^3.1.0",
"gatsby-plugin-matomo": "^0.9.0",
"gatsby-plugin-meta-redirect": "^1.1.1",
"gatsby-plugin-offline": "^3.10.2",
"gatsby-plugin-react-helmet": "^3.10.0",
"gatsby-plugin-sass": "^3.2.0",
"gatsby-plugin-sharp": "^2.14.3",
"gatsby-plugin-sitemap": "^2.12.0",
"gatsby-plugin-svgr": "^2.1.0",
"gatsby-plugin-use-dark-mode": "^1.2.0",
"gatsby-plugin-webpack-size": "^1.0.0",
"gatsby-plugin-offline": "^4.1.0",
"gatsby-plugin-react-helmet": "^4.1.0",
"gatsby-plugin-sharp": "^3.1.1",
"gatsby-plugin-sitemap": "^3.1.0",
"gatsby-plugin-svgr": "^3.0.0-beta.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": "^2.11.0",
"gatsby-remark-autolink-headers": "^3.1.0",
"gatsby-remark-breaks": "^1.0.0",
"gatsby-remark-copy-linked-files": "^2.10.0",
"gatsby-remark-images": "^3.11.1",
"gatsby-remark-copy-linked-files": "^3.1.0",
"gatsby-remark-images": "^4.1.0",
"gatsby-remark-images-medium-zoom": "^1.7.0",
"gatsby-remark-smartypants": "^2.10.0",
"gatsby-remark-vscode": "^3.2.0",
"gatsby-source-filesystem": "^2.11.1",
"gatsby-source-graphql": "^2.14.0",
"gatsby-transformer-remark": "^2.16.1",
"gatsby-transformer-sharp": "^2.12.0",
"gatsby-remark-smartypants": "^3.1.0",
"gatsby-remark-vscode": "^3.2.1",
"gatsby-source-filesystem": "^3.1.0",
"gatsby-source-graphql": "^3.1.0",
"gatsby-transformer-remark": "^3.1.0",
"gatsby-transformer-sharp": "^3.1.0",
"node-fetch": "^2.6.1",
"pigeon-maps": "^0.17.1",
"nord-visual-studio-code": "github:arcticicestudio/nord-visual-studio-code",
"pigeon-maps": "^0.19.5",
"pigeon-marker": "^0.3.4",
"react": "^16.14.0",
"react": "^17.0.1",
"react-clipboard.js": "^2.0.16",
"react-dom": "^16.14.0",
"react-dom": "^17.0.1",
"react-feather": "^2.0.9",
"react-helmet": "^6.1.0",
"react-qr-svg": "^2.3.0",
"react-transition-group": "^4.4.1",
"remark": "^13.0.0",
"remark-react": "^8.0.0",
"slugify": "^1.4.7",
"use-dark-mode": "^2.3.1"
"slugify": "^1.5.0"
},
"devDependencies": {
"@svgr/webpack": "^5.5.0",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@types/classnames": "^2.2.11",
"@types/fs-extra": "^9.0.7",
"@types/jest": "^26.0.20",
"@types/fs-extra": "^9.0.8",
"@types/jest": "^26.0.21",
"@types/loadable__component": "^5.13.3",
"@types/lunr": "^2.3.3",
"@types/node": "^14.14.28",
"@types/node": "^14.14.35",
"@types/node-fetch": "^2.5.8",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"@types/react-helmet": "^6.1.0",
"@types/react-transition-group": "^4.4.0",
"@types/shortid": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^4.15.2",
"@typescript-eslint/parser": "^4.15.2",
"@welldone-software/why-did-you-render": "^6.0.5",
"eslint": "^7.21.0",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@welldone-software/why-did-you-render": "^6.1.1",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-graphql": "^4.0.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-testing-library": "^3.10.1",
"eslint-plugin-testing-library": "^3.10.2",
"fs-extra": "^9.1.0",
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.20",
"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",
"node-sass": "^5.0.0",
"npm-run-all": "^4.1.5",
"ora": "^5.3.0",
"ora": "^5.4.0",
"postcss": "^8.2.8",
"prettier": "^2.2.1",
"shortid": "^2.2.16",
"stylelint": "^13.11.0",
"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.2"
"typescript": "^4.2.3",
"typescript-plugin-css-modules": "^3.2.0"
},
"repository": {
"type": "git",

15
src/@types/Image.d.ts vendored
View File

@ -1,17 +1,14 @@
import { FixedObject, FluidObject } from 'gatsby-image'
import { GatsbyImageProps, IGatsbyImageData } from 'gatsby-plugin-image'
export interface ImageProps {
export interface ImageProps extends GatsbyImageProps {
title?: string
fluid?: FluidObject
fixed?: FixedObject
alt?: string
original?: { src: string }
className?: string
}
export interface ImageNode {
childImageSharp: ImageProps
fields: {
exif: Exif
export interface ImageNode extends IGatsbyImageData {
fields?: {
exif?: Exif
}
}

4
src/@types/css.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: { [key: string]: string }
export default classes
}

View File

@ -1,13 +1,3 @@
declare module '*.module.css' {
const classes: { [key: string]: string }
export default classes
}
declare module '*.module.scss' {
const classes: { [key: string]: string }
export default classes
}
declare module '*.svg' {
import * as React from 'react'
export const ReactComponent: React.FunctionComponent<

View File

@ -0,0 +1,75 @@
.content {
padding: 0 calc(var(--spacer) / 1.5);
width: 100%;
max-width: var(--maxWidthContainer);
margin-left: auto;
margin-right: auto;
}
/* topbar and footer as fixed
site background
///////////////////////////////////// */
.document {
width: 100%;
padding-top: var(--spacer);
background-color: var(--body-background-color);
padding-bottom: calc(var(--spacer) * 2);
transform: translate3d(0, 0, 0);
transition: 0.4s var(--easing);
transition-property: transform, background, border, box-shadow;
border-top: 1px solid rgba(255, 255, 255, 0.85);
box-shadow: 0 1px 10px rgba(1, 85, 101, 0.1),
0 -1px 4px rgba(1, 85, 101, 0.05);
}
:global(.has-menu-open) .document {
transform: translate3d(0, calc(var(--spacer) * 2), 0);
}
:global(.dark) .document {
border-top-color: rgba(255, 255, 255, 0.05);
box-shadow: 0 1px 8px rgba(0, 7, 8, 0.3), 0 -1px 4px rgba(0, 21, 25, 0.8);
}
@media (min-width: 60rem) {
.document {
padding-top: calc(var(--spacer) * 2);
}
}
@media (min-width: 40rem) and (min-height: 500px) {
.document {
margin-top: calc(var(--spacer) * 2.5);
/* height of footer */
margin-bottom: calc(var(--spacer) * 12);
position: relative;
z-index: 2;
min-height: 500px;
}
}
.container {
width: 100%;
max-width: var(--maxWidthContent);
margin-left: auto;
margin-right: auto;
}
.wide {
composes: container;
max-width: none;
}
.breakout {
margin-left: calc(-50vw + 50%);
margin-right: calc(-50vw + 50%);
}
@media (min-width: 1000px) {
.breakout {
margin-left: -8rem;
margin-right: -8rem;
}
}

View File

@ -1,61 +0,0 @@
@import 'variables';
@import 'mixins';
#___gatsby {
// display: flex;
// min-height: 100vh;
// flex-direction: column;
position: relative;
}
.content {
padding: 0 $spacer / $line-height;
width: 100%;
@media (min-width: $screen-sm) {
padding: 0 ($spacer * 2);
}
}
// topbar and footer as fixed
// site background
/////////////////////////////////////
.document {
width: 100%;
padding-top: $spacer;
background-color: $body-background-color;
border-top: 1px solid rgba(255, 255, 255, 0.7);
border-bottom: 1px solid rgba(255, 255, 255, 0.7);
padding-bottom: $spacer * 2;
box-shadow: 0 1px 4px rgba($brand-main, 0.1),
0 -1px 4px rgba($brand-main, 0.2);
transform: translate3d(0, 0, 0);
transition: 0.4s $easing;
transition-property: transform, background;
:global(.has-menu-open) & {
transform: translate3d(0, ($spacer * 3.5), 0);
}
:global(.dark) & {
background-color: $body-background-color--dark;
color: $text-color--dark;
border-top-color: darken($brand-grey, 15%);
border-bottom-color: darken($body-background-color--dark, 3%);
box-shadow: 0 1px 8px rgba(darken($brand-main, 15%), 0.1),
0 -1px 4px darken($brand-main, 15%);
}
@media (min-width: $screen-md) {
padding-top: $spacer * 2;
}
@media (min-width: $screen-sm) and (min-height: 500px) {
margin-top: $spacer * 2.65;
margin-bottom: $spacer * 18; // height of footer
position: relative;
z-index: 2;
min-height: 500px;
}
}

View File

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

View File

@ -0,0 +1,88 @@
.title {
margin-top: calc(var(--spacer) * 3);
margin-bottom: 0;
}
.content {
padding-top: var(--spacer);
padding-left: calc(var(--spacer) / 2);
margin-left: calc(var(--spacer) / 2);
border-left: var(--stroke-width) solid var(--border-color);
}
.content h2,
.content h3,
.content h4 {
position: relative;
margin-bottom: calc(var(--spacer) / 4);
}
.content h2::before,
.content h3::before,
.content h4::before {
content: '';
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
display: inline-block;
background: var(--color-headings);
position: absolute;
left: -1.275rem;
top: calc(var(--font-size-large) / 3);
}
.content h2 + blockquote,
.content h3 + blockquote,
.content h4 + blockquote {
padding-left: 0;
font-size: var(--font-size-small);
}
.content h2 + blockquote::before,
.content h3 + blockquote::before,
.content h4 + blockquote::before {
display: none;
}
.content h2,
.content h3 {
font-size: var(--font-size-large);
background: none;
padding: 0;
margin-left: 0;
margin-top: calc(var(--spacer) / 8);
margin-bottom: calc(var(--spacer) / var(--line-height));
}
.content ul {
font-size: var(--font-size-small);
margin-left: calc(var(--spacer) / 8);
}
.content ul li {
margin-bottom: calc(var(--spacer) / 8);
}
.source {
font-size: var(--font-size-mini);
font-family: var(--font-family-base);
font-weight: var(--font-weight-base);
padding-bottom: calc(var(--spacer) / 2);
}
.source,
.source a {
color: var(--text-color-light);
}
.source a {
margin-left: calc(var(--spacer) / 8);
}
.source code {
font-size: calc(var(--font-size-mini) * 0.9);
}
.source a:hover {
color: var(--link-color);
}

View File

@ -1,92 +0,0 @@
@import 'variables';
.title {
margin-top: $spacer * 3;
margin-bottom: 0;
}
.content {
padding-top: $spacer;
padding-left: $spacer / 2;
margin-left: $spacer / 2;
border-left: 1px solid $brand-grey-dimmed;
:global(.dark) & {
border-left-color: rgba($color-headings--dark, 0.2);
}
h2,
h3,
h4 {
position: relative;
margin-bottom: $spacer / 4;
&::before {
content: '';
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
display: inline-block;
background: $color-headings;
position: absolute;
left: -($spacer / 1.5);
top: $font-size-large / 3;
:global(.dark) & {
background: $color-headings--dark;
}
}
+ blockquote {
padding-left: 0;
font-size: $font-size-small;
&::before {
display: none;
}
}
}
h2,
h3 {
font-size: $font-size-large;
background: none;
padding: 0;
margin-left: 0;
margin-top: $spacer / 8;
margin-bottom: $spacer / $line-height;
}
ul {
font-size: $font-size-small;
margin-left: $spacer / 8;
li {
margin-bottom: $spacer / 8;
}
}
}
.source {
font-size: $font-size-mini;
font-family: $font-family-base;
font-weight: $font-weight-base;
padding-bottom: $spacer / 2;
&,
a {
color: $brand-grey-light;
}
a {
margin-left: $spacer / 8;
code {
font-size: ($font-size-mini * 0.9);
}
&:hover {
color: $link-color;
}
}
}

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 styles from './Changelog.module.scss'
import { title, content, source } from './Changelog.module.css'
import { GitHub, GitHubRepo } from '../../@types/GitHub'
export function PureChangelog({
@ -30,22 +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}>
<p className={styles.source}>
<em>
sourced from{' '}
<a href={filePathUrl}>
<code>{filePathDisplay}</code>
</a>
</em>
</p>
<div className={content}>
{changelogHtml}
<p className={source}>
sourced from{' '}
<a href={filePathUrl}>
<code>{filePathDisplay}</code>
</a>
</p>
</div>
</div>
</>
)
}

View File

@ -1,5 +0,0 @@
.container {
max-width: 37rem;
margin-left: auto;
margin-right: auto;
}

View File

@ -1,10 +0,0 @@
import React, { ReactElement } from 'react'
import styles from './Container.module.scss'
export default function Container({
children
}: {
children: any
}): ReactElement {
return <section className={styles.container}>{children}</section>
}

View File

@ -0,0 +1,30 @@
.button {
margin: 0;
position: absolute;
right: 0;
top: 0;
bottom: 0;
border: 0;
box-shadow: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
background: rgba(var(--brand-grey), 0.3);
padding: calc(var(--spacer) / 3);
}
.button svg {
stroke: var(--text-color-light);
transition: 0.15s ease-out;
}
.copied {
background: green;
}
.copied svg {
stroke: var(--text-color-dimmed);
}
.button:hover svg {
stroke: var(--text-color-dimmed);
}

View File

@ -1,35 +0,0 @@
@import 'variables';
.button {
margin: 0;
position: absolute;
right: 0;
top: 0;
bottom: 0;
border: 0;
box-shadow: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
background: rgba($brand-grey, 0.3);
padding: $spacer / 3;
svg {
stroke: $brand-grey-light;
transition: 0.15s ease-out;
}
&:hover {
svg {
stroke: $brand-grey-dimmed;
}
}
}
.copied {
background: green;
// stylelint-disable-next-line no-descending-specificity
svg {
stroke: $brand-grey-dimmed;
}
}

View File

@ -1,12 +1,12 @@
import React, { ReactElement } from 'react'
import loadable from '@loadable/component'
import styles from './Copy.module.scss'
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

@ -0,0 +1,18 @@
.divider {
position: relative;
border-bottom: 1px dashed var(--border-color);
}
.divider::before {
content: '';
position: absolute;
left: 0;
height: 1px;
bottom: -2px;
width: 100%;
border-bottom: 1px dashed #fff;
}
:global(.dark) .divider::before {
border-bottom-color: var(--brand-grey);
}

View File

@ -0,0 +1,69 @@
.exif {
margin-top: -3rem;
margin-bottom: calc(var(--spacer) * 2);
}
.data {
font-size: var(--font-size-mini);
color: var(--text-color-light);
display: flex;
flex-wrap: wrap;
justify-content: center;
text-align: center;
}
.data span {
display: block;
flex: 1 1 auto;
white-space: nowrap;
padding: var(--spacer) calc(var(--spacer) / 1.5);
border-bottom: 1px dashed var(--border-color);
}
.data span svg {
display: block;
margin-bottom: calc(var(--spacer) / 8);
opacity: 0.65;
}
.data span:first-child {
flex-basis: 100%;
}
@media (min-width: 40rem) {
.data {
margin-bottom: 0;
font-size: var(--font-size-small);
}
.data span {
border-left: 1px dashed var(--border-color);
border-bottom: 0;
}
.data span:first-child {
flex-basis: auto;
border-left: 0;
}
}
.map {
composes: breakout from '../Layout.module.css';
composes: frame from './Image.module.css';
height: 180px;
margin-top: calc(var(--spacer) * 2);
}
@media (min-width: 40rem) {
.map {
margin-top: 0;
}
}
.map img {
border-radius: 0 !important;
}
.map > div:first-child {
background: none !important;
}

View File

@ -1,84 +0,0 @@
@import 'variables';
@import 'mixins';
.exif {
margin-top: -($spacer * $line-height);
margin-bottom: $spacer * 2;
}
.data {
@include breakoutviewport;
font-size: $font-size-mini;
color: $brand-grey-light;
display: flex;
flex-wrap: wrap;
justify-content: center;
text-align: center;
span {
display: block;
flex: 1 1 20%;
white-space: nowrap;
padding: $spacer;
border-bottom: 1px dashed $brand-grey-dimmed;
svg {
display: block;
margin-bottom: $spacer / 8;
opacity: 0.65;
}
&:first-child {
flex-basis: 100%;
}
:global(.dark) & {
border-bottom-color: rgba($brand-grey, 0.6);
}
}
@media (min-width: $screen-sm) {
margin-bottom: 0;
font-size: $font-size-small;
span {
border-left: 1px dashed $brand-grey-dimmed;
border-bottom: 0;
&,
&:first-child {
flex: 1 1 auto;
}
&:first-child {
border-left: 0;
}
:global(.dark) & {
border-left-color: rgba($brand-grey, 0.6);
}
}
}
}
.map {
@include breakoutviewport;
@include media-frame;
overflow: hidden;
height: 220px;
margin-top: $spacer * 2;
@media (min-width: $screen-sm) {
margin-top: 0;
}
img {
border-radius: 0 !important;
}
> div:first-child {
background: none !important;
}
}

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import ExifMap from './ExifMap'
import styles from './Exif.module.scss'
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,7 +1,7 @@
import React, { ReactElement, useState } from 'react'
import Map from 'pigeon-maps'
import { Map } from 'pigeon-maps'
import Marker from 'pigeon-marker'
import useDarkMode from 'use-dark-mode'
import useDarkMode from '../../hooks/useDarkMode'
const mapbox = (mapboxId: string) => (
x: string,
@ -23,10 +23,7 @@ export default function ExifMap({
}: {
gps: { latitude: string; longitude: string }
}): ReactElement {
const { value } = useDarkMode(false, {
classNameDark: 'dark',
classNameLight: 'light'
})
const { value } = useDarkMode()
const isDarkMode = value
const [zoom, setZoom] = useState(12)
@ -40,7 +37,7 @@ export default function ExifMap({
<Map
center={[latitude, longitude]}
zoom={zoom}
height={220}
height={180}
dprs={[1, 2]}
attribution={false}
provider={isDarkMode ? providers['dark'] : providers['light']}

View File

@ -0,0 +1,70 @@
.hamburger {
width: 24px;
height: 24px;
display: inline-block;
position: relative;
transform: rotate(0deg);
cursor: pointer;
margin-top: calc(var(--spacer) / 2);
}
.line {
display: block;
position: absolute;
height: 1px;
width: 100%;
border-bottom: var(--stroke-width) solid var(--text-color-light);
opacity: 1;
left: 0;
transform: rotate(0deg);
transition: 0.2s var(--easing);
}
.line:nth-child(1) {
top: 0;
transform-origin: left center;
}
.line:nth-child(2) {
top: 7px;
transform-origin: left center;
}
.line:nth-child(3) {
top: 14px;
transform-origin: left center;
}
/* open state */
:global(.has-menu-open) .line:nth-child(1) {
transform: rotate(45deg);
top: -1px;
}
:global(.has-menu-open) .line:nth-child(2) {
width: 0%;
opacity: 0;
}
:global(.has-menu-open) .line:nth-child(3) {
transform: rotate(-45deg);
top: 16px;
}
.button {
padding: calc(var(--spacer) / 2);
vertical-align: middle;
display: inline-block;
margin: 0;
margin-right: -1rem;
}
.button:hover,
.button:focus {
outline: 0;
}
.button:hover .line,
.button:focus .line {
border-color: var(--link-color);
}

View File

@ -1,75 +0,0 @@
@import 'variables';
@import 'mixins';
.hamburger {
width: 24px;
height: 24px;
display: inline-block;
position: relative;
transform: rotate(0deg);
cursor: pointer;
margin-top: $spacer / 2;
}
.hamburgerLine {
@include transition;
display: block;
position: absolute;
height: 1px;
width: 100%;
border-bottom: $stroke-width solid $brand-grey-light;
opacity: 1;
left: 0;
transform: rotate(0deg);
&:nth-child(1) {
top: 0;
transform-origin: left center;
}
&:nth-child(2) {
top: 7px;
transform-origin: left center;
}
&:nth-child(3) {
top: 14px;
transform-origin: left center;
}
// open state
:global(.has-menu-open) & {
&:nth-child(1) {
transform: rotate(45deg);
top: -1px;
}
&:nth-child(2) {
width: 0%;
opacity: 0;
}
&:nth-child(3) {
transform: rotate(-45deg);
top: 16px;
}
}
}
.hamburgerButton {
padding: $spacer / 2;
vertical-align: middle;
display: inline-block;
margin: 0;
margin-right: -($spacer / 2);
&:hover,
&:focus {
outline: 0;
.hamburgerLine {
border-color: $brand-cyan;
}
}
}

View File

@ -1,5 +1,5 @@
import React, { ReactElement } from 'react'
import styles from './Hamburger.module.scss'
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.hamburgerButton}
onClick={onClick}
>
<span className={styles.hamburger}>
<span className={styles.hamburgerLine} />
<span className={styles.hamburgerLine} />
<span className={styles.hamburgerLine} />
<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

@ -1,10 +1,8 @@
@import 'variables';
.icon {
width: 1em;
height: 1em;
stroke: currentColor;
stroke-width: $stroke-width;
stroke-width: var(--stroke-width);
stroke-linecap: round;
stroke-linejoin: round;
fill: none;

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 styles from './Icon.module.scss'
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

@ -0,0 +1,53 @@
.frame {
border: var(--stroke-width) solid rgba(255, 255, 255, 0.2);
border-radius: var(--border-radius);
overflow: hidden;
transition: 0.2s ease-out;
box-shadow: var(--box-shadow);
}
a:hover .frame,
a:focus .frame {
border-color: var(--link-color);
}
.image {
composes: frame;
max-width: none;
margin-top: calc(var(--spacer) * var(--line-height));
margin-bottom: calc(var(--spacer) * var(--line-height));
position: relative;
display: block;
}
/* single photo post teasers */
.image:only-child {
margin-top: 0;
margin-bottom: 0;
}
.imageTitle {
font-size: var(--font-size-h3);
font-family: var(--font-family-headings);
line-height: var(--line-height-headings);
font-weight: var(--font-weight-headings);
font-style: normal;
text-align: left;
margin: 0;
position: absolute;
top: 10%;
padding: calc(var(--spacer) / 3) var(--spacer);
background: var(--link-color);
color: #fff !important;
text-shadow: 0 1px 0 #000;
left: 0;
opacity: 0;
transform: translate3d(0, -20px, 0);
transition: 0.2s var(--easing);
}
a:hover .imageTitle,
a:focus .imageTitle {
opacity: 1;
transform: translate3d(0, 0, 0);
}

View File

@ -1,54 +0,0 @@
@import 'variables';
@import 'mixins';
.imageTitle {
@include transition();
font-size: $font-size-h3;
font-family: $font-family-headings;
line-height: $line-height-headings;
font-weight: $font-weight-headings;
font-style: normal;
text-align: left;
margin: 0;
position: absolute;
top: 10%;
padding: $spacer / 3 $spacer;
background: rgba($link-color, 0.85);
color: #fff !important;
text-shadow: 0 1px 0 #000;
left: 0;
opacity: 0;
transform: translate3d(0, -20px, 0);
}
.image {
@include media-frame;
@include breakoutviewport;
max-width: none;
margin-top: $spacer * $line-height;
margin-bottom: $spacer * $line-height;
display: block;
a & {
position: relative;
display: block;
}
// single photo post teasers
&:only-child {
margin-top: 0;
margin-bottom: 0;
}
a:hover &,
a:focus & {
border-color: $link-color !important;
.imageTitle {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
}

View File

@ -1,19 +1,22 @@
import React, { ReactElement } from 'react'
import { graphql } from 'gatsby'
import Img from 'gatsby-image'
import { GatsbyImage } from 'gatsby-plugin-image'
import { ImageProps } from '../../@types/Image'
import styles from './Image.module.scss'
import { image as styleImage, imageTitle } from './Image.module.css'
export const Image = ({
title,
fluid,
fixed,
image,
alt,
original
original,
className
}: ImageProps): ReactElement => (
<figure className={styles.image} data-original={original && original.src}>
<Img backgroundColor="transparent" fluid={fluid} fixed={fixed} alt={alt} />
{title && <figcaption className={styles.imageTitle}>{title}</figcaption>}
<figure
className={`${styleImage} ${className ? className : ''}`}
data-original={original?.src}
>
<GatsbyImage image={image} alt={alt} />
{title && <figcaption className={imageTitle}>{title}</figcaption>}
</figure>
)
@ -22,9 +25,7 @@ export const imageSizeDefault = graphql`
original {
src
}
fluid(maxWidth: 940, quality: 85) {
...GatsbyImageSharpFluid_withWebp_noBase64
}
gatsbyImageData(layout: CONSTRAINED, width: 1040, quality: 85)
}
`
@ -33,9 +34,13 @@ export const imageSizeThumb = graphql`
original {
src
}
fluid(maxWidth: 420, maxHeight: 140, quality: 85, cropFocus: CENTER) {
...GatsbyImageSharpFluid_withWebp_noBase64
}
gatsbyImageData(
layout: CONSTRAINED
width: 480
height: 180
quality: 85
transformOptions: { cropFocus: CENTER }
)
}
`
@ -44,8 +49,12 @@ export const photoSizeThumb = graphql`
original {
src
}
fluid(maxWidth: 300, maxHeight: 300, quality: 85, cropFocus: CENTER) {
...GatsbyImageSharpFluid_withWebp_noBase64
}
gatsbyImageData(
layout: CONSTRAINED
width: 316
height: 316
quality: 85
transformOptions: { cropFocus: CENTER }
)
}
`

View File

@ -0,0 +1,32 @@
.input {
display: block;
width: 100%;
padding: var(--padding-base-vertical) var(--padding-base-horizontal);
font-size: var(--input-font-size);
font-weight: var(--input-font-weight);
line-height: var(--line-height);
color: var(--input-color);
background-color: var(--input-bg);
background-image: none;
border: var(--stroke-width) solid transparent;
border-radius: var(--border-radius);
box-shadow: none;
transition: all ease-in-out 0.15s;
appearance: none;
}
.input::-moz-placeholder,
.input::-webkit-input-placeholder,
.input:-ms-input-placeholder {
color: var(--input-color-placeholder);
opacity: 1;
}
.input:focus {
border-color: var(--input-border-focus);
outline: 0;
}
.input[disabled] {
color: var(--text-color-dimmed);
}

View File

@ -1,36 +0,0 @@
@import 'variables';
@import 'mixins';
.input {
display: block;
width: 100%;
padding: $padding-base-vertical $padding-base-horizontal;
font-size: $input-font-size;
font-weight: $input-font-weight;
line-height: $line-height;
color: $input-color;
background-color: $input-bg;
background-image: none; // Reset unusual Firefox-on-Android default style
border: 0;
border-radius: $input-border-radius;
box-shadow: none;
transition: all ease-in-out 0.15s;
appearance: none;
@include placeholder();
:global(.dark) & {
color: $input-color--dark;
background-color: $input-bg--dark;
@include placeholder($input-color-placeholder--dark);
}
&:focus {
border-color: $input-border-focus;
outline: 0;
}
&[disabled] {
color: $brand-grey-dimmed;
}
}

View File

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

View File

@ -0,0 +1,17 @@
.qr {
margin-bottom: calc(var(--spacer) / 2);
}
.code {
margin: 0 auto;
position: relative;
padding: 0;
padding-right: 2rem;
width: fit-content;
}
.code code {
padding: calc(var(--spacer) / 2);
font-size: 0.65rem;
text-align: center;
}

View File

@ -1,19 +0,0 @@
@import 'variables';
.qr {
margin-bottom: $spacer / 2;
}
.code {
margin: 0 auto;
position: relative;
padding: 0;
padding-right: 2rem;
width: fit-content;
code {
padding: $spacer / 2;
font-size: 0.65rem;
text-align: center;
}
}

View File

@ -1,5 +1,5 @@
import React, { Suspense } from 'react'
import { render, fireEvent, waitForElement } from '@testing-library/react'
import { render, fireEvent, waitFor } from '@testing-library/react'
import Qr from './Qr'
@ -10,10 +10,8 @@ describe('Qr', () => {
<Qr address="xxx" />
</Suspense>
)
const lazyElement = await waitForElement(() =>
container.querySelector('button')
)
expect(lazyElement).toBeInTheDocument()
expect(container.firstChild).toBeInTheDocument()
await waitFor(() => container.querySelector('button'))
fireEvent.click(container.querySelector('button'))
})
})

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import { QRCode } from 'react-qr-svg'
import styles from './Qr.module.scss'
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 { graphql, useStaticQuery } from 'gatsby'
import { getSrc } from 'gatsby-plugin-image'
import { useSiteMetadata } from '../../../hooks/use-site-metadata'
import { Post } from '../../../@types/Post'
import MetaTags from './MetaTags'
@ -39,9 +39,7 @@ export default function SEO({
const postMeta = post.frontmatter
title = `${postMeta.title} ¦ ${siteTitle}`
description = postMeta.description ? postMeta.description : post.excerpt
image = postMeta.image
? postMeta.image.childImageSharp.fluid.src
: `/${logo}`
image = postMeta.image ? getSrc(postMeta.image) : `/${logo}`
postURL = `${siteUrl}${slug}`
} else {
title = `${siteTitle} ¦ ${siteDescription}`

View File

@ -0,0 +1,20 @@
.tag {
color: var(--text-color-light);
margin-left: calc(var(--spacer) / 2);
margin-right: calc(var(--spacer) / 2);
margin-bottom: calc(var(--spacer) / 2);
white-space: nowrap;
display: inline-block;
}
.tag::before {
content: '#';
margin-right: 1px;
opacity: 0.65;
}
.count {
font-size: var(--font-size-small);
margin-left: calc(var(--spacer) / 6);
opacity: 0.65;
}

View File

@ -1,22 +0,0 @@
@import 'variables';
.tag {
color: $brand-grey-light;
margin-left: $spacer / 2;
margin-right: $spacer / 2;
margin-bottom: $spacer / 2;
white-space: nowrap;
display: inline-block;
&::before {
content: '#';
margin-right: 1px;
opacity: 0.65;
}
}
.count {
font-size: $font-size-small;
margin-left: $spacer / 6;
opacity: 0.65;
}

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import styles from './Tag.module.scss'
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

@ -0,0 +1,45 @@
.menu {
margin-top: 0;
position: absolute;
top: 100%;
left: 0;
width: 100%;
}
.menu ul {
margin: 0;
padding: 0;
text-align: center;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
white-space: nowrap;
}
.menu li {
list-style: none;
display: inline-block;
}
.menu li::before {
display: none;
}
.menu a {
color: var(--text-color);
line-height: 1;
text-transform: uppercase;
margin: 0 calc(var(--spacer) / 4);
font-size: var(--font-size-small);
text-shadow: 0 1px 0 rgba(255 255 255 0.5);
padding: var(--padding-base-horizontal);
display: block;
text-align: center;
}
.menu::-webkit-scrollbar,
.menu::-moz-scrollbar,
.menu::-webkit-scrollbar-thumb,
.menu::-webkit-scrollbar-track {
display: none;
}

View File

@ -1,66 +0,0 @@
@import 'variables';
@import 'mixins';
.menu {
@include divider-top;
margin-top: 0;
padding-top: $spacer / 2;
position: absolute;
top: calc(100% + #{$spacer / 1.5});
left: 0;
width: 100%;
ul {
margin: 0;
padding: 0;
text-align: center;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
white-space: nowrap;
}
li {
list-style: none;
display: inline-block;
&::before {
display: none;
}
}
a {
color: $text-color;
line-height: 1;
text-transform: uppercase;
margin: 0 $spacer / 4;
font-size: $font-size-small;
text-shadow: 0 1px 0 rgba(#fff, 0.5);
padding: $padding-base-horizontal;
display: block;
text-align: center;
&:hover,
&:focus {
color: $link-color-hover;
}
:global(.dark) & {
color: $text-color--dark;
text-shadow: 0 -1px 0 rgba(#000, 0.5);
&:hover,
&:focus {
color: $link-color-hover;
}
}
}
&::-webkit-scrollbar,
&::-moz-scrollbar,
&::-webkit-scrollbar-thumb,
&::-webkit-scrollbar-track {
display: none;
}
}

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 styles from './Menu.module.scss'
import { menu as styleMenu } from './Menu.module.css'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import { MenuItem } from '../../@types/Site'
@ -10,17 +10,19 @@ export default function Menu(): ReactElement {
const [menuOpen, setMenuOpen] = useState(false)
const { menu } = useSiteMetadata()
const toggleMenu = () => {
function toggleMenu(): void {
setMenuOpen(!menuOpen)
}
const MenuItems = menu.map((item: MenuItem) => (
<li key={item.title}>
<Link onClick={toggleMenu} to={item.link}>
{item.title}
</Link>
</li>
))
const MenuItems = menu.map(
(item: MenuItem): JSX.Element => (
<li key={item.title}>
<Link onClick={toggleMenu} to={item.link}>
{item.title}
</Link>
</li>
)
)
return (
<>
@ -28,7 +30,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

@ -0,0 +1,25 @@
.link {
text-align: center;
width: 2rem;
padding: calc(var(--spacer) / 4);
margin-left: calc(var(--spacer) / 4);
margin-right: calc(var(--spacer) / 4);
display: inline-block;
color: var(--text-color-light);
}
.link:first-child {
margin-left: 0;
}
.link:last-child {
margin-right: 0;
}
.link svg {
width: 24px;
height: 24px;
transition: stroke 0.3s ease-in-out;
display: block;
margin: 0 auto;
}

View File

@ -1,27 +0,0 @@
@import 'variables';
.link {
text-align: center;
width: 2rem;
padding: $spacer / 4;
margin-left: $spacer / 4;
margin-right: $spacer / 4;
display: inline-block;
color: $text-color-light;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
svg {
width: 24px;
height: 24px;
transition: stroke 0.3s ease-in-out;
display: block;
margin: 0 auto;
}
}

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import Icon from '../atoms/Icon'
import styles from './Networks.module.scss'
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

@ -0,0 +1,41 @@
.pagination {
margin-top: calc(var(--spacer) * 3);
margin-bottom: var(--spacer);
display: flex;
justify-content: center;
}
.number {
text-align: center;
padding: calc(var(--spacer) / 6) calc(var(--spacer) / 2);
border: 1px solid var(--text-color-dimmed);
font-variant-numeric: lining-nums;
margin-left: -1px;
display: flex;
align-items: center;
justify-content: center;
min-width: 2.5rem;
color: var(--text-color);
}
.number:hover,
.number:focus {
background: rgba(255, 255, 255, 0.1);
}
.number:first-child {
border-top-left-radius: var(--border-radius);
border-bottom-left-radius: var(--border-radius);
}
.number:last-child {
border-top-right-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
.current {
composes: number;
cursor: default;
pointer-events: none;
color: var(--text-color-dimmed);
}

View File

@ -1,58 +0,0 @@
@import 'variables';
@import 'mixins';
.pagination {
@include breakoutviewport;
margin-top: $spacer * 3;
margin-bottom: $spacer;
display: flex;
justify-content: center;
}
.number {
text-align: center;
padding: $spacer / 6 $spacer / 2;
border: 1px solid $brand-grey-dimmed;
font-variant-numeric: lining-nums;
margin-left: -1px;
display: flex;
align-items: center;
justify-content: center;
min-width: 2.5rem;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
color: $brand-grey;
:global(.dark) & {
color: $brand-grey-dimmed;
border-color: darken($body-background-color--dark, 5%);
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1);
}
&:hover,
&:focus {
background: rgba(255, 255, 255, 0.1);
}
&:first-child {
border-top-left-radius: $border-radius;
border-bottom-left-radius: $border-radius;
}
&:last-child {
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
}
}
.current {
composes: number;
cursor: default;
pointer-events: none;
color: $brand-grey-dimmed;
background: rgba(255, 255, 255, 0.1);
:global(.dark) & {
color: $brand-grey-light;
}
}

View File

@ -1,11 +1,15 @@
import React, { ReactElement } from 'react'
import { Link } from 'gatsby'
import styles from './Pagination.module.scss'
import shortid from 'shortid'
import { PageContext } from '../../@types/Post'
import Icon from '../atoms/Icon'
import {
current as styleCurrent,
number as styleNumber,
pagination
} from './Pagination.module.css'
const PageNumber = ({
function PageNumber({
i,
slug,
current
@ -13,8 +17,8 @@ const PageNumber = ({
i: number
slug: string
current?: boolean
}) => {
const classes = current ? styles.current : styles.number
}): JSX.Element {
const classes = current ? styleCurrent : styleNumber
const link = i === 0 ? slug : `${slug}page/${i + 1}`
return (
@ -30,13 +34,13 @@ function PrevNext({
}: {
prevPagePath?: string
nextPagePath?: string
}) {
}): JSX.Element {
const link = prevPagePath || nextPagePath
const rel = prevPagePath ? 'prev' : 'next'
const title = prevPagePath ? 'Newer Posts' : 'Older Posts'
return (
<Link to={link} rel={rel} title={title} className={styles.number}>
<Link to={link} rel={rel} title={title} className={styleNumber}>
{prevPagePath ? (
<Icon name="ChevronLeft" />
) : (
@ -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

@ -0,0 +1,6 @@
.time {
font-style: italic;
font-size: var(--font-size-small);
color: var(--text-color-light);
margin-bottom: calc(var(--spacer) / var(--line-height));
}

View File

@ -1,8 +0,0 @@
@import 'variables';
.time {
font-style: italic;
font-size: $font-size-small;
color: $text-color-light;
margin-bottom: $spacer / $line-height;
}

View File

@ -1,6 +1,6 @@
import React, { ReactElement } from 'react'
import Time from '../atoms/Time'
import styles from './PostDate.module.scss'
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,40 +1,40 @@
@import 'variables';
@import 'mixins';
.title {
padding-left: 0.2rem;
padding-right: 0.2rem;
margin-top: $spacer / 2;
margin-top: calc(var(--spacer) / 3);
margin-bottom: 0;
font-size: $font-size-base;
font-size: var(--font-size-base);
transition: color 0.2s ease-out;
color: $text-color-light;
}
.title + div {
padding-left: 0.2rem;
padding-right: 0.2rem;
}
.post {
display: block;
figure {
margin: 0;
}
}
.time {
font-style: italic;
font-size: $font-size-small;
color: $text-color-light;
padding-left: 0.2rem;
padding-right: 0.2rem;
.post:hover {
text-decoration: none;
}
.post:hover .title {
color: var(--link-color);
}
.post figure {
margin: 0;
}
.empty {
@include media-frame;
composes: frame from '../atoms/Image.module.css';
display: block;
min-height: 95px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACaADAAQAAAABAAAACQAAAAAvQpmhAAAAHElEQVQYGWNgoBL4T8gcggoIGcBA0ASCCmhsBQBhFwX7u70C8QAAAABJRU5ErkJggg==);
a:hover & {
border-color: $link-color;
}
}
a:hover .empty {
border-color: var(--link-color);
}

View File

@ -3,7 +3,11 @@ import { Link, graphql } from 'gatsby'
import { Image } from '../atoms/Image'
import { Post } from '../../@types/Post'
import PostTitle from '../templates/Post/Title'
import styles from './PostTeaser.module.scss'
import {
post as stylePost,
empty,
title as styleTitle
} from './PostTeaser.module.css'
export const postTeaserQuery = graphql`
fragment PostTeaser on MarkdownRemark {
@ -42,21 +46,24 @@ export default function PostTeaser({
return (
<Link
className={styles.post}
className={stylePost}
to={slug}
onClick={toggleSearch && toggleSearch}
>
{image ? (
<Image fluid={image.childImageSharp.fluid} alt={title} />
<Image
image={(image as any).childImageSharp.gatsbyImageData}
alt={title}
/>
) : (
<span className={styles.empty} />
<span className={empty} />
)}
<PostTitle
title={title}
date={hideDate ? null : date}
updated={updated}
className={styles.title}
className={styleTitle}
/>
</Link>
)

View File

@ -0,0 +1,61 @@
.title {
font-size: var(--font-size-h3);
margin-bottom: var(--spacer);
}
.relatedPosts ul {
display: grid;
gap: calc(var(--spacer) / 2);
grid-template-columns: repeat(2, 1fr);
padding: 0;
margin: 0;
}
@media (min-width: 40rem) {
.relatedPosts ul {
grid-template-columns: repeat(3, 1fr);
gap: var(--spacer);
}
}
.relatedPosts li {
display: block;
margin: 0;
}
.relatedPosts li::before {
display: none;
}
.relatedPosts figure {
margin: 0;
}
.relatedPosts img {
margin-bottom: 0;
}
.relatedPosts a {
display: block;
}
.relatedPosts a > div {
margin-bottom: 0;
}
.relatedPosts a h4 {
color: var(--text-color-light);
}
.relatedPosts a:hover h4,
.relatedPosts a:focus h4 {
color: var(--link-color);
}
.button {
font-size: var(--font-size-mini);
display: inline-block;
color: var(--text-color-light);
text-transform: uppercase;
margin-left: calc(var(--spacer) / 2);
}

View File

@ -1,72 +0,0 @@
@import 'variables';
@import 'mixins';
.title {
font-size: $font-size-h3;
margin-bottom: $spacer;
}
.relatedPosts {
margin-top: -($spacer * 2);
@media (min-width: $screen-md) {
@include breakoutviewport;
}
ul {
display: grid;
gap: $spacer / 2;
grid-template-columns: repeat(2, 1fr);
padding: 0;
margin: 0;
@media (min-width: $screen-sm) {
grid-template-columns: repeat(3, 1fr);
gap: $spacer;
}
}
li {
display: block;
margin: 0;
&::before {
display: none;
}
}
figure {
margin: 0;
}
img {
margin-bottom: 0;
}
a {
display: block;
> div {
margin-bottom: 0;
}
h4 {
color: $text-color-light;
}
&:hover,
&:focus {
h4 {
color: $link-color;
}
}
}
}
.button {
font-size: $font-size-mini;
display: inline-block;
color: $brand-grey-light;
text-transform: uppercase;
margin-left: $spacer / 2;
}

View File

@ -1,7 +1,7 @@
import React, { ReactElement, useState } from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import PostTeaser from './PostTeaser'
import styles from './RelatedPosts.module.scss'
import { relatedPosts, title, button } from './RelatedPosts.module.css'
import { Post, Frontmatter } from '../../@types/Post'
import { PhotoThumb } from '../templates/Photos'
@ -21,7 +21,7 @@ function postsWithDataFilter(
posts: [{ node: Post }],
key: keyof Frontmatter,
valuesToFind: string[]
) {
): { node: Post }[] {
const newArray = posts
.filter(({ node }: { node: Post }) => {
const frontmatterKey = node.frontmatter[key] as []
@ -39,7 +39,7 @@ function postsWithDataFilter(
return newArray
}
function photosWithDataFilter(posts: []) {
function photosWithDataFilter(posts: [{ node: Post }]): { node: Post }[] {
const newArray = posts
.filter((post: { node: Post }) => {
const { fileAbsolutePath } = post.node
@ -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

@ -0,0 +1,22 @@
.searchButton {
padding: calc(var(--spacer) / 2);
vertical-align: middle;
display: inline-block;
margin: 0;
margin-right: calc(var(--spacer) / 4);
}
.searchButton:focus {
outline: 0;
}
.searchButton svg {
stroke: var(--text-color-light);
width: 24px;
height: 24px;
}
.searchButton:hover svg,
.searchButton:focus svg {
stroke: var(--link-color);
}

View File

@ -1,32 +0,0 @@
@import 'variables';
.searchButton {
padding: $spacer / 2;
vertical-align: middle;
display: inline-block;
margin: 0;
margin-right: $spacer / 4;
&:focus {
outline: 0;
}
svg {
stroke: $brand-grey-light;
width: 24px;
height: 24px;
}
&:hover,
&:focus {
svg {
stroke: $brand-cyan;
}
}
&:active {
svg {
stroke: darken($brand-cyan, 30%);
}
}
}

View File

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

View File

@ -0,0 +1,20 @@
.searchInput::-webkit-search-cancel-button {
display: none;
}
.searchInputClose {
position: absolute;
right: calc(var(--spacer) / 2);
top: calc(var(--spacer) / 2);
}
.searchInputClose svg {
stroke: var(--brand-grey-light);
width: 24px;
height: 24px;
}
.searchInputClose:hover svg,
.searchInputClose:focus svg {
stroke: var(--link-color);
}

View File

@ -1,28 +0,0 @@
@import 'variables';
.searchInput {
composes: input from '../../atoms/Input.module.scss';
&::-webkit-search-cancel-button {
display: none;
}
}
.searchInputClose {
position: absolute;
right: $spacer / 2;
top: $spacer / 2;
svg {
stroke: $brand-grey-light;
width: 24px;
height: 24px;
}
&:hover,
&:focus {
svg {
stroke: $link-color;
}
}
}

View File

@ -1,7 +1,7 @@
import React, { ReactElement } from 'react'
import Input from '../../atoms/Input'
import Icon from '../../atoms/Icon'
import styles from './SearchInput.module.scss'
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

@ -0,0 +1,72 @@
.searchResults {
position: absolute;
left: 0;
right: 0;
z-index: 10;
top: 0;
bottom: 0;
background: var(--body-background-color);
animation: fadein 0.3s;
overflow: scroll;
-webkit-overflow-scrolling: touch;
height: 91vh;
}
.results {
composes: container from '../../Layout.module.css';
padding: var(--spacer) calc(var(--spacer) / 2);
margin-bottom: 0;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
@media (min-width: 60rem) {
.results {
padding-left: 0;
padding-right: 0;
}
}
.results li {
display: block;
flex: 0 0 48%;
margin-bottom: var(--spacer);
}
@media (min-width: 40rem) {
.results li {
flex-basis: 31%;
}
}
.results li::before {
display: none;
}
.results img {
margin-bottom: 0;
}
.results a {
display: block;
}
.results a > div {
margin-bottom: 0;
}
.results a:hover h4,
.results a:focus h4 {
color: var(--link-color);
}
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -1,79 +0,0 @@
@import 'variables';
@import 'mixins';
.searchResults {
position: absolute;
left: 0;
right: 0;
z-index: 10;
top: 0;
bottom: 0;
background: rgba($body-background-color, 0.95);
backdrop-filter: blur(5px);
animation: fadein 0.3s;
overflow: scroll;
-webkit-overflow-scrolling: touch;
height: 91vh;
:global(.dark) & {
background: rgba($body-background-color--dark, 0.95);
}
ul {
@include breakoutviewport;
padding: $spacer $spacer / 2;
margin-bottom: 0;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
@media (min-width: $screen-md) {
padding-left: 0;
padding-right: 0;
}
li {
display: block;
flex: 0 0 48%;
margin-bottom: $spacer;
@media (min-width: $screen-sm) {
flex-basis: 31%;
}
&::before {
display: none;
}
}
}
img {
margin-bottom: 0;
}
a {
display: block;
> div {
margin-bottom: 0;
}
&:hover,
&:focus {
h4 {
color: $link-color;
}
}
}
}
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -1,10 +1,12 @@
import React, { ReactElement } from 'react'
import ReactDOM from 'react-dom'
import { graphql, useStaticQuery } from 'gatsby'
import Container from '../../atoms/Container'
import PostTeaser from '../PostTeaser'
import SearchResultsEmpty from './SearchResultsEmpty'
import styles from './SearchResults.module.scss'
import {
searchResults,
results as styleResults
} from './SearchResults.module.css'
import { Post } from '../../../@types/Post'
export interface Results {
@ -35,26 +37,24 @@ function SearchResultsPure({
toggleSearch(): void
}) {
return (
<div className={styles.searchResults}>
<Container>
{results.length > 0 ? (
<ul>
{results.map((page: { slug: string }) =>
posts
.filter(
({ node }: { node: Post }) => node.fields.slug === page.slug
)
.map(({ node }: { node: Post }) => (
<li key={page.slug}>
<PostTeaser post={node} toggleSearch={toggleSearch} />
</li>
))
)}
</ul>
) : (
<SearchResultsEmpty searchQuery={searchQuery} results={results} />
)}
</Container>
<div className={searchResults}>
{results.length > 0 ? (
<ul className={styleResults}>
{results.map((page: { slug: string }) =>
posts
.filter(
({ node }: { node: Post }) => node.fields.slug === page.slug
)
.map(({ node }: { node: Post }) => (
<li key={page.slug}>
<PostTeaser post={node} toggleSearch={toggleSearch} />
</li>
))
)}
</ul>
) : (
<SearchResultsEmpty searchQuery={searchQuery} results={results} />
)}
</div>
)
}

View File

@ -0,0 +1,34 @@
.empty {
padding-top: 15vh;
display: flex;
justify-content: center;
}
.emptyMessage {
color: var(--text-color-light);
}
.emptyMessageText {
margin-bottom: 0;
position: relative;
}
.emptyMessageText::after {
overflow: hidden;
display: inline-block;
vertical-align: bottom;
animation: ellipsis steps(4, end) 1s infinite;
/* ascii code for the ellipsis character */
content: '\2026';
width: 0;
position: absolute;
left: 101%;
bottom: 0;
}
@keyframes ellipsis {
to {
width: 1rem;
}
}

View File

@ -1,34 +0,0 @@
@import 'variables';
.empty {
padding-top: 15vh;
display: flex;
justify-content: center;
}
.emptyMessage {
color: $brand-grey-light;
}
.emptyMessageText {
margin-bottom: 0;
position: relative;
&::after {
overflow: hidden;
display: inline-block;
vertical-align: bottom;
animation: ellipsis steps(4, end) 1s infinite;
content: '\2026'; // ascii code for the ellipsis character
width: 0;
position: absolute;
left: 101%;
bottom: 0;
}
}
@keyframes ellipsis {
to {
width: 1rem;
}
}

View File

@ -1,5 +1,9 @@
import React, { ReactElement } from 'react'
import styles from './SearchResultsEmpty.module.scss'
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

@ -0,0 +1,46 @@
.search {
position: absolute;
left: calc(var(--spacer) / 2);
right: calc(var(--spacer) / 2);
top: calc(var(--spacer) / 4);
z-index: 10;
}
.search input {
width: 100%;
}
@media (min-width: 40rem) {
.search {
left: 0;
right: 0;
}
}
.appear,
.enter {
opacity: 0.01;
transform: translate3d(0, -100px, 0);
}
.appearActive,
.enterActive {
opacity: 1;
transition: 0.2s ease-out;
transform: translate3d(0, 0, 0);
}
.exit {
opacity: 1;
transform: translate3d(0, 0, 0);
}
.exitActive {
opacity: 0.01;
transition: 0.2s ease-in;
transform: translate3d(0, -100px, 0);
}
:global(.hasSearchOpen) {
overflow: hidden;
}

View File

@ -1,46 +0,0 @@
@import 'variables';
.search {
position: absolute;
left: $spacer / 2;
right: $spacer / 2;
top: $spacer / 4;
z-index: 10;
input {
width: 100%;
}
@media (min-width: $screen-md) {
left: 0;
right: 0;
}
}
.appear,
.enter {
opacity: 0.01;
transform: translate3d(0, -100px, 0);
&.appearActive,
&.enterActive {
opacity: 1;
transition: 0.2s ease-out;
transform: translate3d(0, 0, 0);
}
}
.exit {
opacity: 1;
transform: translate3d(0, 0, 0);
&.exitActive {
opacity: 0.01;
transition: 0.2s ease-in;
transform: translate3d(0, -100px, 0);
}
}
:global(.hasSearchOpen) {
overflow: hidden;
}

Some files were not shown because too many files have changed in this diff Show More