Update to Eleventy v3 (#11)
* feat: upgrade to v3; install eleventy-upgrade-help * feat: convert all files to esm * feat: remove decapcms * fix: remove unused filter * feat: remove netlify packages * feat: update image handling - removes old image shortcode - update to latest 11ty image transform plugin * feat: update colophon * fix: pill style; global style Fixes an issue with <img> inside <figure> not being centered * feat: remove linting packages * feat: update package.json scripts * feat: remove upgrade helper plugin * feat: add new button style, update nav * feat: simplify `pill` class usage * feat: fix tag list in catalogue-item.html * feat: move games into their own section * feat: update node version to latest LTS * feat: move books to their own section * feat: move fun pages into pages dir * feat: update index and book/game templates * feat: add watching section * fix: update scaling values for buttons * feat: various css updates * feat: update now page style * feat: cleaning up newer posts using old shortcode also adding markdown-it-attrs to add attrs to various markdown elements! * fix: movie data structure * feat: update colophon * fix: remove text-skew from post excerpt text * feat: add support for shows in /watching * fix: update book tags * feat: add complete implementation of books pages other stuff happened too * fix: image border-radius * feat: update game layout and content * feat: reorganize watching section * feat: add contact page * feat: small page changes * feat: add podroll page * feat: reorganize content directories * feat: exclude podcasts from page output * chore: delete guestbook page * chore: remove bracket syntax for css classes in html * feat: create macro for tag list * fix: colophon update * chore: remove last.fm data * chore: clean up 11ty config * fix: misc permalink fixes * feat: add update post * fix: media meta grid on mobile * fix: tables on mobile * fix: add titles to icon button links * fix: add missing divider for movies/shows * feat: add alternate feeds * fix: tag cleanup * feat: homepage content update * fix: game meta data * fix: update post dates * feat: add missing link to changelog
15
.eslintrc.js
|
@ -1,15 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
},
|
|
||||||
extends: ["eslint:recommended", "plugin:prettier/recommended"],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module",
|
|
||||||
},
|
|
||||||
ignorePatterns: ["!.eleventy.js"],
|
|
||||||
rules: {
|
|
||||||
"prettier/prettier": "error",
|
|
||||||
},
|
|
||||||
};
|
|
2
.nvmrc
|
@ -1 +1 @@
|
||||||
v20.11.1
|
v22.11.0
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
dist
|
|
|
@ -1,4 +1,6 @@
|
||||||
const postsByTag = (collection) => {
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export const postsByTag = (collection) => {
|
||||||
const posts = collection.getFilteredByTag("post");
|
const posts = collection.getFilteredByTag("post");
|
||||||
|
|
||||||
const postsByTag = {};
|
const postsByTag = {};
|
||||||
|
@ -13,7 +15,22 @@ const postsByTag = (collection) => {
|
||||||
return postsByTag;
|
return postsByTag;
|
||||||
};
|
};
|
||||||
|
|
||||||
const catalogueByType = (collection) => {
|
export const collectionByTag = (collection, collectionName) => {
|
||||||
|
const items = collection.getFilteredByTag(collectionName);
|
||||||
|
|
||||||
|
const itemsByTag = {};
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
for (const tag of item.data.tags) {
|
||||||
|
itemsByTag[tag] ??= [];
|
||||||
|
itemsByTag[tag].push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsByTag;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const catalogueByType = (collection) => {
|
||||||
const allItems = collection.getFilteredByTag("catalogue");
|
const allItems = collection.getFilteredByTag("catalogue");
|
||||||
|
|
||||||
const catalogueByType = {};
|
const catalogueByType = {};
|
||||||
|
@ -28,8 +45,3 @@ const catalogueByType = (collection) => {
|
||||||
|
|
||||||
return catalogueByType;
|
return catalogueByType;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
catalogueByType,
|
|
||||||
postsByTag,
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
const dir = {
|
export const dir = {
|
||||||
assets: "assets",
|
assets: "assets",
|
||||||
data: "_data",
|
data: "_data",
|
||||||
includes: "_includes",
|
includes: "_includes",
|
||||||
input: "src",
|
input: "src",
|
||||||
output: "dist",
|
output: "dist",
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { dir };
|
|
||||||
|
|
22
config/design-tokens/colors.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
export default {
|
||||||
|
light: {
|
||||||
|
primary: "188deg 84% 35%",
|
||||||
|
secondary: "8deg 84% 50%",
|
||||||
|
background: "0deg 0% 98%",
|
||||||
|
surface: "188deg 27% 94%",
|
||||||
|
border: "188deg 48% 80%",
|
||||||
|
text: "0deg 0% 4%",
|
||||||
|
fadeText: "188deg 12% 32%",
|
||||||
|
shadow: "188deg 100% 18%",
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
primary: "188deg 100% 30%",
|
||||||
|
secondary: "8deg 84% 43%",
|
||||||
|
background: "0deg 0% 9%",
|
||||||
|
surface: "202deg 13% 14%",
|
||||||
|
border: "208deg 27% 15%",
|
||||||
|
text: "0deg 0% 98%",
|
||||||
|
fadeText: "188deg 12% 70%",
|
||||||
|
shadow: "188deg 100% 18%",
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"light": {
|
|
||||||
"primary": "188deg 84% 35%",
|
|
||||||
"secondary": "8deg 84% 50%",
|
|
||||||
"background": "0deg 0% 98%",
|
|
||||||
"surface": "188deg 27% 94%",
|
|
||||||
"border": "188deg 48% 80%",
|
|
||||||
"text": "0deg 0% 4%",
|
|
||||||
"fadeText": "188deg 12% 32%",
|
|
||||||
"shadow": "188deg 100% 18%"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"primary": "188deg 100% 30%",
|
|
||||||
"secondary": "8deg 84% 43%",
|
|
||||||
"background": "0deg 0% 9%",
|
|
||||||
"surface": "202deg 13% 14%",
|
|
||||||
"border": "208deg 27% 15%",
|
|
||||||
"text": "0deg 0% 98%",
|
|
||||||
"fadeText": "188deg 12% 70%",
|
|
||||||
"shadow": "188deg 100% 18%"
|
|
||||||
}
|
|
||||||
}
|
|
60
config/design-tokens/fonts.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
export default {
|
||||||
|
display: {
|
||||||
|
family: "Anek Latin",
|
||||||
|
format: "truetype",
|
||||||
|
weights: {
|
||||||
|
ExtraBold: {
|
||||||
|
path: "/aneklatin/AnekLatin-ExtraBold.ttf",
|
||||||
|
"font-style": "normal",
|
||||||
|
weight: 800,
|
||||||
|
},
|
||||||
|
Bold: {
|
||||||
|
path: "/aneklatin/AnekLatin-Bold.ttf",
|
||||||
|
"font-style": "normal",
|
||||||
|
weight: 700,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
family: "iA Writer Quattro V",
|
||||||
|
format: "woff2",
|
||||||
|
weights: {
|
||||||
|
Regular: {
|
||||||
|
path: "/quattro/iAWriterQuattroS-Regular.woff2",
|
||||||
|
"font-style": "normal",
|
||||||
|
weight: 400,
|
||||||
|
},
|
||||||
|
Italic: {
|
||||||
|
path: "/quattro/iAWriterQuattroS-Italic.woff2",
|
||||||
|
"font-style": "italic",
|
||||||
|
weight: 400,
|
||||||
|
},
|
||||||
|
Bold: {
|
||||||
|
path: "/quattro/iAWriterQuattroS-Bold.woff2",
|
||||||
|
"font-style": "normal",
|
||||||
|
weight: 650,
|
||||||
|
},
|
||||||
|
BoldItalic: {
|
||||||
|
path: "/quattro/iAWriterQuattroS-BoldItalic.woff2",
|
||||||
|
"font-style": "italic",
|
||||||
|
weight: 650,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
monospace: {
|
||||||
|
family: "IBM Plex Mono",
|
||||||
|
format: "truetype",
|
||||||
|
weights: {
|
||||||
|
Regular: {
|
||||||
|
path: "/ibmplexmono/IBMPlexMono-Regular.ttf",
|
||||||
|
"font-style": "normal",
|
||||||
|
weight: 400,
|
||||||
|
},
|
||||||
|
Italic: {
|
||||||
|
path: "/ibmplexmono/IBMPlexMono-Italic.ttf",
|
||||||
|
"font-style": "italic",
|
||||||
|
weight: 400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,60 +0,0 @@
|
||||||
{
|
|
||||||
"display": {
|
|
||||||
"family": "Anek Latin",
|
|
||||||
"format": "truetype",
|
|
||||||
"weights": {
|
|
||||||
"ExtraBold": {
|
|
||||||
"path": "/aneklatin/AnekLatin-ExtraBold.ttf",
|
|
||||||
"font-style": "normal",
|
|
||||||
"weight": 800
|
|
||||||
},
|
|
||||||
"Bold": {
|
|
||||||
"path": "/aneklatin/AnekLatin-Bold.ttf",
|
|
||||||
"font-style": "normal",
|
|
||||||
"weight": 700
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"body": {
|
|
||||||
"family": "iA Writer Quattro V",
|
|
||||||
"format": "woff2",
|
|
||||||
"weights": {
|
|
||||||
"Regular": {
|
|
||||||
"path": "/quattro/iAWriterQuattroS-Regular.woff2",
|
|
||||||
"font-style": "normal",
|
|
||||||
"weight": 400
|
|
||||||
},
|
|
||||||
"Italic": {
|
|
||||||
"path": "/quattro/iAWriterQuattroS-Italic.woff2",
|
|
||||||
"font-style": "italic",
|
|
||||||
"weight": 400
|
|
||||||
},
|
|
||||||
"Bold": {
|
|
||||||
"path": "/quattro/iAWriterQuattroS-Bold.woff2",
|
|
||||||
"font-style": "normal",
|
|
||||||
"weight": 650
|
|
||||||
},
|
|
||||||
"BoldItalic": {
|
|
||||||
"path": "/quattro/iAWriterQuattroS-BoldItalic.woff2",
|
|
||||||
"font-style": "italic",
|
|
||||||
"weight": 650
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"monospace": {
|
|
||||||
"family": "IBM Plex Mono",
|
|
||||||
"format": "truetype",
|
|
||||||
"weights": {
|
|
||||||
"Regular": {
|
|
||||||
"path": "/ibmplexmono/IBMPlexMono-Regular.ttf",
|
|
||||||
"font-style": "normal",
|
|
||||||
"weight": 400
|
|
||||||
},
|
|
||||||
"Italic": {
|
|
||||||
"path": "/ibmplexmono/IBMPlexMono-Italic.ttf",
|
|
||||||
"font-style": "italic",
|
|
||||||
"weight": 400
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
config/design-tokens/spacing.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export default {
|
||||||
|
0: 0,
|
||||||
|
0.25: 4,
|
||||||
|
0.5: 8,
|
||||||
|
1: 16,
|
||||||
|
1.5: 24,
|
||||||
|
2: 32,
|
||||||
|
3: 48,
|
||||||
|
4: 64,
|
||||||
|
5: 80,
|
||||||
|
};
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"0": 0,
|
|
||||||
"0.25": 4,
|
|
||||||
"0.5": 8,
|
|
||||||
"1": 16,
|
|
||||||
"1.5": 24,
|
|
||||||
"2": 32,
|
|
||||||
"3": 48,
|
|
||||||
"4": 64,
|
|
||||||
"5": 80
|
|
||||||
}
|
|
|
@ -1,19 +1,19 @@
|
||||||
const dayjs = require("dayjs");
|
import dayjs from "dayjs";
|
||||||
const utc = require("dayjs/plugin/utc");
|
import utc from "dayjs/plugin/utc.js";
|
||||||
const advancedFormat = require("dayjs/plugin/advancedFormat");
|
import advancedFormat from "dayjs/plugin/advancedFormat.js";
|
||||||
|
|
||||||
const pluralizeBase = require("pluralize");
|
import pluralizeBase from "pluralize";
|
||||||
|
|
||||||
const keys = Object.keys;
|
export const keys = Object.keys;
|
||||||
const values = Object.values;
|
export const values = Object.values;
|
||||||
const entries = Object.entries;
|
export const entries = Object.entries;
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(advancedFormat);
|
dayjs.extend(advancedFormat);
|
||||||
|
|
||||||
const formatDate = (date, format) => dayjs.utc(date).format(format);
|
export const formatDate = (date, format) => dayjs.utc(date).format(format);
|
||||||
|
|
||||||
const organizeByDate = (collection) => {
|
export const organizeByDate = (collection) => {
|
||||||
const collectionByDate = {};
|
const collectionByDate = {};
|
||||||
|
|
||||||
collection.forEach((item) => {
|
collection.forEach((item) => {
|
||||||
|
@ -29,15 +29,23 @@ const organizeByDate = (collection) => {
|
||||||
return collectionByDate;
|
return collectionByDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allTags = (collection, ignore = []) => {
|
export const transformByDate = (collection) => {
|
||||||
const tagSet = new Set(collection.flatMap((item) => item.data.tags));
|
const collectionByDate = {};
|
||||||
|
|
||||||
ignore.forEach((tag) => tagSet.delete(tag));
|
collection.forEach((item) => {
|
||||||
|
const year = formatDate(item.date, "YYYY");
|
||||||
|
|
||||||
return [...tagSet];
|
if (!collectionByDate[year]) {
|
||||||
|
return (collectionByDate[year] = { value: year, data: [item] });
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionByDate[year].data.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
return collectionByDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allTagCounts = (collection, ignore = ["post"]) => {
|
export const allTagCounts = (collection, ignore = ["post"]) => {
|
||||||
if (!collection.length) {
|
if (!collection.length) {
|
||||||
throw new Error("Invalid collection, no items");
|
throw new Error("Invalid collection, no items");
|
||||||
}
|
}
|
||||||
|
@ -62,28 +70,23 @@ const allTagCounts = (collection, ignore = ["post"]) => {
|
||||||
return tagArray;
|
return tagArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
const filter = (collection, filters = []) => {
|
export const filter = (collection, filters = []) => {
|
||||||
return collection.filter((item) => !filters.includes(item));
|
return collection.filter((item) => !filters.includes(item));
|
||||||
};
|
};
|
||||||
|
|
||||||
const pluralize = (string, count = 0) => {
|
export const pluralize = (string, count = 0) => {
|
||||||
return pluralizeBase(string, count);
|
return pluralizeBase(string, count);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterCatalogueTags = (tags) => {
|
export const limit = (collection, limit = 5) => collection.slice(0, limit);
|
||||||
// In the case of catalogue items, the 0-index is "catalogue"
|
|
||||||
// and the 1-index is the catalogueType. We don't need to
|
export const filterFavourites = (collection) => {
|
||||||
// show those in the front-end.
|
return collection.filter(
|
||||||
return filter(tags, [tags[0], tags[1]]);
|
(item) => item.data.favourite || item.data.isFavourite,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const limit = (collection, limit = 5) => collection.slice(0, limit);
|
export const isOld = (dateArg) => {
|
||||||
|
|
||||||
const filterFavourites = (collection) => {
|
|
||||||
return collection.filter((item) => item.data.favourite);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isOld = (dateArg) => {
|
|
||||||
const date = dayjs(dateArg);
|
const date = dayjs(dateArg);
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
|
|
||||||
|
@ -91,19 +94,3 @@ const isOld = (dateArg) => {
|
||||||
|
|
||||||
return diffInYears >= 2;
|
return diffInYears >= 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
allTagCounts,
|
|
||||||
allTags,
|
|
||||||
entries,
|
|
||||||
filter,
|
|
||||||
filterCatalogueTags,
|
|
||||||
filterFavourites,
|
|
||||||
formatDate,
|
|
||||||
isOld,
|
|
||||||
keys,
|
|
||||||
limit,
|
|
||||||
organizeByDate,
|
|
||||||
pluralize,
|
|
||||||
values,
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
const markdownIt = require("markdown-it");
|
import markdownIt from "markdown-it";
|
||||||
const markdownItFootnote = require("markdown-it-footnote");
|
import markdownItFootnote from "markdown-it-footnote";
|
||||||
const markdownItPrism = require("markdown-it-prism");
|
import markdownItPrism from "markdown-it-prism";
|
||||||
const markdownItAbbr = require("markdown-it-abbr");
|
import markdownItAbbr from "markdown-it-abbr";
|
||||||
const markdownItAnchor = require("markdown-it-anchor");
|
import markdownItAnchor from "markdown-it-anchor";
|
||||||
|
import markdownItImplicitFigures from "markdown-it-image-figures";
|
||||||
|
import markdownItAttrs from "markdown-it-attrs";
|
||||||
|
|
||||||
const markdown = markdownIt({
|
const markdown = markdownIt({
|
||||||
html: true,
|
html: true,
|
||||||
|
@ -13,9 +15,13 @@ const markdown = markdownIt({
|
||||||
.use(markdownItFootnote)
|
.use(markdownItFootnote)
|
||||||
.use(markdownItAbbr)
|
.use(markdownItAbbr)
|
||||||
.use(markdownItAnchor)
|
.use(markdownItAnchor)
|
||||||
|
.use(markdownItImplicitFigures, {
|
||||||
|
figcaption: true,
|
||||||
|
})
|
||||||
.use(markdownItPrism, {
|
.use(markdownItPrism, {
|
||||||
defaultLanguage: "plaintext",
|
defaultLanguage: "plaintext",
|
||||||
});
|
})
|
||||||
|
.use(markdownItAttrs);
|
||||||
|
|
||||||
markdown.renderer.rules.footnote_block_open = (_tokens, _idx, options) => {
|
markdown.renderer.rules.footnote_block_open = (_tokens, _idx, options) => {
|
||||||
return (
|
return (
|
||||||
|
@ -27,4 +33,4 @@ markdown.renderer.rules.footnote_block_open = (_tokens, _idx, options) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = markdown;
|
export default markdown;
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
const Image = require("@11ty/eleventy-img");
|
|
||||||
|
|
||||||
const stringifyAttributes = (attributeMap) => {
|
|
||||||
return Object.entries(attributeMap)
|
|
||||||
.map(([attribute, value]) => {
|
|
||||||
if (typeof value === "undefined") return "";
|
|
||||||
return `${attribute}="${value}"`;
|
|
||||||
})
|
|
||||||
.join(" ");
|
|
||||||
};
|
|
||||||
|
|
||||||
const imageShortcode = async (
|
|
||||||
src,
|
|
||||||
alt = "",
|
|
||||||
caption = "",
|
|
||||||
className = undefined,
|
|
||||||
placeholder = "",
|
|
||||||
widths = [400, 800, 1280],
|
|
||||||
formats = ["webp", "jpeg"],
|
|
||||||
sizes = "100vw",
|
|
||||||
) => {
|
|
||||||
if (!src) {
|
|
||||||
return `<div class="image-placeholder">${placeholder}</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata = await Image(src, {
|
|
||||||
widths: [...widths, null],
|
|
||||||
formats: [...formats, null],
|
|
||||||
outputDir: "dist/assets/images",
|
|
||||||
urlPath: "/assets/images",
|
|
||||||
sharpOptions: {
|
|
||||||
animated: true,
|
|
||||||
},
|
|
||||||
cacheOptions: {
|
|
||||||
duration: "7d",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const lowsrc = metadata.jpeg[metadata.jpeg.length - 1];
|
|
||||||
|
|
||||||
const imageSources = Object.values(metadata)
|
|
||||||
.map((imageFormat) => {
|
|
||||||
return ` <source type="${imageFormat[0].sourceType}" srcset="${imageFormat
|
|
||||||
.map((entry) => entry.srcset)
|
|
||||||
.join(", ")}" sizes="${sizes}">`;
|
|
||||||
})
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
const imageAttributes = stringifyAttributes({
|
|
||||||
src: lowsrc.url,
|
|
||||||
width: lowsrc.width,
|
|
||||||
height: lowsrc.height,
|
|
||||||
alt,
|
|
||||||
loading: "lazy",
|
|
||||||
decoding: "async",
|
|
||||||
});
|
|
||||||
|
|
||||||
const imageElement = caption
|
|
||||||
? `<figure class="[ flow flex-col items-center justify-center ${className ? ` ${className} ` : ""}]">
|
|
||||||
<picture class="flex items-center justify-center">
|
|
||||||
${imageSources}
|
|
||||||
<img
|
|
||||||
${imageAttributes}>
|
|
||||||
</picture>
|
|
||||||
<figcaption>${caption}</figcaption>
|
|
||||||
</figure>`
|
|
||||||
: `<picture class="[ flex-col items-center justify-center ${className ? ` ${className} ` : ""}]">
|
|
||||||
${imageSources}
|
|
||||||
<img
|
|
||||||
${imageAttributes}>
|
|
||||||
</picture>`;
|
|
||||||
|
|
||||||
return imageElement;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = imageShortcode;
|
|
|
@ -12,4 +12,4 @@ const liteYoutube = (id, label) => {
|
||||||
</div>`;
|
</div>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = liteYoutube;
|
export default liteYoutube;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const htmlmin = require("html-minifier-terser");
|
import htmlmin from "html-minifier-terser";
|
||||||
|
|
||||||
module.exports = (eleventyConfig) => {
|
export default function (eleventyConfig) {
|
||||||
eleventyConfig.addTransform("html-minify", (content, path) => {
|
eleventyConfig.addTransform("html-minify", (content, path) => {
|
||||||
if (path && path.endsWith(".html")) {
|
if (path && path.endsWith(".html")) {
|
||||||
return htmlmin.minify(content, {
|
return htmlmin.minify(content, {
|
||||||
|
@ -17,4 +17,4 @@ module.exports = (eleventyConfig) => {
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
const pluginRss = require("@11ty/eleventy-plugin-rss");
|
import util from "util";
|
||||||
const pluginNoRobots = require("eleventy-plugin-no-robots");
|
import pluginRss from "@11ty/eleventy-plugin-rss";
|
||||||
|
import pluginNoRobots from "eleventy-plugin-no-robots";
|
||||||
|
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";
|
||||||
|
|
||||||
const {
|
import { collectionByTag, postsByTag } from "./config/collections/index.js";
|
||||||
catalogueByType,
|
|
||||||
postsByTag,
|
import { dir } from "./config/constants.js";
|
||||||
} = require("./config/collections/index.js");
|
import {
|
||||||
const { dir } = require("./config/constants.js");
|
|
||||||
const {
|
|
||||||
allTagCounts,
|
allTagCounts,
|
||||||
entries,
|
entries,
|
||||||
filter,
|
filter,
|
||||||
filterCatalogueTags,
|
|
||||||
filterFavourites,
|
filterFavourites,
|
||||||
formatDate,
|
formatDate,
|
||||||
isOld,
|
isOld,
|
||||||
|
@ -19,12 +18,13 @@ const {
|
||||||
organizeByDate,
|
organizeByDate,
|
||||||
pluralize,
|
pluralize,
|
||||||
values,
|
values,
|
||||||
} = require("./config/filters/index.js");
|
} from "./config/filters/index.js";
|
||||||
const markdown = require("./config/plugins/markdown.js");
|
import markdown from "./config/plugins/markdown.js";
|
||||||
const imageShortcode = require("./config/shortcodes/image.js");
|
import liteYoutube from "./config/shortcodes/youtube.js";
|
||||||
const liteYoutube = require("./config/shortcodes/youtube.js");
|
|
||||||
|
|
||||||
module.exports = (eleventyConfig) => {
|
import htmlConfigTransform from "./config/transforms/html-config.js";
|
||||||
|
|
||||||
|
export default function (eleventyConfig) {
|
||||||
eleventyConfig.addWatchTarget("./src/css");
|
eleventyConfig.addWatchTarget("./src/css");
|
||||||
|
|
||||||
// --------------------- Plugins ---------------------
|
// --------------------- Plugins ---------------------
|
||||||
|
@ -32,14 +32,24 @@ module.exports = (eleventyConfig) => {
|
||||||
eleventyConfig.addPlugin(pluginNoRobots);
|
eleventyConfig.addPlugin(pluginNoRobots);
|
||||||
|
|
||||||
// --------------------- Custom Collections -----------------------
|
// --------------------- Custom Collections -----------------------
|
||||||
eleventyConfig.addCollection("catalogueByType", catalogueByType);
|
|
||||||
eleventyConfig.addCollection("postsByTag", postsByTag);
|
eleventyConfig.addCollection("postsByTag", postsByTag);
|
||||||
|
eleventyConfig.addCollection("booksByTag", (collection) =>
|
||||||
|
collectionByTag(collection, "book"),
|
||||||
|
);
|
||||||
|
eleventyConfig.addCollection("gamesByTag", (collection) =>
|
||||||
|
collectionByTag(collection, "game"),
|
||||||
|
);
|
||||||
|
eleventyConfig.addCollection("showsByTag", (collection) =>
|
||||||
|
collectionByTag(collection, "tv"),
|
||||||
|
);
|
||||||
|
eleventyConfig.addCollection("moviesByTag", (collection) =>
|
||||||
|
collectionByTag(collection, "movie"),
|
||||||
|
);
|
||||||
|
|
||||||
// --------------------- Custom Filters -----------------------
|
// --------------------- Custom Filters -----------------------
|
||||||
eleventyConfig.addFilter("allTagCounts", allTagCounts);
|
eleventyConfig.addFilter("allTagCounts", allTagCounts);
|
||||||
eleventyConfig.addFilter("entries", entries);
|
eleventyConfig.addFilter("entries", entries);
|
||||||
eleventyConfig.addFilter("filter", filter);
|
eleventyConfig.addFilter("filter", filter);
|
||||||
eleventyConfig.addFilter("filterCatalogueTags", filterCatalogueTags);
|
|
||||||
eleventyConfig.addFilter("filterFavourites", filterFavourites);
|
eleventyConfig.addFilter("filterFavourites", filterFavourites);
|
||||||
eleventyConfig.addFilter("formatDate", formatDate);
|
eleventyConfig.addFilter("formatDate", formatDate);
|
||||||
eleventyConfig.addFilter("isOld", isOld);
|
eleventyConfig.addFilter("isOld", isOld);
|
||||||
|
@ -50,7 +60,26 @@ module.exports = (eleventyConfig) => {
|
||||||
eleventyConfig.addFilter("pluralize", pluralize);
|
eleventyConfig.addFilter("pluralize", pluralize);
|
||||||
|
|
||||||
// --------------------- Custom Transforms -----------------------
|
// --------------------- Custom Transforms -----------------------
|
||||||
eleventyConfig.addPlugin(require("./config/transforms/html-config.js"));
|
eleventyConfig.addPlugin(htmlConfigTransform);
|
||||||
|
|
||||||
|
// Image Transforms
|
||||||
|
// Works with any <img> tag in output files.
|
||||||
|
eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
|
||||||
|
extensions: "html",
|
||||||
|
|
||||||
|
formats: ["webp", "jpeg"],
|
||||||
|
|
||||||
|
sharpOptions: {
|
||||||
|
animated: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultAttributes: {
|
||||||
|
loading: "lazy",
|
||||||
|
decoding: "async",
|
||||||
|
},
|
||||||
|
|
||||||
|
failOnError: false,
|
||||||
|
});
|
||||||
|
|
||||||
// --------------------- Passthrough File Copy -----------------------
|
// --------------------- Passthrough File Copy -----------------------
|
||||||
["src/assets/fonts/", "src/assets/images"].forEach((path) =>
|
["src/assets/fonts/", "src/assets/images"].forEach((path) =>
|
||||||
|
@ -62,9 +91,12 @@ module.exports = (eleventyConfig) => {
|
||||||
eleventyConfig.setLibrary("md", markdown);
|
eleventyConfig.setLibrary("md", markdown);
|
||||||
|
|
||||||
// --------------------- Shortcodes -----------------------
|
// --------------------- Shortcodes -----------------------
|
||||||
eleventyConfig.addShortcode("image", imageShortcode);
|
|
||||||
eleventyConfig.addShortcode("youtube", liteYoutube);
|
eleventyConfig.addShortcode("youtube", liteYoutube);
|
||||||
|
|
||||||
|
eleventyConfig.addFilter("console", function (value) {
|
||||||
|
return util.inspect(value);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Optional (default is set): If your site deploys to a subdirectory, change `pathPrefix`, for example with with GitHub pages
|
// Optional (default is set): If your site deploys to a subdirectory, change `pathPrefix`, for example with with GitHub pages
|
||||||
pathPrefix: "/",
|
pathPrefix: "/",
|
||||||
|
@ -75,4 +107,4 @@ module.exports = (eleventyConfig) => {
|
||||||
|
|
||||||
dir,
|
dir,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
5868
package-lock.json
generated
22
package.json
|
@ -5,38 +5,34 @@
|
||||||
"repository": "https://github.com/wonderfulfrog/wonderfulfrog.com",
|
"repository": "https://github.com/wonderfulfrog/wonderfulfrog.com",
|
||||||
"author": "Devin Haska",
|
"author": "Devin Haska",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"debug": "DEBUG=Eleventy* eleventy --serve --watch",
|
"debug": "DEBUG=Eleventy* npx @11ty/eleventy",
|
||||||
"dev": "eleventy --serve --watch --quiet",
|
"start": "npx @11ty/eleventy --serve --quiet",
|
||||||
"lint": "eslint src/ config/",
|
"build": "npx @11ty/eleventy"
|
||||||
"build": "eleventy"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^2.0.1",
|
"@11ty/eleventy": "^3.0.0",
|
||||||
"@11ty/eleventy-fetch": "^4.0.0",
|
"@11ty/eleventy-fetch": "^4.0.0",
|
||||||
"@11ty/eleventy-img": "^3.1.8",
|
"@11ty/eleventy-img": "^5.0.0",
|
||||||
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
||||||
"@netlify/functions": "^2.6.0",
|
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"cssnano": "^6.0.3",
|
"cssnano": "^6.0.3",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"eleventy-plugin-no-robots": "^1.0.1",
|
"eleventy-plugin-no-robots": "^1.0.1",
|
||||||
"eslint": "8.56.0",
|
|
||||||
"eslint-config-prettier": "9.1.0",
|
|
||||||
"eslint-plugin-prettier": "5.1.3",
|
|
||||||
"html-minifier-terser": "^7.2.0",
|
"html-minifier-terser": "^7.2.0",
|
||||||
"markdown-it": "^14.0.0",
|
"markdown-it": "^14.0.0",
|
||||||
"markdown-it-abbr": "^2.0.0",
|
"markdown-it-abbr": "^2.0.0",
|
||||||
"markdown-it-anchor": "^8.6.7",
|
"markdown-it-anchor": "^8.6.7",
|
||||||
|
"markdown-it-attrs": "^4.3.1",
|
||||||
"markdown-it-footnote": "^4.0.0",
|
"markdown-it-footnote": "^4.0.0",
|
||||||
|
"markdown-it-image-figures": "^2.1.1",
|
||||||
"markdown-it-prism": "^2.3.0",
|
"markdown-it-prism": "^2.3.0",
|
||||||
"netlify-plugin-cache": "^1.0.3",
|
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"postcss": "^8.4.33",
|
"postcss": "^8.4.33",
|
||||||
"postcss-import": "^16.0.0",
|
"postcss-import": "^16.0.0",
|
||||||
"postcss-import-ext-glob": "^2.1.1",
|
"postcss-import-ext-glob": "^2.1.1"
|
||||||
"prettier": "3.2.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,4 +87,4 @@ const blogroll = [
|
||||||
|
|
||||||
const sortedBlogroll = blogroll.sort((a, b) => a.title.localeCompare(b.title));
|
const sortedBlogroll = blogroll.sort((a, b) => a.title.localeCompare(b.title));
|
||||||
|
|
||||||
module.exports = sortedBlogroll;
|
export default sortedBlogroll;
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
require("dotenv").config();
|
|
||||||
|
|
||||||
const EleventyFetch = require("@11ty/eleventy-fetch");
|
|
||||||
const dayjs = require("dayjs");
|
|
||||||
const utc = require("dayjs/plugin/utc");
|
|
||||||
const relativeTime = require("dayjs/plugin/relativeTime");
|
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(relativeTime);
|
|
||||||
|
|
||||||
const lastFmApiKey = process.env.LAST_FM_API_KEY;
|
|
||||||
|
|
||||||
const baseUrl = "http://ws.audioscrobbler.com";
|
|
||||||
const username = "wonderfulfrog";
|
|
||||||
|
|
||||||
const fetchLastFm = async (method, duration, extraArgs) => {
|
|
||||||
try {
|
|
||||||
const path = `/2.0/?method=${method}&user=${username}&api_key=${lastFmApiKey}&format=json`;
|
|
||||||
let url = `${baseUrl}${path}`;
|
|
||||||
|
|
||||||
if (extraArgs) {
|
|
||||||
url = `${url}&${extraArgs}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await EleventyFetch(url, { duration, type: "json" });
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error fetching last.fm data for method=${method}`, e);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchRecentAlbums = async (period = "7day") => {
|
|
||||||
const response = await fetchLastFm(
|
|
||||||
"user.gettopalbums",
|
|
||||||
"7d",
|
|
||||||
`period=${period}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const albums = response.topalbums.album.slice(0, 8);
|
|
||||||
|
|
||||||
const recentAlbums = albums.map((album) => {
|
|
||||||
const extraLargeImage = album.image.find(
|
|
||||||
(img) => img.size === "extralarge",
|
|
||||||
);
|
|
||||||
const imageUrl = extraLargeImage ? extraLargeImage["#text"] : "";
|
|
||||||
return {
|
|
||||||
artist: album.artist.name,
|
|
||||||
artistMbid: album.artist.mbid,
|
|
||||||
album: album.name,
|
|
||||||
albumMbid: album.mbid,
|
|
||||||
playcount: album.playcount,
|
|
||||||
url: album.url,
|
|
||||||
imageUrl,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return recentAlbums;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchRecentTracks = async () => {
|
|
||||||
const response = await fetchLastFm("user.getrecenttracks", "5m");
|
|
||||||
|
|
||||||
if (!response) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const tracks = response.recenttracks.track.slice(0, 5);
|
|
||||||
|
|
||||||
const recentTracks = tracks.map((track) => {
|
|
||||||
const timestamp = track.date
|
|
||||||
? dayjs(track.date["#text"]).utc(true).fromNow()
|
|
||||||
: dayjs().fromNow();
|
|
||||||
return {
|
|
||||||
artist: track.artist["#text"],
|
|
||||||
track: track.name,
|
|
||||||
url: track.url,
|
|
||||||
timestamp,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return recentTracks;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = async function () {
|
|
||||||
const recentTracks = await fetchRecentTracks();
|
|
||||||
const recentAlbums = await fetchRecentAlbums();
|
|
||||||
|
|
||||||
return {
|
|
||||||
recentTracks,
|
|
||||||
recentAlbums,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,10 +1,10 @@
|
||||||
require("dotenv").config();
|
import "dotenv/config";
|
||||||
|
|
||||||
const EleventyFetch = require("@11ty/eleventy-fetch");
|
import EleventyFetch from "@11ty/eleventy-fetch";
|
||||||
const cheerio = require("cheerio");
|
import cheerio from "cheerio";
|
||||||
const dayjs = require("dayjs");
|
import dayjs from "dayjs";
|
||||||
const utc = require("dayjs/plugin/utc");
|
import utc from "dayjs/plugin/utc.js";
|
||||||
const relativeTime = require("dayjs/plugin/relativeTime");
|
import relativeTime from "dayjs/plugin/relativeTime.js";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
@ -33,7 +33,7 @@ const fetchRecentMovies = async () => {
|
||||||
|
|
||||||
$("channel")
|
$("channel")
|
||||||
.children("item")
|
.children("item")
|
||||||
.slice(0, 6)
|
.slice(0, 5)
|
||||||
.each((_, element) => {
|
.each((_, element) => {
|
||||||
const title = $(element).children("letterboxd\\:filmTitle").text();
|
const title = $(element).children("letterboxd\\:filmTitle").text();
|
||||||
|
|
||||||
|
@ -65,4 +65,4 @@ const fetchRecentMovies = async () => {
|
||||||
return recentMovies;
|
return recentMovies;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = fetchRecentMovies;
|
export default fetchRecentMovies;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module.exports = {
|
export default {
|
||||||
url: process.env.URL || "http://localhost:8080",
|
url: process.env.URL || "http://localhost:8080",
|
||||||
siteName: "wonderfulfrog",
|
siteName: "wonderfulfrog",
|
||||||
siteDescription:
|
siteDescription:
|
||||||
|
@ -7,9 +7,4 @@ module.exports = {
|
||||||
lang: "en",
|
lang: "en",
|
||||||
author: "Devin Haska",
|
author: "Devin Haska",
|
||||||
repoUrl: "https://github.com/wonderfulfrog/wonderfulfrog.com",
|
repoUrl: "https://github.com/wonderfulfrog/wonderfulfrog.com",
|
||||||
social: {
|
|
||||||
github: "https://github.com/wonderfulfrog",
|
|
||||||
mastodon: "https://mastodon.social/@wonderfulfrog",
|
|
||||||
lastfm: "https://www.last.fm/user/wonderfulfrog",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,24 +1,41 @@
|
||||||
module.exports = {
|
export default {
|
||||||
top: [
|
top: [
|
||||||
{
|
{
|
||||||
text: "About",
|
text: "About",
|
||||||
url: "/about",
|
url: "/about",
|
||||||
|
icon: "circle-info",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "Posts",
|
text: "Posts",
|
||||||
url: "/posts",
|
url: "/posts",
|
||||||
|
icon: "list",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "Catalogue",
|
text: "Games",
|
||||||
url: "/catalogue",
|
url: "/games",
|
||||||
|
icon: "game-controller",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Watching",
|
||||||
|
url: "/watching",
|
||||||
|
icon: "tv-retro",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Books",
|
||||||
|
url: "/books",
|
||||||
|
icon: "book",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
bottom: [
|
bottom: [
|
||||||
|
{ text: "Blogroll", url: "/blogroll" },
|
||||||
{
|
{
|
||||||
text: "Colophon",
|
text: "Colophon",
|
||||||
url: "/colophon",
|
url: "/colophon",
|
||||||
},
|
},
|
||||||
{ text: "Blogroll", url: "/blogroll" },
|
{
|
||||||
|
text: "Contact",
|
||||||
|
url: "/contact",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: "/uses",
|
text: "/uses",
|
||||||
url: "/uses",
|
url: "/uses",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const { getFontUrl } = require("../utils/fonts");
|
import { getFontUrl } from "../utils/fonts.js";
|
||||||
const fonts = require("../../config/design-tokens/fonts.json");
|
import fonts from "../../config/design-tokens/fonts.js";
|
||||||
|
|
||||||
const preloads = [
|
const preloads = [
|
||||||
{
|
{
|
||||||
|
@ -19,4 +19,4 @@ const preloads = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = preloads;
|
export default preloads;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
require("dotenv").config();
|
import "dotenv/config";
|
||||||
|
|
||||||
const EleventyFetch = require("@11ty/eleventy-fetch");
|
import EleventyFetch from "@11ty/eleventy-fetch";
|
||||||
|
|
||||||
const accessToken = process.env.DARK_VISITORS_ACCESS_TOKEN;
|
const accessToken = process.env.DARK_VISITORS_ACCESS_TOKEN;
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ const fetchRobotsTxt = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = async function () {
|
export default async function () {
|
||||||
const robotsTxt = await fetchRobotsTxt();
|
const robotsTxt = await fetchRobotsTxt();
|
||||||
|
|
||||||
if (!robotsTxt) {
|
if (!robotsTxt) {
|
||||||
|
@ -109,4 +109,4 @@ module.exports = async function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
return robotsTxt;
|
return robotsTxt;
|
||||||
};
|
}
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
src="https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@1.4.0/lite-youtube.min.js"></script>
|
src="https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@1.4.0/lite-youtube.min.js"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body class="[ flex-col ]">
|
<body class="flex-col">
|
||||||
{% noRobots %}
|
{% noRobots %}
|
||||||
{% include "partials/header.html" %}
|
{% include "partials/header.html" %}
|
||||||
<main id="main" class="[ flow flex-1 wrapper ]" tabindex="-1">
|
<main id="main" class="flow flex-1 wrapper" tabindex="-1">
|
||||||
{{ content | safe }}
|
{{ content | safe }}
|
||||||
</main>
|
</main>
|
||||||
{% include "partials/footer.html" %}
|
{% include "partials/footer.html" %}
|
||||||
|
|
42
src/_includes/layouts/book.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
layout: "layouts/base"
|
||||||
|
---
|
||||||
|
|
||||||
|
{% from "macros/date.njk" import format %}
|
||||||
|
{% from "macros/utils.njk" import stars %}
|
||||||
|
{% from "macros/tags.njk" import tagList %}
|
||||||
|
<header class="flow flow-space-1">
|
||||||
|
{{ format(page.date) }}
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
{% if pullquote %}<p class="text-fadeText flow-space-0.25">{{ pullquote }}</p>{% endif %}
|
||||||
|
</header>
|
||||||
|
{% if content %}
|
||||||
|
<section class="flow">
|
||||||
|
{{ content | safe }}
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
<hr class="my-2" />
|
||||||
|
<footer class="media-meta-grid gap-1">
|
||||||
|
<div class="media-image media-image--tall">
|
||||||
|
<img src="{{ image }}" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="flow flex-col justify-center">
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
{% if subtitle %}<p class="text-fadeText flow-space-0.5 line-height-m">{{ subtitle }}</p>{% endif %}
|
||||||
|
{% if rating %}{{ stars(rating) }}{% endif %}
|
||||||
|
<ul class="list-none p-0 mb-0 media-meta gap-0.5">
|
||||||
|
{% if author %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Author</strong><span>{{ author }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if year %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Published</strong><span>{{ year }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<hr class="my-2">
|
||||||
|
{{ tagList(tags | filter("book") , "/books") }}
|
|
@ -1,44 +0,0 @@
|
||||||
---
|
|
||||||
layout: "layouts/base"
|
|
||||||
imageAlt: ""
|
|
||||||
imageCaption: ""
|
|
||||||
---
|
|
||||||
|
|
||||||
{% set filteredTags = tags | filterCatalogueTags %}
|
|
||||||
{% from "macros/date.njk" import format %}
|
|
||||||
{% from "macros/utils.njk" import stars %}
|
|
||||||
|
|
||||||
<article class="[ catalogue ] [ flow ]">
|
|
||||||
<header class="[ catalogue-header ] [ flow flow-space-1 ]">
|
|
||||||
{{ format(page.date) }}
|
|
||||||
<h1>{{ title }}</h1>
|
|
||||||
{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}
|
|
||||||
{% if rating %}<div class="[ text-skew ]">{{ stars(rating) }}</div>{% endif %}
|
|
||||||
{% if tertiary or year %}
|
|
||||||
<div class="[ cluster text-skew flow-space-1 ]">
|
|
||||||
{% if tertiary %}
|
|
||||||
<div class="[ line-height-m ]">
|
|
||||||
{{ tertiary | safe }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if year %}
|
|
||||||
<p class="[ font-size-s ]"><span class="[ text-fadeText ]">ca.</span> {{ year }}</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if filteredTags | length > 0 %}
|
|
||||||
<ul class="[ categories ] [ cluster p-0 flow-space-2 line-height-m ]" role="list">
|
|
||||||
{% for tag in filteredTags %}<li class="[ flex gap-0.25 ]">{{ tag }}</li>{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
</header>
|
|
||||||
{% if image %}
|
|
||||||
{% image image, imageAlt, imageCaption, "[ my-3 ]" %}
|
|
||||||
{% endif %}
|
|
||||||
{{ content | safe }}
|
|
||||||
{% if url %}
|
|
||||||
<a href="{{ url }}" class="[ flex mt-1 items-center gap-0.5 ]" target="_blank" rel="external noreferrer noopener">
|
|
||||||
{% include "svgs/link.svg" %}{{ linkTitle }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</article>
|
|
49
src/_includes/layouts/game.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
layout: "layouts/base"
|
||||||
|
---
|
||||||
|
|
||||||
|
{% from "macros/date.njk" import format %}
|
||||||
|
{% from "macros/utils.njk" import stars %}
|
||||||
|
{% from "macros/tags.njk" import tagList %}
|
||||||
|
<header class="flow flow-space-1">
|
||||||
|
{{ format(page.date) }}
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<p class="text-fadeText flow-space-0.25">{{ pullquote }}</p>
|
||||||
|
</header>
|
||||||
|
{% if content %}
|
||||||
|
<section class="flow">
|
||||||
|
{{ content | safe }}
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
<hr class="my-2" />
|
||||||
|
<footer class="media-meta-grid gap-1">
|
||||||
|
<div class="media-image media-image--tall">
|
||||||
|
<img src="{{ image }}" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="flow flex-col justify-center">
|
||||||
|
<div class="flex items-center gap-0.5 flex-wrap">
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
</div>
|
||||||
|
{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}
|
||||||
|
{% if rating %}{{ stars(rating) }}{% endif %}
|
||||||
|
<ul class="list-none p-0 mb-0 media-meta gap-0.5">
|
||||||
|
{% if year %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Released</strong><span>{{ year }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if platform %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Platform</strong><span>{{ platform }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if playtime %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Playtime</strong><span>{{ playtime }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<hr class="my-2">
|
||||||
|
{{ tagList(tags | filter("game") , "/games") }}
|
61
src/_includes/layouts/movie.html
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
layout: "layouts/base"
|
||||||
|
---
|
||||||
|
|
||||||
|
{% from "macros/date.njk" import format %}
|
||||||
|
{% from "macros/utils.njk" import stars %}
|
||||||
|
{% from "macros/tags.njk" import tagList %}
|
||||||
|
<header class="flow flow-space-1">
|
||||||
|
{{ format(page.date) }}
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<p class="text-fadeText flow-space-0.25">{{ pullquote }}</p>
|
||||||
|
</header>
|
||||||
|
{% if watchHistory.length > 1 %}
|
||||||
|
<p>
|
||||||
|
<span class="text-primary">{% include "svgs/circle-info.svg" %}</span>
|
||||||
|
I've seen this movie <strong>{{ watchHistory.length }}</strong> {{ "time" | pluralize(watchHistory) }}!
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if favourite %}
|
||||||
|
<p>
|
||||||
|
<span class="text-secondary">{% include "svgs/star.svg" %}</span>
|
||||||
|
This is one of my favourite movies!
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if content %}
|
||||||
|
<section class="flow">
|
||||||
|
{{ content | safe }}
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
<hr class="my-2" />
|
||||||
|
<footer class="media-meta-grid gap-1">
|
||||||
|
<div class="media-image media-image--tall">
|
||||||
|
<img src="{{ image }}" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="flow flex-col justify-center">
|
||||||
|
<div class="flex items-center gap-0.5 flex-wrap">
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
</div>
|
||||||
|
{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}
|
||||||
|
{% if rating %}{{ stars(rating) }}{% endif %}
|
||||||
|
<ul class="list-none p-0 mb-0 media-meta gap-0.5">
|
||||||
|
{% if year %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Released</strong><span>{{ year }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if director %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Director</strong>{{ director }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if runtime %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Runtime</strong><span>{{ runtime }} mins</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<hr class="my-2">
|
||||||
|
{{ tagList(tags | filter("movie") , "/watching/movies") }}
|
|
@ -3,15 +3,17 @@ layout: "layouts/base"
|
||||||
---
|
---
|
||||||
|
|
||||||
{% from "macros/date.njk" import format %}
|
{% from "macros/date.njk" import format %}
|
||||||
<article class="[ flow ]">
|
<article class="flow">
|
||||||
<header class="[ flow flow-space-1 mb-2 ]">
|
<header class="flow flow-space-1 mb-2">
|
||||||
{{ format(page.date) }}
|
{{ format(page.date) }}
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<p class="[ text-skew text-fadeText flow-space-0.25 ]">{{ excerpt }}</p>
|
<p class="text-fadeText flow-space-0.25">{{ excerpt }}</p>
|
||||||
<ul class="[ categories ] [ cluster list-none p-0 flow-space-2 line-height-m ]">
|
<ul class="categories cluster list-none p-0 flow-space-2 line-height-m">
|
||||||
{% for tag in tags | filter(["post"]) %}
|
{% for tag in tags | filter(["post"]) %}
|
||||||
<li class="[ flex gap-0.25 ]">
|
<li>
|
||||||
<a href="/tags/{{ tag | slugify }}">{{ tag }}</a>
|
<a class="button" href="/tags/{{ tag | slugify }}">
|
||||||
|
{% include "svgs/frame.svg" %}
|
||||||
|
{{ tag }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
56
src/_includes/layouts/show.html
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
---
|
||||||
|
layout: "layouts/base"
|
||||||
|
---
|
||||||
|
|
||||||
|
{% from "macros/date.njk" import format %}
|
||||||
|
{% from "macros/utils.njk" import stars %}
|
||||||
|
{% from "macros/tags.njk" import tagList %}
|
||||||
|
<header class="flow flow-space-1">
|
||||||
|
{{ format(page.date) }}
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
<p class="text-fadeText flow-space-0.25">{{ pullquote }}</p>
|
||||||
|
</header>
|
||||||
|
{% if content %}
|
||||||
|
<section class="flow">
|
||||||
|
{{ content | safe }}
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
{% if favourite or isFavourite %}
|
||||||
|
<p>
|
||||||
|
<span class="text-secondary">{% include "svgs/star.svg" %}</span>
|
||||||
|
This is one of my favourite shows!
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<hr class="my-2" />
|
||||||
|
<footer class="media-meta-grid gap-1">
|
||||||
|
<div class="media-image media-image--tall">
|
||||||
|
<img src="{{ image }}" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="flow flex-col justify-center">
|
||||||
|
<div class="flex items-center gap-0.5 flex-wrap">
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
</div>
|
||||||
|
{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}
|
||||||
|
{% if director %}
|
||||||
|
<p class="flex gap-0.5">
|
||||||
|
<span class="text-fadeText">Directed by</span>{{ director }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if watchHistory %}<p class="flow-space-0.25">{{ format(watchHistory | last) }}</p>{% endif %}
|
||||||
|
{% if rating %}{{ stars(rating) }}{% endif %}
|
||||||
|
<ul class="list-none p-0 mb-0 media-meta gap-0.5">
|
||||||
|
{% if year %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Released</strong><span>{{ year }}</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if runtime %}
|
||||||
|
<li class="flex-col">
|
||||||
|
<strong>Runtime</strong><span>{{ runtime }} mins</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<hr class="my-2">
|
||||||
|
{{ tagList(tags | filter("tv") , "/watching/shows") }}
|
|
@ -1,36 +0,0 @@
|
||||||
{% from "macros/utils.njk" import stars %}
|
|
||||||
|
|
||||||
{% macro one(post, format, showType = true) %}
|
|
||||||
<article class="[ posts-list-item column-gap-0.5 justify-between line-height-m ]">
|
|
||||||
<div class="[ cluster gap-0.5 ]"><a href="{{ post.url }}" class="[ line-height-m ]">{{ post.data.title }}</a>
|
|
||||||
{% if showType %}<p class="[ font-size-s text-fadeText ]">{{ post.data.tags[1] }}</p>{% endif %}</div>
|
|
||||||
<div class="[ cluster ]">
|
|
||||||
{% if post.data.rating %}
|
|
||||||
{{ stars(post.data.rating) }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro list(posts, showType = true) %}
|
|
||||||
<ol class="[ flow p-0 ]" role="list">
|
|
||||||
{% for post in posts %}
|
|
||||||
<li>
|
|
||||||
{{ one(post, format, showType) }}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ol>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro yearList(posts, year, format = "MM/DD", showType = true) %}
|
|
||||||
<section class="[ flow ]">
|
|
||||||
<header class="[ cluster gap-0.5 ]">
|
|
||||||
<h2>{{ year }}</h2>
|
|
||||||
<p class="[ pill ]" data-state="extrasmall">
|
|
||||||
{{ posts | length }}
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
{{ list(posts, format) }}
|
|
||||||
</section>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% macro format(dateString) %}
|
{% macro format(dateString) %}
|
||||||
<time class="[ date ] [ flex items-center gap-0.5 text-fadeText ]" datetime="{{ date }}">
|
<time class="date flex items-center gap-0.5 text-fadeText"
|
||||||
<span class="[ text-primary line-height-s ]">{% include "svgs/calendar.svg" %}</span>{{ dateString | formatDate("MMMM Do YYYY") }}
|
datetime="{{ dateString }}">
|
||||||
|
<span class="text-primary line-height-s">{% include "svgs/calendar.svg" %}</span>{{ dateString | formatDate("MMMM Do YYYY") }}
|
||||||
</time>
|
</time>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
14
src/_includes/macros/media-grid.njk
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% macro grid(data, shape = "vertical") %}
|
||||||
|
<ul class="media-grid {{ shape }} list-none p-0">
|
||||||
|
{% for item in data %}
|
||||||
|
<li class="radius-0.5">
|
||||||
|
<a href="{{ item.url }}">
|
||||||
|
{% if item.data.image %}<img src="{{ item.data.image }}" alt="" />{% endif %}
|
||||||
|
<div class="meta font-size-s line-height-s flex items-end px-0.5 pb-0.5">
|
||||||
|
<span class="meta-text">{{ item.data.title }}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endmacro %}
|
|
@ -1,29 +1,21 @@
|
||||||
{% macro one(post, format = "MM/DD") %}
|
{% macro one(post, fmt = "MM/DD") %}
|
||||||
<article class="[ posts-list-item column-gap-0.5 justify-between line-height-l ]">
|
<article class="posts-list-item column-gap-0.5 justify-between line-height-l">
|
||||||
<a href="{{ post.url }}">{{ post.data.title }}</a>
|
<a href="{{ post.url }}">{{ post.data.title }}</a>
|
||||||
<time datetime="{{ post.date }}" class="[ text-fadeText ]">{{ post.date | formatDate(format) }}</time>
|
<time datetime="{{ post.date }}" class="text-fadeText">{{ post.date | formatDate(fmt) }}</time>
|
||||||
<p class="[ text-fadeText font-size-s line-height-m ]">{{ post.data.excerpt }}</p>
|
<p class="text-fadeText font-size-s line-height-m">{{ post.data.excerpt }}</p>
|
||||||
</article>
|
</article>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro list(posts, format = "MM/DD") %}
|
{% macro list(posts, format = "MM/DD") %}
|
||||||
<ol class="[ flow p-0 ]" role="list">
|
<ol class="flow p-0" role="list">
|
||||||
{% for post in posts %}
|
{% for post in posts %}<li class="flow-space-0.5">{{ one(post, format) }}</li>{% endfor %}
|
||||||
<li class="[ flow-space-0.5 ]">
|
</ol>
|
||||||
{{ one(post, format) }}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ol>
|
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro yearList(posts, year, format = "MM/DD") %}
|
{% macro yearList(posts, year, format = "MM/DD") %}
|
||||||
<section class="[ flow ]">
|
<section class="flow">
|
||||||
<header class="[ cluster gap-0.5 ]">
|
<header class="cluster gap-0.5">
|
||||||
<h2>{{ year }}</h2>
|
<h2>{{ year }}</h2>
|
||||||
<p class="[ pill ]" data-state="extrasmall">
|
<p class="pill">{{ posts | length }}</p>
|
||||||
{{ posts | length }}
|
</header>
|
||||||
</p>
|
{{ list(posts, format) }}
|
||||||
</header>
|
</section>
|
||||||
{{ list(posts, format) }}
|
|
||||||
</section>
|
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
11
src/_includes/macros/tags.njk
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{% macro tagList(tags, urlPrefix) %}
|
||||||
|
<ul class="categories cluster list-none p-0 line-height-m">
|
||||||
|
{% for tag in tags %}
|
||||||
|
<li>
|
||||||
|
<a class="button" href="{{ urlPrefix }}/tag/{{ tag | slugify }}">
|
||||||
|
{% include "svgs/frame.svg" %}
|
||||||
|
{{ tag }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endmacro %}
|
|
@ -1,12 +1,13 @@
|
||||||
{% macro stars(number) %}
|
{% macro stars(number) %}
|
||||||
{% set filledStars = number %}
|
{% set filledStars = number %}
|
||||||
{% set emptyStars = 5 - number %}
|
{% set emptyStars = 5 - number %}
|
||||||
<ul class="[ stars ] [ flex list-none p-0 m-0 text-primary ]" aria-description="{{ number }} out of 5 stars">
|
<ul class="stars flex list-none p-0 mb-0 text-primary"
|
||||||
{% for i in range(0, filledStars) %}
|
aria-description="{{ number }} out of 5 stars">
|
||||||
<li class="[ star-filled ]">{% include "svgs/star.svg" %}</li>
|
{% for i in range(0, filledStars) %}
|
||||||
{% endfor %}
|
<li class="star-filled">{% include "svgs/star.svg" %}</li>
|
||||||
{% for i in range(0, emptyStars) %}
|
{% endfor %}
|
||||||
<li class="[ star-empty ]">{% include "svgs/star-empty.svg" %}</li>
|
{% for i in range(0, emptyStars) %}
|
||||||
{% endfor %}
|
<li class="star-empty">{% include "svgs/star-empty.svg" %}</li>
|
||||||
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% set catalogueTypes = collections.catalogueByType | keys %}
|
|
||||||
<ul class="[ cluster p-0 gap-0.5 ]" role="list">
|
|
||||||
<li>
|
|
||||||
<a class="[ pill ]" href="/catalogue">all<span class="[ pill-count ]">{{ collections.catalogue | length }}</span></a>
|
|
||||||
</li>
|
|
||||||
{% for type in catalogueTypes %}
|
|
||||||
<li>
|
|
||||||
<a class="[ pill ]" href="/catalogue/{{ type | pluralize }}">{{ type | pluralize }}<span class="[ pill-count ]">{{ collections.catalogueByType[ type ] | length }}</span></a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
|
@ -1,9 +1,9 @@
|
||||||
<footer class="[ bg-surface mt-1.5 py-1.5 ]">
|
<footer class="bg-surface mt-1.5 py-1.5">
|
||||||
<div class="[ wrapper flow ]">
|
<div class="wrapper flow">
|
||||||
<div class="[ repel ]">
|
<div class="repel">
|
||||||
<p>© {{ meta.author }} 2018 - 2025.</p>
|
<p>© {{ meta.author }} 2018 - 2025.</p>
|
||||||
<nav>
|
<nav>
|
||||||
<ul class="[ cluster p-0 m-0 line-height-m ]" role="list">
|
<ul class="cluster p-0 m-0 line-height-m" role="list">
|
||||||
{% for link in navigation.bottom %}
|
{% for link in navigation.bottom %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ link.url }}">{{ link.text }}</a>
|
<a href="{{ link.url }}">{{ link.text }}</a>
|
||||||
|
@ -12,33 +12,40 @@
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="[ repel ]">
|
<div class="repel">
|
||||||
<nav class="[ flow-space-0.5 ]">
|
<nav class="flow-space-0.5">
|
||||||
<ul class="[ menu ] [ cluster p-0 m-0 gap-0.5 ]" role="list">
|
<ul class="cluster p-0 m-0 gap-0.5" role="list">
|
||||||
<li>
|
<li>
|
||||||
<span class="[ visually-hidden ]">Feeds</span>
|
<a href="/feeds"
|
||||||
<a href="/feeds" class="[ button ]" aria-label="View RSS feed">{% include "svgs/rss.svg" %}</a>
|
class="button button--alt"
|
||||||
|
aria-label="Feeds"
|
||||||
|
title="Feeds">{% include "svgs/rss.svg" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/wonderfulfrog"
|
||||||
|
class="button button--alt"
|
||||||
|
target="_blank"
|
||||||
|
rel="me external noreferrer noopener"
|
||||||
|
title="GitHub"
|
||||||
|
aria-label="GitHub">{% include "svgs/social-github.svg" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://mastodon.social/@wonderfulfrog"
|
||||||
|
class="button button--alt"
|
||||||
|
target="_blank"
|
||||||
|
rel="me external noreferrer noopener"
|
||||||
|
title="Mastodon"
|
||||||
|
aria-label="Mastodon">{% include "svgs/social-mastodon.svg" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% for key, url in meta.social %}
|
|
||||||
{% set alt = key | capitalize %}
|
|
||||||
<li>
|
|
||||||
<span class="[ visually-hidden ]">{{ alt }}</span>
|
|
||||||
<a href="{{ url }}"
|
|
||||||
class="[ button ]"
|
|
||||||
target="_blank"
|
|
||||||
rel="me external noreferrer noopener"
|
|
||||||
aria-label="View {{ alt }} page">{% include "svgs/social-" + key + ".svg" %}</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<ul class="[ cluster p-0 m-0 ]" role="list">
|
<ul class="cluster p-0 m-0" role="list">
|
||||||
<li>
|
|
||||||
<a href="/webrings">Webrings</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<a href="/changelog">Changelog</a>
|
<a href="/changelog">Changelog</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/webrings">Webrings</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ meta.repoUrl }}"
|
<a href="{{ meta.repoUrl }}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
<header class="[ navbar ][ py-1 mb-2 ]">
|
<header class="navbar py-1 mb-2">
|
||||||
<a href="#main">Skip to content</a>
|
<a href="#main">Skip to content</a>
|
||||||
<div class="[ wrapper ]">
|
<div class="wrapper">
|
||||||
<nav class="[ repel ]">
|
<nav class="repel">
|
||||||
<div class="[ site-logo ] [ flex items-center gap-0.5 ]">
|
<div class="site-logo flex items-center gap-0.5">
|
||||||
<a class="[ logo ] [ flex items-center justify-center ]"
|
<a class="logo flex items-center justify-center"
|
||||||
aria-label="Go to index"
|
aria-label="Go to index"
|
||||||
href="{{ meta.url }}">
|
href="{{ meta.url }}">
|
||||||
<img src="/assets/images/logo.svg" width="32" height="32" alt="" />
|
<img eleventy:ignore
|
||||||
|
src="/assets/images/logo.svg"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
alt="" />
|
||||||
</a>
|
</a>
|
||||||
<a class="[ wordmark ] [ flex ]"
|
<a class="wordmark flex" href="{{ meta.url }}" aria-label="Go to index">
|
||||||
href="{{ meta.url }}"
|
<svg height="0" width="0">
|
||||||
aria-label="Go to index">
|
<filter id="3d_text" color-interpolation-filters="sRGB">
|
||||||
|
<feDropShadow dx="0" dy="1" stdDeviation="0" flood-opacity="1" flood-color="var(--color-shadow)" />
|
||||||
|
<feDropShadow dx="0" dy="2" stdDeviation="0" flood-opacity="1" flood-color="var(--color-shadow)" />
|
||||||
|
</filter>
|
||||||
|
</svg>
|
||||||
<span>wonderful</span>
|
<span>wonderful</span>
|
||||||
<span>frog</span>
|
<span>frog</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<ul class="[ menu ] [ list-none cluster p-0 m-0 ]">
|
<ul class="menu list-none cluster p-0 m-0">
|
||||||
{% for item in navigation.top %}
|
{% for item in navigation.top %}
|
||||||
<li>
|
<li>
|
||||||
<a class="[ button ]" href="{{ item.url }}">{{ item.text }}</a>
|
<a class="button button--alt font-size-s line-height-m"
|
||||||
|
href="{{ item.url }}"
|
||||||
|
title="{{ item.text }}"
|
||||||
|
aria-label="{{ item.text }}">{% include "svgs/" + item.icon + ".svg" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
3
src/_includes/svgs/book.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 0C3.34315 0 2 1.34315 2 3V13C2 14.6569 3.34315 16 5 16H14V14H4V12H14V0H5Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 212 B |
3
src/_includes/svgs/circle-info.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM7 8H6V6H9V11H10V13H7V8ZM9 5V3H7V5H9Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 321 B |
3
src/_includes/svgs/frame.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 15V12H10V15H12V12H15V10H12V6H15V4H12V1H10V4H6V1H4V4H1V6H4V10H1V12H4V15H6ZM10 10H6V6H10V10Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 269 B |
3
src/_includes/svgs/game-controller.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 13L3 14L6 11H10L13 14L16 13L15.248 4.7284C15.1076 3.18316 13.812 2 12.2604 2H3.73964C2.18803 2 0.89244 3.18316 0.751964 4.72839L0 13ZM12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6ZM12 8C12 8.55228 11.5523 9 11 9C10.4477 9 10 8.55228 10 8C10 7.44772 10.4477 7 11 7C11.5523 7 12 7.44772 12 8ZM5 8C6.10457 8 7 7.10457 7 6C7 4.89543 6.10457 4 5 4C3.89543 4 3 4.89543 3 6C3 7.10457 3.89543 8 5 8Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 633 B |
10
src/_includes/svgs/list.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 1H1V3H3V1Z" fill="currentColor"/>
|
||||||
|
<path d="M3 5H1V7H3V5Z" fill="currentColor"/>
|
||||||
|
<path d="M1 9H3V11H1V9Z" fill="currentColor"/>
|
||||||
|
<path d="M3 13H1V15H3V13Z" fill="currentColor"/>
|
||||||
|
<path d="M15 1H5V3H15V1Z" fill="currentColor"/>
|
||||||
|
<path d="M15 5H5V7H15V5Z" fill="currentColor"/>
|
||||||
|
<path d="M5 9H15V11H5V9Z" fill="currentColor"/>
|
||||||
|
<path d="M15 13H5V15H15V13Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 486 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg fill="currentColor" width="16" height="16" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M14.131 22.948l-1.172-3.193c0 0-1.912 2.131-4.771 2.131-2.537 0-4.333-2.203-4.333-5.729 0-4.511 2.276-6.125 4.515-6.125 3.224 0 4.245 2.089 5.125 4.772l1.161 3.667c1.161 3.561 3.365 6.421 9.713 6.421 4.548 0 7.631-1.391 7.631-5.068 0-2.968-1.697-4.511-4.844-5.244l-2.344-0.511c-1.624-0.371-2.104-1.032-2.104-2.131 0-1.249 0.985-1.984 2.604-1.984 1.767 0 2.704 0.661 2.865 2.24l3.661-0.444c-0.297-3.301-2.584-4.656-6.323-4.656-3.308 0-6.532 1.251-6.532 5.245 0 2.5 1.204 4.077 4.245 4.807l2.484 0.589c1.865 0.443 2.484 1.224 2.484 2.287 0 1.359-1.323 1.921-3.828 1.921-3.703 0-5.244-1.943-6.124-4.625l-1.204-3.667c-1.541-4.765-4.005-6.531-8.891-6.531-5.287-0.016-8.151 3.385-8.151 9.192 0 5.573 2.864 8.595 8.005 8.595 4.14 0 6.125-1.943 6.125-1.943z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 875 B |
3
src/_includes/svgs/tv-retro.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2.96338L5.75926 0.349182L4.24074 1.65076L6.25437 4H1V15H15V4H9.74563L11.7593 1.65076L10.2407 0.349182L8 2.96338ZM11 6H3V13H11V6Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 307 B |
4
src/_includes/svgs/user.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8 7C9.65685 7 11 5.65685 11 4C11 2.34315 9.65685 1 8 1C6.34315 1 5 2.34315 5 4C5 5.65685 6.34315 7 8 7Z" fill="currentColor"/>
|
||||||
|
<path d="M14 12C14 10.3431 12.6569 9 11 9H5C3.34315 9 2 10.3431 2 12V15H14V12Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 342 B |
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"excludeFromSitemap": true
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
---
|
|
||||||
permalink: /admin/config.yml
|
|
||||||
---
|
|
||||||
|
|
||||||
backend:
|
|
||||||
name: github
|
|
||||||
repo: wonderfulfrog/wonderfulfrog.com
|
|
||||||
branch: main
|
|
||||||
squash_merges: true
|
|
||||||
commit_messages:
|
|
||||||
create: {% raw %}"feat: add {{collection}} `{{slug}}`"{% endraw %}
|
|
||||||
update: {% raw %}"feat: update {{collection}} `{{slug}}`"{% endraw %}
|
|
||||||
delete: {% raw %}"feat: remove {{collection}} `{{slug}}`"{% endraw %}
|
|
||||||
|
|
||||||
media_folder: ""
|
|
||||||
|
|
||||||
publish_mode: editorial_workflow
|
|
||||||
|
|
||||||
collections:
|
|
||||||
- name: "post"
|
|
||||||
label: "Post"
|
|
||||||
folder: "src/posts"
|
|
||||||
create: true
|
|
||||||
slug: "{% raw %}{{year}}-{{month}}-{{day}}-{{slug}}{% endraw %}"
|
|
||||||
fields:
|
|
||||||
- { label: "Title", name: "title", widget: "string" }
|
|
||||||
- { label: "Excerpt", name: "excerpt", widget: "string" }
|
|
||||||
- { label: "Tags", name: "tags", widget: "list" }
|
|
||||||
- { label: "Using YouTube", name: "youtube", widget: "boolean", default: false, hint: "If using the YouTube shortcode, enable this option to append the lite-youtube script." }
|
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
|
||||||
- label: "Page"
|
|
||||||
name: "page"
|
|
||||||
create: false
|
|
||||||
files:
|
|
||||||
{% for page in collections.page %}
|
|
||||||
- label: "{{ page.data.title }}"
|
|
||||||
name: "{{ page.fileSlug }}"
|
|
||||||
file: "{{ page.inputPath | replace("./", "") }}"
|
|
||||||
fields:
|
|
||||||
- { label: "Title", name: "title", widget: "string" }
|
|
||||||
- { label: "Permalink", name: "permalink", widget: "string" }
|
|
||||||
- { label: "Layout", name: "layout", widget: "hidden", default: "layouts/base" }
|
|
||||||
- { label: "Using YouTube", name: "youtube", widget: "boolean", default: false, hint: "If using the YouTube shortcode, enable this option to append the lite-youtube script." }
|
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
|
||||||
{% endfor %}
|
|
||||||
- name: "book"
|
|
||||||
label: "Book"
|
|
||||||
folder: "src/catalogue/books"
|
|
||||||
slug: "{% raw %}{{year}}-{{month}}-{{day}}-{{slug}}{% endraw %}"
|
|
||||||
create: true
|
|
||||||
fields:
|
|
||||||
- { label: "Title", name: "title", widget: "string" }
|
|
||||||
- { label: "Subtitle", name: "subtitle", widget: "string" }
|
|
||||||
- { label: "Author", name: "author", widget: "string" }
|
|
||||||
- { label: "Rating", name: "rating", widget: "number", min: 1, max: 5 }
|
|
||||||
- { label: "Image", name: "image", widget: "string", hint: "A CDN URL."}
|
|
||||||
- { label: "Tags", name: "tags", widget: "list" }
|
|
||||||
- { label: "Using YouTube", name: "youtube", widget: "boolean", default: false, hint: "If using the YouTube shortcode, enable this option to append the lite-youtube script." }
|
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
|
||||||
- name: "game"
|
|
||||||
label: "Game"
|
|
||||||
folder: "src/catalogue/games"
|
|
||||||
slug: "{% raw %}{{year}}-{{month}}-{{day}}-{{slug}}{% endraw %}"
|
|
||||||
create: true
|
|
||||||
fields:
|
|
||||||
- { label: "Title", name: "title", widget: "string" }
|
|
||||||
- { label: "Subtitle", name: "subtitle", widget: "string" }
|
|
||||||
- { label: "Year", name: "year", widget: "string" }
|
|
||||||
- { label: "Rating", name: "rating", widget: "number", min: 1, max: 5 }
|
|
||||||
- { label: "Image", name: "image", widget: "string", hint: "A CDN URL."}
|
|
||||||
- { label: "Tags", name: "tags", widget: "list" }
|
|
||||||
- { label: "Using YouTube", name: "youtube", widget: "boolean", default: false, hint: "If using the YouTube shortcode, enable this option to append the lite-youtube script." }
|
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
|
||||||
- name: "comic"
|
|
||||||
label: "Comic"
|
|
||||||
folder: "src/catalogue/comics"
|
|
||||||
slug: "{% raw %}{{year}}-{{month}}-{{day}}-{{slug}}{% endraw %}"
|
|
||||||
create: true
|
|
||||||
fields:
|
|
||||||
- { label: "Title", name: "title", widget: "string" }
|
|
||||||
- { label: "Publisher", name: "publisher", widget: "string" }
|
|
||||||
- { label: "Author", name: "author", widget: "string" }
|
|
||||||
- { label: "Year", name: "year", widget: "string" }
|
|
||||||
- { label: "Rating", name: "rating", widget: "number", min: 1, max: 5 }
|
|
||||||
- { label: "Image", name: "image", widget: "string", hint: "A CDN URL."}
|
|
||||||
- { label: "Tags", name: "tags", widget: "list" }
|
|
||||||
- { label: "Using YouTube", name: "youtube", widget: "boolean", default: false, hint: "If using the YouTube shortcode, enable this option to append the lite-youtube script." }
|
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
|
||||||
- name: "podcast"
|
|
||||||
label: "Podcast"
|
|
||||||
folder: "src/catalogue/podcasts"
|
|
||||||
slug: "{% raw %}{{year}}-{{month}}-{{day}}-{{slug}}{% endraw %}"
|
|
||||||
create: true
|
|
||||||
fields:
|
|
||||||
- { label: "Title", name: "title", widget: "string" }
|
|
||||||
- { label: "URL", name: "url", widget: "string", hint: "A URL for someone to listen to the podcast." }
|
|
||||||
- { label: "Rating", name: "rating", widget: "number", min: 1, max: 5 }
|
|
||||||
- { label: "Image", name: "image", widget: "string", hint: "A CDN URL."}
|
|
||||||
- { label: "Tags", name: "tags", widget: "list" }
|
|
||||||
- { label: "Using YouTube", name: "youtube", widget: "boolean", default: false, hint: "If using the YouTube shortcode, enable this option to append the lite-youtube script." }
|
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
permalink: admin/index.html
|
|
||||||
---
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="robots" content="noindex" />
|
|
||||||
<!-- Making Decap more responsive in mobile -->
|
|
||||||
<!-- https://github.com/hithismani/responsive-decap -->
|
|
||||||
<link rel="stylesheet"
|
|
||||||
href="https://cdn.jsdelivr.net/gh/hithismani/responsive-decap@main/dist/responsive.min.css">
|
|
||||||
<title>Content Manager</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- Include the script that builds the page and powers Decap CMS -->
|
|
||||||
<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,10 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
layout: "layouts/catalogue-item",
|
|
||||||
tags: "book",
|
|
||||||
permalink: "catalogue/books/{{ page.fileSlug }}/index.html",
|
|
||||||
linkTitle: "View book details",
|
|
||||||
eleventyComputed: {
|
|
||||||
tertiary: (data) =>
|
|
||||||
`<p class="[ flow-space-0.5 ]"><span class="[ text-fadeText ]">by</span> ${data.author}</p>`,
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
tags: "catalogue",
|
|
||||||
eleventyComputed: {
|
|
||||||
description: (data) => `My thoughts on ${data.title}.`,
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
title: "The Monster Sisters"
|
|
||||||
publisher: "Orca Books"
|
|
||||||
author: "Gareth Gaudin"
|
|
||||||
year: 2021
|
|
||||||
isbn: 9781459822290
|
|
||||||
rating: 4
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-monster-sisters-vol-2.jpg
|
|
||||||
tags: ["adventure"]
|
|
||||||
---
|
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
title: "Monstress: Awakening"
|
|
||||||
publisher: Image Comics
|
|
||||||
author: Majorie Liu, Sana Takeda
|
|
||||||
volume: 1
|
|
||||||
year: 2017
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/monstress-vol-1.jpeg
|
|
||||||
rating: 3
|
|
||||||
tags: ["fantasy"]
|
|
||||||
---
|
|
||||||
|
|
||||||
The first 75% of the volume is quite confusing. It’s like reading the second or third volume in a series without reading the first. I get starting with a mystery as a hook, but when every second word is something new, I get confused!
|
|
||||||
|
|
||||||
I’m not convinced that comics are the best form of world building, at least through expository dialogue. When several entire pages that are at least half-filled with text, I think there’s a problem.
|
|
||||||
|
|
||||||
The artwork is praised often elsewhere and I must agree - every page (even panel) could stand on its own. Absolutely stunning. The world building on the visual level absolutely nails it. If atmosphere is set within the first few pages and I’m here for it.
|
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
title: "Monstress: The Blood"
|
|
||||||
publisher: Image Comics
|
|
||||||
author: Majorie Lui, Sana Takeda
|
|
||||||
volume: 2
|
|
||||||
year: 2017
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/monstress-vol-2-tp_77ea1f170a.jpg
|
|
||||||
isbn: 9781534300415
|
|
||||||
rating: 3
|
|
||||||
tags: ["fantasy"]
|
|
||||||
---
|
|
||||||
|
|
||||||
I love the cast of the story. We are introduced to some new characters with a heavy nautical theme. Old Tooth the shark-person looks straight out of the 90s (remember Street Sharks?). The artwork continues to be the main selling point for me.
|
|
||||||
|
|
||||||
Where things falter though has to be the writing (again). There are less mysteries to track and more emphasis on character development, but I’m finding it so dense that it’s hard to follow. It practically requires a wiki open while reading. There is so much to pack into a graphic novel with limited pages. I think the series would do better as a novel if I’m completely honest.
|
|
||||||
|
|
||||||
I can’t get into the story, but Sana Takeda’s art is so phenomenal I want to keep going.
|
|
|
@ -1,9 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
layout: "layouts/catalogue-item",
|
|
||||||
tags: "comic",
|
|
||||||
permalink: "catalogue/comics/{{ page.fileSlug }}/index.html",
|
|
||||||
eleventyComputed: {
|
|
||||||
tertiary: (data) =>
|
|
||||||
`<p class="[ flow-space-0.5 ]"><span class="[ text-fadeText ]">by</span> ${data.author}</p>`,
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
title: Axiom Verge
|
|
||||||
platform: Nintendo Switch
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/Axiom_Verge_Title.png
|
|
||||||
tags: ["action", "metroidvania", "indie", "single player"]
|
|
||||||
year: 2015
|
|
||||||
---
|
|
|
@ -1,8 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
layout: "layouts/catalogue-item",
|
|
||||||
tags: "game",
|
|
||||||
permalink: "catalogue/games/{{ page.fileSlug }}/index.html",
|
|
||||||
eleventyComputed: {
|
|
||||||
subtitle: (data) => `${data.platform}`,
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
title: Hunting Warhead
|
|
||||||
rating: 5
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/hunting-warhead.webp
|
|
||||||
url: https://www.cbc.ca/listen/cbc-podcasts/387-hunting-warhead
|
|
||||||
---
|
|
||||||
|
|
||||||
A chilling and gripping history telling the story of how one of the largest networks of CSAM was taken down by tracking its biggest maintainer.
|
|
||||||
|
|
||||||
[Listen to the podcast](https://www.cbc.ca/listen/cbc-podcasts/387-hunting-warhead)
|
|
|
@ -1,15 +0,0 @@
|
||||||
---
|
|
||||||
title: Maintenance Phase
|
|
||||||
rating: 5
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/maintenance-phase.webp
|
|
||||||
url: https://www.maintenancephase.com/
|
|
||||||
---
|
|
||||||
|
|
||||||
A phenomenal podcast that challenged everything I knew about fatness, dieting, food, and my own relationship with each. I don't say this lightly but it has been a life-changing revelation for me. I devour every new episode with fervor and eagerly consume all their content.
|
|
||||||
|
|
||||||
## Favourite episodes
|
|
||||||
|
|
||||||
- [Dr. Oz](https://maintenancephase.buzzsprout.com/1411126/7857472-dr-oz)
|
|
||||||
- [Halo Top Ice Cream](https://maintenancephase.buzzsprout.com/1411126/7127890-halo-top-ice-cream)
|
|
||||||
- [The Biggest Loser](https://maintenancephase.buzzsprout.com/1411126/7353850-the-biggest-loser)
|
|
||||||
I used to watch this show religiously with my parents. I'd look to it at times for inspiration. Realizing the literal hell the cast when through now is horrifying.
|
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
title: Reply All
|
|
||||||
url: https://gimletmedia.com/shows/reply-all
|
|
||||||
rating: 3
|
|
||||||
image: https://cdn.wonderfulfrog.com/images/reply-all.jpeg
|
|
||||||
---
|
|
||||||
|
|
||||||
There are too many episodes to this podcast, but I have been shared a few interesting ones from my partner. I was never a regular listener, and have not listened since it [effectively imploded](https://www.vulture.com/article/gimlet-reply-all-controversy-spotify-test-kitchen.html)
|
|
||||||
|
|
||||||
## Favourite episodes
|
|
||||||
|
|
||||||
- [The Case of the Missing Hit](https://pca.st/episode/cc572c51-e2bd-41fe-a138-d4f8ecba3549)
|
|
||||||
This is a real journey. There are so many fun twists and turns.
|
|
|
@ -1,6 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
layout: "layouts/catalogue-item",
|
|
||||||
tags: "podcast",
|
|
||||||
permalink: "catalogue/podcasts/{{ page.fileSlug }}/index.html",
|
|
||||||
linkTitle: "Listen to the podcast",
|
|
||||||
};
|
|
|
@ -4,7 +4,10 @@ author: Brandon Sanderson
|
||||||
isbn: 9780765326355
|
isbn: 9780765326355
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-way-of-kings.jpeg
|
image: https://cdn.wonderfulfrog.com/images/the-way-of-kings.jpeg
|
||||||
tags: ["fantasy"]
|
isFavourite: true
|
||||||
|
tags:
|
||||||
|
- fantasy
|
||||||
|
- fiction
|
||||||
year: 2010
|
year: 2010
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,9 @@ author: Seth Godin
|
||||||
isbn: 9781591841661
|
isbn: 9781591841661
|
||||||
rating: 4
|
rating: 4
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-dip.jpeg
|
image: https://cdn.wonderfulfrog.com/images/the-dip.jpeg
|
||||||
tags: ["self help"]
|
tags:
|
||||||
|
- self-help
|
||||||
|
- non-fiction
|
||||||
year: 2007
|
year: 2007
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,9 @@ author: Mark Manson
|
||||||
isbn: 9780062457714
|
isbn: 9780062457714
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/subtle-art.jpeg
|
image: https://cdn.wonderfulfrog.com/images/subtle-art.jpeg
|
||||||
tags: ["self help"]
|
tags:
|
||||||
|
- self-help
|
||||||
|
- non-fiction
|
||||||
year: 2016
|
year: 2016
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,9 +3,11 @@ title: The Total Money Makeover
|
||||||
subtitle: A Proven Plan for Financial Fitness
|
subtitle: A Proven Plan for Financial Fitness
|
||||||
author: Dave Ramsey
|
author: Dave Ramsey
|
||||||
isbn: 9780785289081
|
isbn: 9780785289081
|
||||||
rating: 3
|
rating: 2
|
||||||
image: https://cdn.wonderfulfrog.com/images/total-money-makeover.webp
|
image: https://cdn.wonderfulfrog.com/images/total-money-makeover.webp
|
||||||
tags: ["finance"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- finance
|
||||||
year: 2003
|
year: 2003
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,10 @@ author: Gary Taubes
|
||||||
isbn: 9780307272706
|
isbn: 9780307272706
|
||||||
rating: 3
|
rating: 3
|
||||||
image: https://cdn.wonderfulfrog.com/images/why-we-get-fat.jpeg
|
image: https://cdn.wonderfulfrog.com/images/why-we-get-fat.jpeg
|
||||||
tags: ["self help", "diet"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- diet
|
||||||
year: 2010
|
year: 2010
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,9 +3,13 @@ title: Digital Minimalism
|
||||||
subtitle: Choosing a Focused Life in a Noisy World
|
subtitle: Choosing a Focused Life in a Noisy World
|
||||||
author: Cal Newport
|
author: Cal Newport
|
||||||
isbn: 9780525536512
|
isbn: 9780525536512
|
||||||
rating: 4
|
rating: 2
|
||||||
image: https://cdn.wonderfulfrog.com/images/digital-minimalism.jpg
|
image: https://cdn.wonderfulfrog.com/images/digital-minimalism.jpg
|
||||||
tags: ["white guy productivity", "technology"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- technology
|
||||||
|
- white-guy-productivity
|
||||||
year: 2019
|
year: 2019
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,11 @@ author: Brené Brown
|
||||||
isbn: 9781473562523
|
isbn: 9781473562523
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/dare-to-lead.jpeg
|
image: https://cdn.wonderfulfrog.com/images/dare-to-lead.jpeg
|
||||||
tags: ["leadership", "self help", "business"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- leadership
|
||||||
|
- business
|
||||||
year: 2018
|
year: 2018
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,12 @@ author: Edmond Lau
|
||||||
isbn: 9780996128100
|
isbn: 9780996128100
|
||||||
rating: 4
|
rating: 4
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-effective-engineer.jpeg
|
image: https://cdn.wonderfulfrog.com/images/the-effective-engineer.jpeg
|
||||||
tags: ["leadership", "software development", "technology"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- leadership
|
||||||
|
- technology
|
||||||
|
- software-development
|
||||||
year: 2015
|
year: 2015
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,9 +3,12 @@ title: Atomic Habits
|
||||||
subtitle: An Easy & Proven Way to Build Good Habits & Break Bad Ones
|
subtitle: An Easy & Proven Way to Build Good Habits & Break Bad Ones
|
||||||
author: James Clear
|
author: James Clear
|
||||||
isbn: 9780735211292
|
isbn: 9780735211292
|
||||||
rating: 5
|
rating: 3
|
||||||
image: https://cdn.wonderfulfrog.com/images/atomic-habits.jpeg
|
image: https://cdn.wonderfulfrog.com/images/atomic-habits.jpeg
|
||||||
tags: ["psychology", "self help"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- psychology
|
||||||
year: 2018
|
year: 2018
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,10 @@ author: Ryan Holiday
|
||||||
isbn: 9781591847816
|
isbn: 9781591847816
|
||||||
rating: 3
|
rating: 3
|
||||||
image: https://cdn.wonderfulfrog.com/images/ego-is-the-enemy.jpeg
|
image: https://cdn.wonderfulfrog.com/images/ego-is-the-enemy.jpeg
|
||||||
tags: ["stoicism", "self help"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- stoicism
|
||||||
year: 2016
|
year: 2016
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,9 @@ author: Marcus Aurelius
|
||||||
isbn: 9780140449334
|
isbn: 9780140449334
|
||||||
rating: 2
|
rating: 2
|
||||||
image: https://cdn.wonderfulfrog.com/images/meditations.jpeg
|
image: https://cdn.wonderfulfrog.com/images/meditations.jpeg
|
||||||
tags: ["stoicism"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- stoicism
|
||||||
year: 180
|
year: 180
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,10 @@ author: Cal Newport
|
||||||
isbn: 9781455586691
|
isbn: 9781455586691
|
||||||
rating: 2
|
rating: 2
|
||||||
image: https://cdn.wonderfulfrog.com/images/deep-work.jpeg
|
image: https://cdn.wonderfulfrog.com/images/deep-work.jpeg
|
||||||
tags: ["white guy productivity"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
|
- white-guy-productivity
|
||||||
year: 2016
|
year: 2016
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,10 @@ author: Matthew Walker
|
||||||
isbn: 9781501144318
|
isbn: 9781501144318
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/why-we-sleep.jpeg
|
image: https://cdn.wonderfulfrog.com/images/why-we-sleep.jpeg
|
||||||
tags: ["sleep"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- sleep
|
||||||
|
- science
|
||||||
year: 2017
|
year: 2017
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,9 @@ author: Neil deGrasse Tyson
|
||||||
isbn: 9780393609394
|
isbn: 9780393609394
|
||||||
rating: 2
|
rating: 2
|
||||||
image: https://cdn.wonderfulfrog.com/images/astro-in-hurry.jpeg
|
image: https://cdn.wonderfulfrog.com/images/astro-in-hurry.jpeg
|
||||||
tags: ["science"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- science
|
||||||
year: 2017
|
year: 2017
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,10 @@ author: Angie Thomas
|
||||||
isbn: 9780062498533
|
isbn: 9780062498533
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-hate-u-give.jpg
|
image: https://cdn.wonderfulfrog.com/images/the-hate-u-give.jpg
|
||||||
tags: ["fiction", "racism"]
|
isFavourite: true
|
||||||
|
tags:
|
||||||
|
- fiction
|
||||||
|
- racism
|
||||||
year: 2017
|
year: 2017
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,11 @@ author: Desmond Cole
|
||||||
isbn: 9780385686341
|
isbn: 9780385686341
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-skin-were-in.jpeg
|
image: https://cdn.wonderfulfrog.com/images/the-skin-were-in.jpeg
|
||||||
tags: ["politics", "canada", "racism"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- racism
|
||||||
|
- canada
|
||||||
|
- politics
|
||||||
year: 2020
|
year: 2020
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,9 @@ author: Gay Hendricks
|
||||||
isbn: 9780061735363
|
isbn: 9780061735363
|
||||||
rating: 3
|
rating: 3
|
||||||
image: https://cdn.wonderfulfrog.com/images/the-big-leap.jpg
|
image: https://cdn.wonderfulfrog.com/images/the-big-leap.jpg
|
||||||
tags: ["self help"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- self-help
|
||||||
year: 2009
|
year: 2009
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,10 @@ author: Joshua Whitehead
|
||||||
isbn: 9781551527253
|
isbn: 9781551527253
|
||||||
rating: 4
|
rating: 4
|
||||||
image: https://cdn.wonderfulfrog.com/images/jonny-appleseed.jpeg
|
image: https://cdn.wonderfulfrog.com/images/jonny-appleseed.jpeg
|
||||||
tags: ["fictional", "emotional"]
|
tags:
|
||||||
|
- fiction
|
||||||
|
- emotional
|
||||||
|
- lgbtq
|
||||||
year: 2018
|
year: 2018
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
---
|
---
|
||||||
title: Understanding Comics
|
title: Understanding Comics
|
||||||
subtitle: The Invisible Art
|
subtitle: The Invisible Art
|
||||||
|
date: 2021-09-09T10:00:00-07:00
|
||||||
author: Scott McCloud
|
author: Scott McCloud
|
||||||
isbn: 9780060976255
|
isbn: 9780060976255
|
||||||
rating: 5
|
rating: 5
|
||||||
image: https://cdn.wonderfulfrog.com/images/understanding-comics.jpg
|
image: https://cdn.wonderfulfrog.com/images/understanding-comics.jpg
|
||||||
tags: ["comics", "art", "history"]
|
tags:
|
||||||
|
- comics
|
||||||
|
- art
|
||||||
|
- history
|
||||||
|
isFavourite: true
|
||||||
year: 1993
|
year: 1993
|
||||||
---
|
---
|
||||||
|
|
||||||
I debated if this should end up in a different catalogue, but it deserves to be in both, if anything.
|
|
||||||
|
|
||||||
If you wondered just how deep the rabbit hole goes when it comes to all things comics, Scott McCloud is here to tell you it goes _deep_. Anyone who may doubt the medium of the comic -- I challenge you to come out without a new perspective reading this. I know I did. I appreciate them so much more now.
|
If you wondered just how deep the rabbit hole goes when it comes to all things comics, Scott McCloud is here to tell you it goes _deep_. Anyone who may doubt the medium of the comic -- I challenge you to come out without a new perspective reading this. I know I did. I appreciate them so much more now.
|
||||||
|
|
||||||
To call it "a book about comics" feels like a disservice. There is so much more going on here about history and the human mind.
|
To call it "a book about comics" feels like a disservice. There is so much more going on here about history and the human mind.
|
|
@ -5,7 +5,9 @@ author: Victoria Ortiz
|
||||||
isbn: 978054497364
|
isbn: 978054497364
|
||||||
rating: 4
|
rating: 4
|
||||||
image: https://cdn.wonderfulfrog.com/images/dissenter-on-the-bench.jpeg
|
image: https://cdn.wonderfulfrog.com/images/dissenter-on-the-bench.jpeg
|
||||||
tags: ["biography"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- biography
|
||||||
year: 2019
|
year: 2019
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,7 +4,9 @@ author: Jim Butcher
|
||||||
image: https://cdn.wonderfulfrog.com/images/jim-butcher-peace-talks.jpeg
|
image: https://cdn.wonderfulfrog.com/images/jim-butcher-peace-talks.jpeg
|
||||||
isbn: 9780393609394
|
isbn: 9780393609394
|
||||||
rating: 2
|
rating: 2
|
||||||
tags: ["fiction", "fantasy"]
|
tags:
|
||||||
|
- fiction
|
||||||
|
- fantasy
|
||||||
year: 2020
|
year: 2020
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,10 @@ image: https://cdn.wonderfulfrog.com/images/9780807041307_p0_v1_s1200x630.jpg
|
||||||
url: https://www.yourfatfriend.com/book
|
url: https://www.yourfatfriend.com/book
|
||||||
isbn: 9780807041307
|
isbn: 9780807041307
|
||||||
rating: 5
|
rating: 5
|
||||||
tags: ["anti-fat"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- diet
|
||||||
|
- anti-fat
|
||||||
year: 2020
|
year: 2020
|
||||||
---
|
---
|
||||||
|
|
|
@ -5,7 +5,11 @@ author: Johann Hari
|
||||||
isbn: 9781526620224
|
isbn: 9781526620224
|
||||||
rating: 4
|
rating: 4
|
||||||
image: https://cdn.wonderfulfrog.com/images/stolen-focus.jpg
|
image: https://cdn.wonderfulfrog.com/images/stolen-focus.jpg
|
||||||
tags: ["focus", "adhd", "psychology"]
|
tags:
|
||||||
|
- non-fiction
|
||||||
|
- adhd
|
||||||
|
- focus
|
||||||
|
- psychology
|
||||||
year: 2022
|
year: 2022
|
||||||
---
|
---
|
||||||
|
|
5
src/content/books/books.11tydata.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default {
|
||||||
|
layout: "layouts/book",
|
||||||
|
permalink: "books/{{ page.fileSlug }}/index.html",
|
||||||
|
tags: "book",
|
||||||
|
};
|
|
@ -1,8 +1,11 @@
|
||||||
---
|
---
|
||||||
title: "Super Mario World 2: Yoshi's Island"
|
title: "Super Mario World 2: Yoshi's Island"
|
||||||
platform: Super Nintendo
|
platform: Super Nintendo
|
||||||
image: https://cdn.wonderfulfrog.com/images/Yoshis_Island_box_art.jpg
|
image: https://cdn.wonderfulfrog.com/images/games/yoshis-island-cover.jpg
|
||||||
tags: ["platformer"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- platformer
|
||||||
|
isFavourite: true
|
||||||
year: 1995
|
year: 1995
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
title: "Majora's Mask 3D"
|
title: "Majora's Mask 3D"
|
||||||
platform: Nintendo 3DS
|
platform: Nintendo 3DS
|
||||||
image: https://cdn.wonderfulfrog.com/images/Majoras_Mask_3D_cover.png
|
image: https://cdn.wonderfulfrog.com/images/Majoras_Mask_3D_cover.png
|
||||||
tags: ["adventure", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- adventure
|
||||||
year: 2015
|
year: 2015
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
title: "Mario & Luigi: Dream Team"
|
title: "Mario & Luigi: Dream Team"
|
||||||
platform: Nintendo 3DS
|
platform: Nintendo 3DS
|
||||||
image: https://cdn.wonderfulfrog.com/images/MarioLuigiDreamTeam.jpg
|
image: https://cdn.wonderfulfrog.com/images/MarioLuigiDreamTeam.jpg
|
||||||
tags: ["rpg", "turn based", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- rpg
|
||||||
|
- turn-based
|
||||||
year: 2013
|
year: 2013
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,13 +2,17 @@
|
||||||
title: "Donkey Kong Country: Tropical Freeze"
|
title: "Donkey Kong Country: Tropical Freeze"
|
||||||
platform: Nintendo Wii U
|
platform: Nintendo Wii U
|
||||||
image: https://cdn.wonderfulfrog.com/images/DKC5_box_art.jpg
|
image: https://cdn.wonderfulfrog.com/images/DKC5_box_art.jpg
|
||||||
tags: ["platformer", "challenging", "single player", "multiplayer"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- co-op
|
||||||
|
- platformer
|
||||||
|
- challenging
|
||||||
year: 2014
|
year: 2014
|
||||||
---
|
---
|
||||||
|
|
||||||
Another challenging game. DKCTF nails the difficulty perfectly. As mentioned before, every failure feels like it's 100% the fault of the player. The controls are spot on and frankly should be the model for any platformer going forward. There are various control schemes to suit practically any player. My only minor gripe was that the buttons for each scheme were not customizable -- but the default options are fine. Going from the first level to the last is fairly short experience, but in typical DKC fashion the meat of the game comes from replaying levels and grabbing all the collectables. If that's not really your thing then you've still got a solid platformer that should scratch the itch. Visually the game is amazing to look at... I mean just look at this:
|
Another challenging game. DKCTF nails the difficulty perfectly. As mentioned before, every failure feels like it's 100% the fault of the player. The controls are spot on and frankly should be the model for any platformer going forward. There are various control schemes to suit practically any player. My only minor gripe was that the buttons for each scheme were not customizable -- but the default options are fine. Going from the first level to the last is fairly short experience, but in typical DKC fashion the meat of the game comes from replaying levels and grabbing all the collectables. If that's not really your thing then you've still got a solid platformer that should scratch the itch. Visually the game is amazing to look at... I mean just look at this:
|
||||||
|
|
||||||
{% image "https://cdn.wonderfulfrog.com/images/dkctf.jpg", "", "" %}
|

|
||||||
|
|
||||||
The soundtrack is all aces as well -- composed by the same guy behind the first games - David Wise. It provides a fitting background - intense enough that it energizes you, but not overbearing that it comes the star of the show.
|
The soundtrack is all aces as well -- composed by the same guy behind the first games - David Wise. It provides a fitting background - intense enough that it energizes you, but not overbearing that it comes the star of the show.
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
title: "Professor Layton vs. Phoenix Wright: Ace Attorney"
|
title: "Professor Layton vs. Phoenix Wright: Ace Attorney"
|
||||||
platform: Nintendo 3DS
|
platform: Nintendo 3DS
|
||||||
image: https://cdn.wonderfulfrog.com/images/Laytonvsaceattorneycover.jpg
|
image: https://cdn.wonderfulfrog.com/images/Laytonvsaceattorneycover.jpg
|
||||||
tags: ["puzzle", "narrative", "visual novel", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- narrative
|
||||||
|
- puzzle
|
||||||
|
- visual-novel
|
||||||
year: 2014
|
year: 2014
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
title: "The Wonderful 101"
|
title: "The Wonderful 101"
|
||||||
platform: Nintendo Wii U
|
platform: Nintendo Wii U
|
||||||
image: https://cdn.wonderfulfrog.com/images/Wonderful_101_box_artwork.jpg
|
image: https://cdn.wonderfulfrog.com/images/Wonderful_101_box_artwork.jpg
|
||||||
tags: ["action", "beat em up", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- action
|
||||||
|
- beat-em-up
|
||||||
|
- platinum
|
||||||
year: 2013
|
year: 2013
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
title: Fallout 4
|
title: Fallout 4
|
||||||
platform: PC
|
platform: PC
|
||||||
image: https://cdn.wonderfulfrog.com/images/Fallout_4_cover_art.jpg
|
image: https://cdn.wonderfulfrog.com/images/Fallout_4_cover_art.jpg
|
||||||
tags: ["rpg", "action", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- action
|
||||||
|
- rpg
|
||||||
year: 2015
|
year: 2015
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
title: "Persona 3 Portable"
|
title: "Persona 3 Portable"
|
||||||
platform: PlayStation Portable
|
platform: PlayStation Portable
|
||||||
image: https://cdn.wonderfulfrog.com/images/p3p.jpg
|
image: https://cdn.wonderfulfrog.com/images/p3p.jpg
|
||||||
tags: ["rpg", "turn based", "persona", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- rpg
|
||||||
|
- turn-based
|
||||||
|
- persona
|
||||||
youtube: true
|
youtube: true
|
||||||
year: 2009
|
year: 2009
|
||||||
---
|
---
|
|
@ -2,7 +2,10 @@
|
||||||
title: Shovel Knight
|
title: Shovel Knight
|
||||||
platform: Nintendo Wii U
|
platform: Nintendo Wii U
|
||||||
image: https://cdn.wonderfulfrog.com/images/Shovel_knight_cover.jpg
|
image: https://cdn.wonderfulfrog.com/images/Shovel_knight_cover.jpg
|
||||||
tags: ["platformer", "retro", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- platformer
|
||||||
|
- retro
|
||||||
year: 2014
|
year: 2014
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,7 +2,12 @@
|
||||||
title: Tomb Raider
|
title: Tomb Raider
|
||||||
platform: PC
|
platform: PC
|
||||||
image: https://cdn.wonderfulfrog.com/images/TombRaider2013.jpg
|
image: https://cdn.wonderfulfrog.com/images/TombRaider2013.jpg
|
||||||
tags: ["action", "adventure", "puzzle", "reboot", "single player"]
|
tags:
|
||||||
|
- single-player
|
||||||
|
- action
|
||||||
|
- adventure
|
||||||
|
- puzzle
|
||||||
|
- reboot
|
||||||
year: 2013
|
year: 2013
|
||||||
---
|
---
|
||||||
|
|