dark mode + .... some form of toggle :)

This commit is contained in:
Rick van Lieshout 2022-09-11 00:00:31 +02:00
parent c22687de6d
commit 31c106c58e
20 changed files with 291 additions and 20 deletions

View File

@ -4,12 +4,23 @@ This is the repository for my personal blog/website [rickvanlieshout.com](https:
## Todo
- Add a dark mode
- Add a better dark mode toggle
- show in posts view
- print view
- hide mode toggler
- hide "all articles" link in posts
- Change theme of prism.js
- "time to read" (https://www.gatsbyjs.com/plugins/gatsby-remark-reading-time/)
<!-- migrations -->
- migrate resume
- migrate projects
- migrate some example blogs
<!-- optional stuff -->
- release to sftp or gh-pages
- "time to read"
- Maybe improve the cookie page with actual cookie data?
- lighthouse doesn't like the blue... :)

View File

@ -4,10 +4,13 @@ template: "page"
---
A cookie is a small text file that a website saves on the client
computer (your computer) to make the website function better. Some
examples of what cookies are used for:
computer (your computer) to make the website function better.
Note that these files can be stored as actual "cookies" or as "local/session storage".<br />
I will be using Cookie to refer to both in the rest of this article
- Remembering what language the website should be in
Some examples of what cookies are used for:
- Remembering what language/theme the website should be in
- Storing your cart when shopping online
- Keeping you logged in

30
gatsby-ssr.ts Normal file
View File

@ -0,0 +1,30 @@
import { RenderBodyArgs } from "gatsby";
import React from "react";
const setColorTheme = `
(function() {
const mode = localStorage.getItem('theme');
if (mode !== null && ['light', 'dark'].includes(mode)) {
document.documentElement.dataset.theme = mode;
return;
}
const preferredColorScheme = window.matchMedia('(prefers-color-scheme: dark)');
const hasMediaQueryPreference = typeof preferredColorScheme.matches === 'boolean';
if (hasMediaQueryPreference && preferredColorScheme.matches === true) {
document.documentElement.dataset.theme = 'dark';
} else {
document.documentElement.dataset.theme = 'light'
}
})();
`;
export const onRenderBody = ({ setPreBodyComponents }: RenderBodyArgs) => {
setPreBodyComponents([
React.createElement("script", {
key: "theme",
dangerouslySetInnerHTML: {
__html: setColorTheme,
},
}),
]);
};

113
package-lock.json generated
View File

@ -84,6 +84,7 @@
"jest-svg-transformer": "^1.0.0",
"lint-staged": "^13.0.3",
"lost": "9.0.1",
"markdownlint": "^0.26.2",
"postcss": "^8.4.16",
"postcss-scss": "^4.0.4",
"prettier": "^2.7.1",
@ -21170,6 +21171,15 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dev": true,
"dependencies": {
"uc.micro": "^1.0.1"
}
},
"node_modules/lint-staged": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz",
@ -21899,6 +21909,40 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"bin": {
"markdown-it": "bin/markdown-it.js"
}
},
"node_modules/markdown-it/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/markdown-it/node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true,
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/markdown-table": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz",
@ -21911,6 +21955,18 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/markdownlint": {
"version": "0.26.2",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.26.2.tgz",
"integrity": "sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==",
"dev": true,
"dependencies": {
"markdown-it": "13.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/marked": {
"version": "4.0.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz",
@ -32347,6 +32403,12 @@
"node": "*"
}
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"node_modules/uglify-js": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz",
@ -49604,6 +49666,15 @@
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dev": true,
"requires": {
"uc.micro": "^1.0.1"
}
},
"lint-staged": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz",
@ -50168,6 +50239,33 @@
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"dev": true
},
"markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dev": true,
"requires": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true
}
}
},
"markdown-table": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz",
@ -50176,6 +50274,15 @@
"repeat-string": "^1.0.0"
}
},
"markdownlint": {
"version": "0.26.2",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.26.2.tgz",
"integrity": "sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==",
"dev": true,
"requires": {
"markdown-it": "13.0.1"
}
},
"marked": {
"version": "4.0.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.0.19.tgz",
@ -57849,6 +57956,12 @@
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"uglify-js": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.0.tgz",

View File

@ -119,6 +119,7 @@
"jest-svg-transformer": "^1.0.0",
"lint-staged": "^13.0.3",
"lost": "9.0.1",
"markdownlint": "^0.26.2",
"postcss": "^8.4.16",
"postcss-scss": "^4.0.4",
"prettier": "^2.7.1",

View File

@ -1,14 +1,33 @@
@use "sass:math";
@use "sass:color";
$color-base: hsl(0, 0%, 13%);
$color-primary: hsl(220, 100%, 68%);
$color-secondary: hsl(220, 100%, 68%);
:root {
--bg-color: #fff;
--base: hsl(0, 0%, 13%);
--primary: hsl(220, 100%, 68%);
--secondary: hsl(220, 100%, 68%);
--gray: hsl(230, 23%, 23%);
--gray-border: hsl(230, 77%, 13%);
}
[data-theme="dark"] {
--bg-color: hsl(210, 3.7%, 10.6%);
--base: hsl(219, 14%, 71%);
--primary: hsl(220, 100%, 68%);
--secondary: hsl(220, 100%, 68%);
--gray-border: hsl(220, 10%, 40%);
--gray: hsl(0, 0%, 100%);
}
$color-bg: var(--bg-color);
$color-base: var(--base);
$color-primary: var(--primary);
$color-secondary: var(--secondary);
$color-white: hsl(0, 0%, 100%);
$color-gray: color.adjust($color-base, $lightness: 40%);
$color-gray-border: color.adjust($color-base, $lightness: 77%);
$color-gray-bg: color.adjust($color-base, $lightness: 79%);
$color-gray: color.adjust(hsl(0, 0%, 13%), $lightness: 30%);
$color-gray-border: var(--gray-border);
$color-gray-bg: var(--gray-border);
$typographic-font-family: -apple-system, "BlinkMacSystemFont", "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;

View File

@ -6,6 +6,7 @@ html {
}
body {
background-color: $color-bg;
color: $typographic-base-font-color;
font-family: $typographic-font-family;
font-size: $typographic-base-font-size;

View File

@ -0,0 +1,29 @@
import React, { useState, useEffect } from "react";
enum themes {
dark = "dark",
light = "light",
}
function ThemeSwitcher() {
const initTheme = document.documentElement.dataset.theme;
const [theme, setTheme] = useState(initTheme);
useEffect(() => {
localStorage.setItem("theme", theme ?? themes.light);
document.documentElement.dataset.theme = theme;
}, [theme]);
const getOppositeTheme = () => {
return theme === themes.light ? themes.dark : themes.light;
};
function toggleTheme() {
const newTheme = theme === themes.dark ? themes.light : themes.dark;
setTheme(newTheme);
}
return <a href="#" onClick={toggleTheme}>{`Switch to ${getOppositeTheme()} mode`}</a>;
}
export default ThemeSwitcher;

View File

@ -3,9 +3,9 @@
.header {
align-items: baseline;
background: #fff;
background: $color-bg;
border-bottom: 1px solid #808080;
color: #000;
color: $color-base;
display: flex;
height: 50px;
justify-content: space-between;
@ -33,7 +33,7 @@
}
.name {
color: #000;
color: $color-base;
}
}

View File

@ -4,6 +4,6 @@
@import "../../../assets/scss/mixins";
.copyright {
color: color.adjust($color-gray, $whiteness: 18%);
color: $color-gray;
font-size: $typographic-small-font-size;
}

View File

@ -7,6 +7,7 @@ import { Menu } from "./Menu";
import * as styles from "./Sidebar.module.scss";
import { useSiteMetadata } from "@/hooks";
import ThemeSwitcher from "../DarkmodeSwitch/ThemeSwitcher";
type Props = {
isIndex?: boolean;
@ -23,6 +24,7 @@ const Sidebar = ({ isIndex }: Props) => {
<Contacts contacts={author.contacts} />
<Copyright copyright={copyright} />
<Menu menu={legalMenu} />
<ThemeSwitcher />
</div>
</div>
);

View File

@ -141,6 +141,12 @@ exports[`Sidebar renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
`;

View File

@ -142,6 +142,12 @@ exports[`CategoriesTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>

View File

@ -142,6 +142,12 @@ exports[`CategoryTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>

View File

@ -142,6 +142,12 @@ exports[`IndexTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>

View File

@ -4,6 +4,7 @@ import { Layout } from "@/components/Layout";
import { Page } from "@/components/Page";
import { Sidebar } from "@/components/Sidebar";
import { useSiteMetadata } from "@/hooks";
import { Link } from "gatsby";
const NotFoundTemplate: React.FC = () => {
const { title, subtitle } = useSiteMetadata();
@ -11,8 +12,12 @@ const NotFoundTemplate: React.FC = () => {
return (
<Layout title={`Not Found - ${title}`} description={subtitle}>
<Sidebar />
<Page title="NOT FOUND">
<p>You just hit a route that doesn't exist... the sadness.</p>
<Page title="Oh no! page be lost">
<p>
You've stumbled upon a link that doesn't work anymore {":("}
<br />
Use the menu to navigate around or click <Link to="/">here</Link> to go to the main page.
</p>
</Page>
</Layout>
);

View File

@ -142,16 +142,31 @@ exports[`NotFoundTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>
<div>
<h1>
NOT FOUND
Oh no! page be lost
</h1>
<div>
<p>
You just hit a route that doesn't exist... the sadness.
You've stumbled upon a link that doesn't work anymore
:(
<br />
Use the menu to navigate around or click
<a
href="/"
>
here
</a>
to go to the main page.
</p>
</div>
</div>

View File

@ -142,6 +142,12 @@ exports[`PageTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>

View File

@ -142,6 +142,12 @@ exports[`TagTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>

View File

@ -142,6 +142,12 @@ exports[`TagsTemplate renders correctly 1`] = `
<nav>
<ul />
</nav>
<a
href="#"
onClick={[Function]}
>
Switch to light mode
</a>
</div>
</div>
<div>