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
This commit is contained in:
Devin Haska 2025-01-27 18:23:38 -08:00 committed by GitHub
parent e28e804e12
commit 5f8227a46b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
282 changed files with 4577 additions and 5016 deletions

View file

@ -87,4 +87,4 @@ const blogroll = [
const sortedBlogroll = blogroll.sort((a, b) => a.title.localeCompare(b.title));
module.exports = sortedBlogroll;
export default sortedBlogroll;

View file

@ -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,
};
};

View file

@ -1,10 +1,10 @@
require("dotenv").config();
import "dotenv/config";
const EleventyFetch = require("@11ty/eleventy-fetch");
const cheerio = require("cheerio");
const dayjs = require("dayjs");
const utc = require("dayjs/plugin/utc");
const relativeTime = require("dayjs/plugin/relativeTime");
import EleventyFetch from "@11ty/eleventy-fetch";
import cheerio from "cheerio";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc.js";
import relativeTime from "dayjs/plugin/relativeTime.js";
dayjs.extend(utc);
dayjs.extend(relativeTime);
@ -33,7 +33,7 @@ const fetchRecentMovies = async () => {
$("channel")
.children("item")
.slice(0, 6)
.slice(0, 5)
.each((_, element) => {
const title = $(element).children("letterboxd\\:filmTitle").text();
@ -65,4 +65,4 @@ const fetchRecentMovies = async () => {
return recentMovies;
};
module.exports = fetchRecentMovies;
export default fetchRecentMovies;

View file

@ -1,4 +1,4 @@
module.exports = {
export default {
url: process.env.URL || "http://localhost:8080",
siteName: "wonderfulfrog",
siteDescription:
@ -7,9 +7,4 @@ module.exports = {
lang: "en",
author: "Devin Haska",
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",
},
};

View file

@ -1,24 +1,41 @@
module.exports = {
export default {
top: [
{
text: "About",
url: "/about",
icon: "circle-info",
},
{
text: "Posts",
url: "/posts",
icon: "list",
},
{
text: "Catalogue",
url: "/catalogue",
text: "Games",
url: "/games",
icon: "game-controller",
},
{
text: "Watching",
url: "/watching",
icon: "tv-retro",
},
{
text: "Books",
url: "/books",
icon: "book",
},
],
bottom: [
{ text: "Blogroll", url: "/blogroll" },
{
text: "Colophon",
url: "/colophon",
},
{ text: "Blogroll", url: "/blogroll" },
{
text: "Contact",
url: "/contact",
},
{
text: "/uses",
url: "/uses",

View file

@ -1,5 +1,5 @@
const { getFontUrl } = require("../utils/fonts");
const fonts = require("../../config/design-tokens/fonts.json");
import { getFontUrl } from "../utils/fonts.js";
import fonts from "../../config/design-tokens/fonts.js";
const preloads = [
{
@ -19,4 +19,4 @@ const preloads = [
},
];
module.exports = preloads;
export default preloads;

View file

@ -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;
@ -101,7 +101,7 @@ const fetchRobotsTxt = async () => {
}
};
module.exports = async function () {
export default async function () {
const robotsTxt = await fetchRobotsTxt();
if (!robotsTxt) {
@ -109,4 +109,4 @@ module.exports = async function () {
}
return robotsTxt;
};
}

View file

@ -22,10 +22,10 @@
src="https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@1.4.0/lite-youtube.min.js"></script>
{% endif %}
</head>
<body class="[ flex-col ]">
<body class="flex-col">
{% noRobots %}
{% 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 }}
</main>
{% include "partials/footer.html" %}

View 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") }}

View file

@ -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>

View 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") }}

View 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") }}

View file

@ -3,15 +3,17 @@ layout: "layouts/base"
---
{% from "macros/date.njk" import format %}
<article class="[ flow ]">
<header class="[ flow flow-space-1 mb-2 ]">
<article class="flow">
<header class="flow flow-space-1 mb-2">
{{ format(page.date) }}
<h1>{{ title }}</h1>
<p class="[ text-skew text-fadeText flow-space-0.25 ]">{{ excerpt }}</p>
<ul class="[ categories ] [ cluster list-none p-0 flow-space-2 line-height-m ]">
<p class="text-fadeText flow-space-0.25">{{ excerpt }}</p>
<ul class="categories cluster list-none p-0 flow-space-2 line-height-m">
{% for tag in tags | filter(["post"]) %}
<li class="[ flex gap-0.25 ]">
<a href="/tags/{{ tag | slugify }}">{{ tag }}</a>
<li>
<a class="button" href="/tags/{{ tag | slugify }}">
{% include "svgs/frame.svg" %}
{{ tag }}</a>
</li>
{% endfor %}
</ul>

View 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") }}

View file

@ -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 %}

View file

@ -1,5 +1,6 @@
{% macro format(dateString) %}
<time class="[ date ] [ flex items-center gap-0.5 text-fadeText ]" datetime="{{ date }}">
<span class="[ text-primary line-height-s ]">{% include "svgs/calendar.svg" %}</span>{{ dateString | formatDate("MMMM Do YYYY") }}
<time class="date flex items-center gap-0.5 text-fadeText"
datetime="{{ dateString }}">
<span class="text-primary line-height-s">{% include "svgs/calendar.svg" %}</span>{{ dateString | formatDate("MMMM Do YYYY") }}
</time>
{% endmacro %}

View 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 %}

View file

@ -1,29 +1,21 @@
{% macro one(post, format = "MM/DD") %}
<article class="[ posts-list-item column-gap-0.5 justify-between line-height-l ]">
<a href="{{ post.url }}">{{ post.data.title }}</a>
<time datetime="{{ post.date }}" class="[ text-fadeText ]">{{ post.date | formatDate(format) }}</time>
<p class="[ text-fadeText font-size-s line-height-m ]">{{ post.data.excerpt }}</p>
</article>
{% macro one(post, fmt = "MM/DD") %}
<article class="posts-list-item column-gap-0.5 justify-between line-height-l">
<a href="{{ post.url }}">{{ post.data.title }}</a>
<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>
</article>
{% endmacro %}
{% macro list(posts, format = "MM/DD") %}
<ol class="[ flow p-0 ]" role="list">
{% for post in posts %}
<li class="[ flow-space-0.5 ]">
{{ one(post, format) }}
</li>
{% endfor %}
</ol>
<ol class="flow p-0" role="list">
{% for post in posts %}<li class="flow-space-0.5">{{ one(post, format) }}</li>{% endfor %}
</ol>
{% endmacro %}
{% macro yearList(posts, year, format = "MM/DD") %}
<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>
<section class="flow">
<header class="cluster gap-0.5">
<h2>{{ year }}</h2>
<p class="pill">{{ posts | length }}</p>
</header>
{{ list(posts, format) }}
</section>
{% endmacro %}

View 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 %}

View file

@ -1,12 +1,13 @@
{% macro stars(number) %}
{% set filledStars = number %}
{% set emptyStars = 5 - number %}
<ul class="[ stars ] [ flex list-none p-0 m-0 text-primary ]" aria-description="{{ number }} out of 5 stars">
{% for i in range(0, filledStars) %}
<li class="[ star-filled ]">{% include "svgs/star.svg" %}</li>
{% endfor %}
{% for i in range(0, emptyStars) %}
<li class="[ star-empty ]">{% include "svgs/star-empty.svg" %}</li>
{% endfor %}
<ul class="stars flex list-none p-0 mb-0 text-primary"
aria-description="{{ number }} out of 5 stars">
{% for i in range(0, filledStars) %}
<li class="star-filled">{% include "svgs/star.svg" %}</li>
{% endfor %}
{% for i in range(0, emptyStars) %}
<li class="star-empty">{% include "svgs/star-empty.svg" %}</li>
{% endfor %}
</ul>
{% endmacro %}

View file

@ -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>

View file

@ -1,9 +1,9 @@
<footer class="[ bg-surface mt-1.5 py-1.5 ]">
<div class="[ wrapper flow ]">
<div class="[ repel ]">
<footer class="bg-surface mt-1.5 py-1.5">
<div class="wrapper flow">
<div class="repel">
<p>&copy; {{ meta.author }} 2018 - 2025.</p>
<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 %}
<li>
<a href="{{ link.url }}">{{ link.text }}</a>
@ -12,33 +12,40 @@
</ul>
</nav>
</div>
<div class="[ repel ]">
<nav class="[ flow-space-0.5 ]">
<ul class="[ menu ] [ cluster p-0 m-0 gap-0.5 ]" role="list">
<div class="repel">
<nav class="flow-space-0.5">
<ul class="cluster p-0 m-0 gap-0.5" role="list">
<li>
<span class="[ visually-hidden ]">Feeds</span>
<a href="/feeds" class="[ button ]" aria-label="View RSS feed">{% include "svgs/rss.svg" %}</a>
<a href="/feeds"
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>
{% 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>
</nav>
<ul class="[ cluster p-0 m-0 ]" role="list">
<li>
<a href="/webrings">Webrings</a>
</li>
<ul class="cluster p-0 m-0" role="list">
<li>
<a href="/changelog">Changelog</a>
</li>
<li>
<a href="/webrings">Webrings</a>
</li>
<li>
<a href="{{ meta.repoUrl }}"
target="_blank"

View file

@ -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>
<div class="[ wrapper ]">
<nav class="[ repel ]">
<div class="[ site-logo ] [ flex items-center gap-0.5 ]">
<a class="[ logo ] [ flex items-center justify-center ]"
<div class="wrapper">
<nav class="repel">
<div class="site-logo flex items-center gap-0.5">
<a class="logo flex items-center justify-center"
aria-label="Go to index"
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 class="[ wordmark ] [ flex ]"
href="{{ meta.url }}"
aria-label="Go to index">
<a class="wordmark flex" href="{{ meta.url }}" aria-label="Go to index">
<svg height="0" width="0">
<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>frog</span>
</a>

View file

@ -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 %}
<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>
{% endfor %}
</ul>

View 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

View 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

View 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

View 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

View 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

View file

@ -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

View 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

View 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

View file

@ -1,3 +0,0 @@
{
"excludeFromSitemap": true
}

View file

@ -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" }

View file

@ -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>

View file

@ -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>`,
},
};

View file

@ -1,6 +0,0 @@
module.exports = {
tags: "catalogue",
eleventyComputed: {
description: (data) => `My thoughts on ${data.title}.`,
},
};

View file

@ -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"]
---

View file

@ -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. Its 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!
Im 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 theres 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 Im here for it.

View file

@ -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 Im finding it so dense that its 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 Im completely honest.
I cant get into the story, but Sana Takedas art is so phenomenal I want to keep going.

View file

@ -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>`,
},
};

View file

@ -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
---

View file

@ -1,8 +0,0 @@
module.exports = {
layout: "layouts/catalogue-item",
tags: "game",
permalink: "catalogue/games/{{ page.fileSlug }}/index.html",
eleventyComputed: {
subtitle: (data) => `${data.platform}`,
},
};

View file

@ -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)

View file

@ -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.

View file

@ -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.

View file

@ -1,6 +0,0 @@
module.exports = {
layout: "layouts/catalogue-item",
tags: "podcast",
permalink: "catalogue/podcasts/{{ page.fileSlug }}/index.html",
linkTitle: "Listen to the podcast",
};

View file

@ -4,7 +4,10 @@ author: Brandon Sanderson
isbn: 9780765326355
rating: 5
image: https://cdn.wonderfulfrog.com/images/the-way-of-kings.jpeg
tags: ["fantasy"]
isFavourite: true
tags:
- fantasy
- fiction
year: 2010
---

View file

@ -5,7 +5,9 @@ author: Seth Godin
isbn: 9781591841661
rating: 4
image: https://cdn.wonderfulfrog.com/images/the-dip.jpeg
tags: ["self help"]
tags:
- self-help
- non-fiction
year: 2007
---

View file

@ -5,7 +5,9 @@ author: Mark Manson
isbn: 9780062457714
rating: 5
image: https://cdn.wonderfulfrog.com/images/subtle-art.jpeg
tags: ["self help"]
tags:
- self-help
- non-fiction
year: 2016
---

View file

@ -3,9 +3,11 @@ title: The Total Money Makeover
subtitle: A Proven Plan for Financial Fitness
author: Dave Ramsey
isbn: 9780785289081
rating: 3
rating: 2
image: https://cdn.wonderfulfrog.com/images/total-money-makeover.webp
tags: ["finance"]
tags:
- non-fiction
- finance
year: 2003
---

View file

@ -5,7 +5,10 @@ author: Gary Taubes
isbn: 9780307272706
rating: 3
image: https://cdn.wonderfulfrog.com/images/why-we-get-fat.jpeg
tags: ["self help", "diet"]
tags:
- non-fiction
- self-help
- diet
year: 2010
---

View file

@ -3,9 +3,13 @@ title: Digital Minimalism
subtitle: Choosing a Focused Life in a Noisy World
author: Cal Newport
isbn: 9780525536512
rating: 4
rating: 2
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
---

View file

@ -4,7 +4,11 @@ author: Brené Brown
isbn: 9781473562523
rating: 5
image: https://cdn.wonderfulfrog.com/images/dare-to-lead.jpeg
tags: ["leadership", "self help", "business"]
tags:
- non-fiction
- self-help
- leadership
- business
year: 2018
---

View file

@ -5,7 +5,12 @@ author: Edmond Lau
isbn: 9780996128100
rating: 4
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
---

View file

@ -3,9 +3,12 @@ title: Atomic Habits
subtitle: An Easy & Proven Way to Build Good Habits & Break Bad Ones
author: James Clear
isbn: 9780735211292
rating: 5
rating: 3
image: https://cdn.wonderfulfrog.com/images/atomic-habits.jpeg
tags: ["psychology", "self help"]
tags:
- non-fiction
- self-help
- psychology
year: 2018
---

View file

@ -4,7 +4,10 @@ author: Ryan Holiday
isbn: 9781591847816
rating: 3
image: https://cdn.wonderfulfrog.com/images/ego-is-the-enemy.jpeg
tags: ["stoicism", "self help"]
tags:
- non-fiction
- self-help
- stoicism
year: 2016
---

View file

@ -4,7 +4,9 @@ author: Marcus Aurelius
isbn: 9780140449334
rating: 2
image: https://cdn.wonderfulfrog.com/images/meditations.jpeg
tags: ["stoicism"]
tags:
- non-fiction
- stoicism
year: 180
---

View file

@ -5,7 +5,10 @@ author: Cal Newport
isbn: 9781455586691
rating: 2
image: https://cdn.wonderfulfrog.com/images/deep-work.jpeg
tags: ["white guy productivity"]
tags:
- non-fiction
- self-help
- white-guy-productivity
year: 2016
---

View file

@ -5,7 +5,10 @@ author: Matthew Walker
isbn: 9781501144318
rating: 5
image: https://cdn.wonderfulfrog.com/images/why-we-sleep.jpeg
tags: ["sleep"]
tags:
- non-fiction
- sleep
- science
year: 2017
---

View file

@ -4,7 +4,9 @@ author: Neil deGrasse Tyson
isbn: 9780393609394
rating: 2
image: https://cdn.wonderfulfrog.com/images/astro-in-hurry.jpeg
tags: ["science"]
tags:
- non-fiction
- science
year: 2017
---

View file

@ -4,7 +4,10 @@ author: Angie Thomas
isbn: 9780062498533
rating: 5
image: https://cdn.wonderfulfrog.com/images/the-hate-u-give.jpg
tags: ["fiction", "racism"]
isFavourite: true
tags:
- fiction
- racism
year: 2017
---

View file

@ -5,7 +5,11 @@ author: Desmond Cole
isbn: 9780385686341
rating: 5
image: https://cdn.wonderfulfrog.com/images/the-skin-were-in.jpeg
tags: ["politics", "canada", "racism"]
tags:
- non-fiction
- racism
- canada
- politics
year: 2020
---

View file

@ -5,7 +5,9 @@ author: Gay Hendricks
isbn: 9780061735363
rating: 3
image: https://cdn.wonderfulfrog.com/images/the-big-leap.jpg
tags: ["self help"]
tags:
- non-fiction
- self-help
year: 2009
---

View file

@ -4,7 +4,10 @@ author: Joshua Whitehead
isbn: 9781551527253
rating: 4
image: https://cdn.wonderfulfrog.com/images/jonny-appleseed.jpeg
tags: ["fictional", "emotional"]
tags:
- fiction
- emotional
- lgbtq
year: 2018
---

View file

@ -1,16 +1,19 @@
---
title: Understanding Comics
subtitle: The Invisible Art
date: 2021-09-09T10:00:00-07:00
author: Scott McCloud
isbn: 9780060976255
rating: 5
image: https://cdn.wonderfulfrog.com/images/understanding-comics.jpg
tags: ["comics", "art", "history"]
tags:
- comics
- art
- history
isFavourite: true
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.
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.

View file

@ -5,7 +5,9 @@ author: Victoria Ortiz
isbn: 978054497364
rating: 4
image: https://cdn.wonderfulfrog.com/images/dissenter-on-the-bench.jpeg
tags: ["biography"]
tags:
- non-fiction
- biography
year: 2019
---

View file

@ -4,7 +4,9 @@ author: Jim Butcher
image: https://cdn.wonderfulfrog.com/images/jim-butcher-peace-talks.jpeg
isbn: 9780393609394
rating: 2
tags: ["fiction", "fantasy"]
tags:
- fiction
- fantasy
year: 2020
---

View file

@ -5,7 +5,10 @@ image: https://cdn.wonderfulfrog.com/images/9780807041307_p0_v1_s1200x630.jpg
url: https://www.yourfatfriend.com/book
isbn: 9780807041307
rating: 5
tags: ["anti-fat"]
tags:
- non-fiction
- diet
- anti-fat
year: 2020
---

View file

@ -5,7 +5,11 @@ author: Johann Hari
isbn: 9781526620224
rating: 4
image: https://cdn.wonderfulfrog.com/images/stolen-focus.jpg
tags: ["focus", "adhd", "psychology"]
tags:
- non-fiction
- adhd
- focus
- psychology
year: 2022
---

View file

@ -0,0 +1,5 @@
export default {
layout: "layouts/book",
permalink: "books/{{ page.fileSlug }}/index.html",
tags: "book",
};

View file

@ -1,8 +1,11 @@
---
title: "Super Mario World 2: Yoshi's Island"
platform: Super Nintendo
image: https://cdn.wonderfulfrog.com/images/Yoshis_Island_box_art.jpg
tags: ["platformer"]
image: https://cdn.wonderfulfrog.com/images/games/yoshis-island-cover.jpg
tags:
- single-player
- platformer
isFavourite: true
year: 1995
---

View file

@ -2,7 +2,9 @@
title: "Majora's Mask 3D"
platform: Nintendo 3DS
image: https://cdn.wonderfulfrog.com/images/Majoras_Mask_3D_cover.png
tags: ["adventure", "single player"]
tags:
- single-player
- adventure
year: 2015
---

View file

@ -2,7 +2,10 @@
title: "Mario & Luigi: Dream Team"
platform: Nintendo 3DS
image: https://cdn.wonderfulfrog.com/images/MarioLuigiDreamTeam.jpg
tags: ["rpg", "turn based", "single player"]
tags:
- single-player
- rpg
- turn-based
year: 2013
---

View file

@ -2,13 +2,17 @@
title: "Donkey Kong Country: Tropical Freeze"
platform: Nintendo Wii U
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
---
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", "", "" %}
![Screenshot of Donkey Kong Country: Tropical Freeze. DK and Diddy are swimming underwater.](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.

View file

@ -2,7 +2,11 @@
title: "Professor Layton vs. Phoenix Wright: Ace Attorney"
platform: Nintendo 3DS
image: https://cdn.wonderfulfrog.com/images/Laytonvsaceattorneycover.jpg
tags: ["puzzle", "narrative", "visual novel", "single player"]
tags:
- single-player
- narrative
- puzzle
- visual-novel
year: 2014
---

View file

@ -2,7 +2,11 @@
title: "The Wonderful 101"
platform: Nintendo Wii U
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
---

View file

@ -2,7 +2,10 @@
title: Fallout 4
platform: PC
image: https://cdn.wonderfulfrog.com/images/Fallout_4_cover_art.jpg
tags: ["rpg", "action", "single player"]
tags:
- single-player
- action
- rpg
year: 2015
---

View file

@ -2,7 +2,11 @@
title: "Persona 3 Portable"
platform: PlayStation Portable
image: https://cdn.wonderfulfrog.com/images/p3p.jpg
tags: ["rpg", "turn based", "persona", "single player"]
tags:
- single-player
- rpg
- turn-based
- persona
youtube: true
year: 2009
---

View file

@ -2,7 +2,10 @@
title: Shovel Knight
platform: Nintendo Wii U
image: https://cdn.wonderfulfrog.com/images/Shovel_knight_cover.jpg
tags: ["platformer", "retro", "single player"]
tags:
- single-player
- platformer
- retro
year: 2014
---

View file

@ -2,7 +2,12 @@
title: Tomb Raider
platform: PC
image: https://cdn.wonderfulfrog.com/images/TombRaider2013.jpg
tags: ["action", "adventure", "puzzle", "reboot", "single player"]
tags:
- single-player
- action
- adventure
- puzzle
- reboot
year: 2013
---

View file

@ -2,7 +2,11 @@
title: "Assassin's Creed IV: Black Flag"
platform: PlayStation 3
image: https://cdn.wonderfulfrog.com/images/Assassins_Creed_IV_-_Black_Flag_cover.jpg
tags: ["action", "adventure", "single player"]
tags:
- single-player
- action
- open-world
- adventure
year: 2013
---

View file

@ -2,7 +2,11 @@
title: DOOM
platform: PC
image: https://cdn.wonderfulfrog.com/images/Doom_Cover.jpg
tags: ["action", "fps", "retro shooter", "single player"]
tags:
- single-player
- fps
- action
- retro-shooter
year: 2016
---

View file

@ -3,7 +3,13 @@ title: Hyper Light Drifter
platform: PC
image: https://cdn.wonderfulfrog.com/images/HyperLightDrifterBoxArt.png
tags:
["action", "hack and slash", "retro", "indie", "open world", "single player"]
- single-player
- action
- retro
- indie
- hack-n-slash
- open-world
- retro
year: 2016
---

View file

@ -2,7 +2,11 @@
title: Vanquish
platform: PlayStation 3
image: https://cdn.wonderfulfrog.com/images/PG_Vanquish_box_artwork.png
tags: ["action", "platinum", "shooter", "single player"]
tags:
- single-player
- action
- shooter
- platinum
year: 2010
---

View file

@ -2,6 +2,10 @@
title: Persona 5
platform: PlayStation 4
image: https://cdn.wonderfulfrog.com/images/Persona_5_cover_art.jpg
tags: ["rpg", "turn based", "single player", "persona"]
tags:
- single-player
- rpg
- turn-based
- persona
year: 2016
---

View file

@ -0,0 +1,12 @@
---
title: Axiom Verge
platform: Nintendo Switch
image: https://cdn.wonderfulfrog.com/images/games/axiom-verge-cover.jpg
tags:
- single-player
- action
- indie
- metroidvania
- retro
year: 2015
---

View file

@ -0,0 +1,12 @@
---
title: "Animal Crossings: New Horizons"
platform: Nintendo Switch
image: https://cdn.wonderfulfrog.com/images/acnh.jpg
tags:
- single-player
- multiplayer
- life-sim
year: 2020
playtime: 400+ hours
pullquote: Idyllic escape from the world
---

View file

@ -1,8 +1,12 @@
---
title: Her Story
platform: PC
image: https://cdn.wonderfulfrog.com/images/Her_Story_store_art.jpg
tags: ["pc", "narrative", "simulator", "indie", "single player"]
image: https://cdn.wonderfulfrog.com/images/games/her-story-cover.jpg
tags:
- single-player
- indie
- simulation
- narrative
year: 2015
---

View file

@ -3,7 +3,11 @@ title: "Middle-earth: Shadow of Mordor"
platform: PC
image: https://cdn.wonderfulfrog.com/images/Shadow_of_Mordor_cover_art.jpg
tags:
["action", "nemesis system", "hack and slash", "single player", "open world"]
- single-player
- action
- hack-n-slash
- open-world
- nemesis-system
year: 2014
---

View file

@ -1,10 +1,17 @@
---
title: Citizen Sleeper
platform: PC
image: https://cdn.wonderfulfrog.com/images/Citizen_Sleeper_cover_art.jpg
tags: ["narrative", "visual novel", "indie", "rpg", "single player"]
image: https://cdn.wonderfulfrog.com/images/games/citizen-sleeper-cover.jpg
tags:
- single-player
- narrative
- visual-novel
- indie
- rpg
year: 2022
rating: 4
playtime: 7 hours
pullquote: Immersive dice simulator
---
I loved the dice system that powered the entire game. It made it feel like you have control over the situation (you can choose where to put your dice rolls), but at the same time none at all (the result is still a dice roll). I especially liked that every choice never felt like a great option. Every choice will inadvertently affect someone or something else negatively. There was never a clear-cut best choice.

View file

@ -0,0 +1,5 @@
export default {
layout: "layouts/game",
permalink: "games/{{ page.fileSlug }}/index.html",
tags: "game",
};

View file

@ -0,0 +1,20 @@
---
title: "Coraline"
year: 2009
watchHistory:
- 2022-01-25
- 2024-08-17
image: https://cdn.wonderfulfrog.com/images/movies/coraline-poster.jpg
letterboxd:
- https://letterboxd.com/wonderfulfrog/film/coraline/
- https://letterboxd.com/wonderfulfrog/film/coraline/1/
director: Henry Selick
runtime: 100
favourite: true
rating: 5
tags:
- animation
- horror
- fantasy
- family
---

View file

@ -0,0 +1,17 @@
---
title: "Megalopolis"
year: 2024
watchHistory:
- 2024-09-30
image: https://cdn.wonderfulfrog.com/images/movies/megalopolis-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/megalopolis-2024/
director: Francis Ford Coppola
runtime: 138
tags:
- drama
- science-fiction
---
I wholeheartedly did not enjoy this movie, and yet afterward we could not stop talking about it. There were so many moments to remember - and some even got overwritten in my mind and had to be teased out later.
I don't know if I'm experiencing a trainwreck or another cultural touchstone...

View file

@ -0,0 +1,13 @@
---
title: "Sugarcane"
year: 2024
watchHistory:
- 2024-09-30
image: https://cdn.wonderfulfrog.com/images/movies/sugarcane-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/sugarcane/
director: Emily Kassie, Julian Brave NoiseCat
runtime: 107
favourite: true
tags:
- documentary
---

View file

@ -0,0 +1,15 @@
---
title: "Your Monster"
year: 2024
watchHistory:
- 2024-10-30
image: https://cdn.wonderfulfrog.com/images/movies/your-monster-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/your-monster-2024/
director: Caroline Lindy
runtime: 104
tags:
- music
- comedy
- horror
- romance
---

View file

@ -0,0 +1,12 @@
---
title: "Dracula A.D. 1972"
year: 1972
watchHistory:
- 2023-10-31
image: https://cdn.wonderfulfrog.com/images/movies/dracula-a-d-1972-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/dracula-ad-1972/
director: Alan Gibson
runtime: 96
tags:
- horror
---

View file

@ -0,0 +1,12 @@
---
title: "Dracula"
year: 1931
watchHistory:
- 2023-10-31
image: https://cdn.wonderfulfrog.com/images/movies/dracula-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/dracula/
director: Tod Browning
runtime: 74
tags:
- horror
---

View file

@ -0,0 +1,16 @@
---
title: "Woman of the Hour"
year: 2023
watchHistory:
- 2024-11-09
image: https://cdn.wonderfulfrog.com/images/movies/woman-of-the-hour-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/woman-of-the-hour/
director: Anna Kendrick
runtime: 94
tags:
- drama
- thriller
- crime
---
Do we really need to keep making movies that center around serial killers? Objectively a good movie, but I'm getting tired of media being created to effectively celebrate the accomplishments of murderers.

View file

@ -0,0 +1,16 @@
---
title: "Tetris"
year: 2023
watchHistory:
- 2024-11-09
image: https://cdn.wonderfulfrog.com/images/movies/tetris-poster.jpg
letterboxd: https://letterboxd.com/wonderfulfrog/film/tetris/
director: Jon S. Baird
runtime: 118
tags:
- thriller
- history
- drama
---
It is now my head canon that securing the publishing rights to Tetris involved a desperate car chase through the streets of Moscow. Theres no going back now.

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