Improved readability and accessibility for CSS-less mode

This commit is contained in:
Bad Manners 2024-08-10 01:17:33 -03:00
parent 6dd8a92318
commit d529b043c6
14 changed files with 166 additions and 94 deletions

View file

@ -40,8 +40,8 @@ npm run export-story -- -o ~/Documents/TO_UPLOAD slug-for-story-to-export
npm run build npm run build
``` ```
Then, after configuring the `gallerybm` host (or the name of your choosing) in `~/.ssh/config`: Then, after configuring the `gallerybm` host (or the name of your choosing) in `~/.ssh/config`, you can use a command like:
```bash ```bash
rsync --delete -acP dist/ gallerybm:/home/public rsync --delete-after -acP dist/ gallerybm:/home/public
``` ```

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "gallery-badmanners-xyz", "name": "gallery-badmanners-xyz",
"version": "1.6.2", "version": "1.6.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "gallery-badmanners-xyz", "name": "gallery-badmanners-xyz",
"version": "1.6.2", "version": "1.6.3",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.2", "@astrojs/check": "^0.9.2",

View file

@ -1,7 +1,7 @@
{ {
"name": "gallery-badmanners-xyz", "name": "gallery-badmanners-xyz",
"type": "module", "type": "module",
"version": "1.6.2", "version": "1.6.3",
"scripts": { "scripts": {
"postinstall": "astro sync", "postinstall": "astro sync",
"dev": "astro dev", "dev": "astro dev",

View file

@ -11,22 +11,22 @@
aria-hidden="false" aria-hidden="false"
> >
<div class="mx-auto flex min-h-screen max-w-3xl flex-col items-center justify-center text-center tracking-tight"> <div class="mx-auto flex min-h-screen max-w-3xl flex-col items-center justify-center text-center tracking-tight">
<div class="h-14 w-14 text-bm-500 sm:h-16 sm:w-16 dark:text-bm-400"> <div class="text-bm-500 dark:text-bm-400">
<svg class="fill-current" viewBox="0 0 512 512"> <svg width="3rem" height="3rem" class="fill-current" viewBox="0 0 512 512">
<path <path
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z" d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
></path> ></path>
</svg> </svg>
</div> </div>
<div class="pb-3 pt-2 text-2xl font-light text-stone-700 sm:pb-4 sm:pt-2 sm:text-3xl dark:text-stone-50"> <div class="pb-3 pt-2 text-3xl font-light text-stone-700 sm:pb-4 sm:pt-2 dark:text-stone-50">
Age verification Age verification
</div> </div>
<div <div
class="mx-6 mb-4 max-w-xl border-b border-stone-300 pb-4 text-base text-stone-700 sm:text-xl dark:border-stone-300 dark:text-stone-50" class="mx-6 mb-4 max-w-xl border-b border-stone-300 pb-4 text-xl text-stone-700 dark:border-stone-300 dark:text-stone-50"
> >
You must be 18+ to access this page. You must be 18+ to access this page.
</div> </div>
<p class="px-8 text-base font-light leading-snug text-stone-700 sm:max-w-2xl sm:text-lg dark:text-stone-50"> <p class="px-8 text-lg font-light leading-snug text-stone-700 sm:max-w-2xl dark:text-stone-50">
By confirming that you are at least 18 years old, your selection will be saved to your browser to prevent this By confirming that you are at least 18 years old, your selection will be saved to your browser to prevent this
screen from appearing in the future. screen from appearing in the future.
</p> </p>

View file

@ -1,23 +1,24 @@
--- ---
import type { CollectionEntry } from "astro:content"; import type { CopyrightedCharacters, Lang } from "../content/config";
import type { Lang } from "../content/config";
import { t } from "../i18n"; import { t } from "../i18n";
import UserComponent from "./UserComponent.astro"; import UserComponent from "./UserComponent.astro";
import CopyrightedCharactersItem from "./CopyrightedCharactersItem.astro"; import CopyrightedCharactersItem from "./CopyrightedCharactersItem.astro";
import { formatCopyrightedCharacters } from "../utils/format_copyrighted_characters";
type Props = { type Props = {
copyrightedCharacters?: Array<[CollectionEntry<"users">, string[]]>; copyrightedCharacters?: CopyrightedCharacters;
lang: Lang; lang: Lang;
}; };
const { copyrightedCharacters, lang } = Astro.props; const { copyrightedCharacters, lang } = Astro.props;
const charactersPerUser = copyrightedCharacters ? await formatCopyrightedCharacters(copyrightedCharacters) : null;
--- ---
{ {
copyrightedCharacters ? ( charactersPerUser ? (
<section id="copyrighted-characters" aria-label={t(lang, "characters/copyrighted_characters_aria_label")}> <section id="copyrighted-characters" aria-label={t(lang, "characters/copyrighted_characters_aria_label")}>
<ul> <ul>
{copyrightedCharacters.map(([owner, characterList]) => ( {charactersPerUser.map(([owner, characterList]) => (
<CopyrightedCharactersItem <CopyrightedCharactersItem
stringFunction={ stringFunction={
characterList[0] === "" characterList[0] === ""

View file

@ -11,6 +11,8 @@
colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
} }
document.querySelectorAll<HTMLElementTagNameMap["button"]>("button[data-dark-mode]").forEach((button) => { document.querySelectorAll<HTMLElementTagNameMap["button"]>("button[data-dark-mode]").forEach((button) => {
button.classList.remove("hidden");
button.removeAttribute("aria-hidden");
button.addEventListener("click", (e) => { button.addEventListener("click", (e) => {
e.preventDefault(); e.preventDefault();
if (colorScheme === "dark") { if (colorScheme === "dark") {

View file

@ -41,7 +41,14 @@ const { link, instance, user, postId } = Astro.props;
</template> </template>
<template id="template-button-loading"> <template id="template-button-loading">
<svg class="-mt-1 mr-1 inline h-5 w-5 animate-spin" fill="none" viewBox="0 0 24 24" aria-hidden> <svg
width="1.25rem"
height="1.25rem"
class="-mt-1 mr-1 inline animate-spin"
fill="none"
viewBox="0 0 24 24"
aria-hidden
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path <path
class="opacity-100" class="opacity-100"
@ -71,7 +78,7 @@ const { link, instance, user, postId } = Astro.props;
<div class="ml-1 flex flex-row pb-2 pt-1"> <div class="ml-1 flex flex-row pb-2 pt-1">
<div class="flex" aria-label="Favorites"> <div class="flex" aria-label="Favorites">
<span data-favorites></span> <span data-favorites></span>
<svg class="ml-2 w-5 fill-current" viewBox="0 0 576 512" aria-hidden> <svg width="1.25rem" class="ml-2 fill-current" viewBox="0 0 576 512" aria-hidden>
<path <path
d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z" d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"
></path> ></path>
@ -79,7 +86,7 @@ const { link, instance, user, postId } = Astro.props;
</div> </div>
<div class="ml-4 flex" aria-label="Reblogs"> <div class="ml-4 flex" aria-label="Reblogs">
<span data-reblogs></span> <span data-reblogs></span>
<svg class="ml-2 w-5 fill-current" viewBox="0 0 512 512" aria-hidden> <svg width="1.25rem" class="ml-2 fill-current" viewBox="0 0 512 512" aria-hidden>
<path <path
d="M0 224c0 17.7 14.3 32 32 32s32-14.3 32-32c0-53 43-96 96-96H320v32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S320 19.1 320 32V64H160C71.6 64 0 135.6 0 224zm512 64c0-17.7-14.3-32-32-32s-32 14.3-32 32c0 53-43 96-96 96H192V352c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V448H352c88.4 0 160-71.6 160-160z" d="M0 224c0 17.7 14.3 32 32 32s32-14.3 32-32c0-53 43-96 96-96H320v32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S320 19.1 320 32V64H160C71.6 64 0 135.6 0 224zm512 64c0-17.7-14.3-32-32-32s-32 14.3-32 32c0 53-43 96-96 96H192V352c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V448H352c88.4 0 160-71.6 160-160z"
></path> ></path>

View file

@ -8,12 +8,9 @@ type Props = {
user: CollectionEntry<"users">; user: CollectionEntry<"users">;
}; };
let { user, lang } = Astro.props; const { user, lang } = Astro.props;
const username = getUsernameForLang(user, lang); const username = getUsernameForLang(user, lang);
let link: string | null = null; const link = user.data.preferredLink ? user.data.links[user.data.preferredLink]!.link : null;
if (user.data.preferredLink) {
link = user.data.links[user.data.preferredLink]!.link;
}
--- ---
{ {

View file

@ -25,8 +25,8 @@ function parseRegex<R extends { [key: string]: string }>(regex: RegExp) {
/** Record of website links for a user. /** Record of website links for a user.
* *
* For each entry, you can enter a URL for the value or - for any key apart * For each entry, you can enter a URL for the value, or a pre-parsed object
* from `website` - a pre-parsed object containing the link and username. * containing a link and a username (except for `website`).
*/ */
const websiteLinks = z.object({ const websiteLinks = z.object({
website: z.object({ link: z.string().url() }).or( website: z.object({ link: z.string().url() }).or(

View file

@ -102,6 +102,10 @@ const UI_STRINGS = {
"published_content/related_games": { "published_content/related_games": {
en: "Related games", en: "Related games",
}, },
"published_content/copyright_aria_label": {
en: "Copyright",
tok: "toki lawa",
},
// Story page-specific strings // Story page-specific strings
"story/return_to_stories": { "story/return_to_stories": {
en: "Return to stories", en: "Return to stories",
@ -138,6 +142,22 @@ const UI_STRINGS = {
en: "Click to reveal", en: "Click to reveal",
tok: "Click to reveal summary in English", tok: "Click to reveal summary in English",
}, },
"story/previous_story": {
en: (title: string) => `Previous: ${title}`,
},
"story/previous_story_aria_label": {
en: "Previous story",
},
"story/next_story": {
en: (title: string) => `Next: ${title}`,
},
"story/next_story_aria_label": {
en: "Next story",
},
"story/information_aria_label": {
en: "Story information",
tok: "sona tan lipu ni",
},
"story/authors": { "story/authors": {
en: (authorsList: string[]) => `by ${UI_STRINGS["util/join_names"].en(authorsList)}`, en: (authorsList: string[]) => `by ${UI_STRINGS["util/join_names"].en(authorsList)}`,
tok: (authorsList: string[]) => tok: (authorsList: string[]) =>
@ -158,6 +178,9 @@ const UI_STRINGS = {
"game/title_aria_label": { "game/title_aria_label": {
en: "Game title", en: "Game title",
}, },
"game/information_aria_label": {
en: "Game information",
},
"game/platforms": { "game/platforms": {
en: (platforms: GamePlatform[]) => { en: (platforms: GamePlatform[]) => {
if (platforms.length == 0) { if (platforms.length == 0) {

View file

@ -3,6 +3,7 @@ import { getImage } from "astro:assets";
import BaseLayout from "./BaseLayout.astro"; import BaseLayout from "./BaseLayout.astro";
import Navigation from "../components/Navigation.astro"; import Navigation from "../components/Navigation.astro";
import logoBM from "../assets/images/logo_bm.png"; import logoBM from "../assets/images/logo_bm.png";
import { t } from "../i18n";
type Props = { type Props = {
pageTitle?: string; pageTitle?: string;
@ -45,31 +46,34 @@ const copyrightYear = currentYear > FIRST_YEAR ? `${FIRST_YEAR}${currentYear}
<a class="hover:underline focus:underline" href="/licenses.txt" target="_blank">Licenses</a> <a class="hover:underline focus:underline" href="/licenses.txt" target="_blank">Licenses</a>
</div> </div>
<div class="mt-2 flex items-center gap-x-1 pb-10"> <div class="mt-2 flex items-center gap-x-1 pb-10">
<a class="text-link p-1" href="https://badmanners.xyz/" target="_blank" aria-label="Main website"> <a class="text-link p-1" href="https://badmanners.xyz/" target="_blank" aria-labelled-by="label-main-website">
<svg viewBox="0 0 576 512" class="h-6 w-6 fill-current" aria-hidden="true"> <svg width="1.5rem" height="1.5rem" viewBox="0 0 576 512" class="fill-current" aria-hidden>
<path <path
d="M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40H456c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1H416 392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32 14.3-32 32v64 24c0 22.1-17.9 40-40 40H160 128.1c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2H104c-22.1 0-40-17.9-40-40V360c0-.9 0-1.9 .1-2.8V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z" d="M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40H456c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1H416 392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32 14.3-32 32v64 24c0 22.1-17.9 40-40 40H160 128.1c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2H104c-22.1 0-40-17.9-40-40V360c0-.9 0-1.9 .1-2.8V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"
></path> ></path>
</svg> </svg>
<span id="label-main-website" class="hidden">Main website</span>
</a> </a>
<a class="text-link p-1" href="/feed.xml" target="_blank" aria-label="RSS feed"> <a class="text-link p-1" href="/feed.xml" target="_blank" aria-labelledby="label-rss-feed">
<svg viewBox="0 0 448 512" class="h-6 w-6 fill-current" aria-hidden="true"> <svg width="1.5rem" height="1.5rem" viewBox="0 0 448 512" class="fill-current" aria-hidden>
<path <path
d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"
></path> ></path>
</svg> </svg>
<span id="label-rss-feed" class="hidden">RSS feed</span>
</a> </a>
<button data-dark-mode class="text-link p-1" aria-label="Toggle dark mode"> <button data-dark-mode class="text-link hidden p-1" aria-labelledby="label-toggle-dark-mode" aria-hidden>
<svg viewBox="0 0 512 512" class="hidden h-6 w-6 fill-current dark:block" aria-hidden="true"> <svg width="1.5rem" height="1.5rem" viewBox="0 0 512 512" class="hidden fill-current dark:block" aria-hidden>
<path <path
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z" d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"
></path> ></path>
</svg> </svg>
<svg viewBox="0 0 512 512" class="block h-6 w-6 fill-current dark:hidden" aria-hidden="true"> <svg width="1.5rem" height="1.5rem" viewBox="0 0 512 512" class="block fill-current dark:hidden" aria-hidden>
<path <path
d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z" d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"
></path> ></path>
</svg> </svg>
<span id="label-toggle-dark-mode" class="hidden">{t("en", "published_content/toggle_dark_mode")}</span>
</button> </button>
</div> </div>
</div> </div>

View file

@ -10,14 +10,12 @@ import CopyrightedCharacters from "../components/CopyrightedCharacters.astro";
import Prose from "../components/Prose.astro"; import Prose from "../components/Prose.astro";
import MastodonComments from "../components/MastodonComments.astro"; import MastodonComments from "../components/MastodonComments.astro";
import UserComponent from "../components/UserComponent.astro"; import UserComponent from "../components/UserComponent.astro";
import { formatCopyrightedCharacters } from "../utils/format_copyrighted_characters";
type Props = CollectionEntry<"games">["data"]; type Props = CollectionEntry<"games">["data"];
const { props } = Astro; const { props } = Astro;
const series = props.series && (await getEntry(props.series)); const series = props.series && (await getEntry(props.series));
const authorsList = await getEntries(props.authors); const authorsList = await getEntries(props.authors);
const copyrightedCharacters = await formatCopyrightedCharacters(props.copyrightedCharacters);
const relatedStories = (await getEntries(props.relatedStories)).filter((story) => !story.data.isDraft); const relatedStories = (await getEntries(props.relatedStories)).filter((story) => !story.data.isDraft);
const relatedGames = (await getEntries(props.relatedGames)).filter((game) => !game.data.isDraft); const relatedGames = (await getEntries(props.relatedGames)).filter((game) => !game.data.isDraft);
const categorizedTags = Object.fromEntries( const categorizedTags = Object.fromEntries(
@ -75,45 +73,62 @@ const thumbnail =
<div <div
class="pointer-events-auto sticky top-6 flex rounded-full bg-white px-1 py-1 shadow-md dark:bg-black print:hidden" class="pointer-events-auto sticky top-6 flex rounded-full bg-white px-1 py-1 shadow-md dark:bg-black print:hidden"
> >
<a <a href={series ? series.data.url : "/games"} class="text-link my-1 p-2" aria-labelled-by="label-return-to">
href={series ? series.data.url : "/games"} <svg width="1.25rem" height="1.25rem" viewBox="0 0 512 512" class="fill-current" aria-hidden>
class="text-link my-1 h-9 w-9 p-2"
aria-label={series
? t(props.lang, "published_content/return_to_series", series.data.name)
: t(props.lang, "game/return_to_games")}
>
<svg viewBox="0 0 512 512" class="fill-current" aria-hidden="true">
<path <path
d="M48.5 224H40c-13.3 0-24-10.7-24-24V72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8H48.5z" d="M48.5 224H40c-13.3 0-24-10.7-24-24V72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8H48.5z"
></path> ></path>
</svg> </svg>
<span class="hidden" id="label-return-to">
{
series
? t(props.lang, "published_content/return_to_series", series.data.name)
: t(props.lang, "game/return_to_games")
}
</span>
</a> </a>
<a <a
href="#description" href="#description"
class="text-link my-1 h-9 w-9 border-l border-stone-300 p-2 dark:border-stone-700" class="text-link my-1 border-l border-stone-300 p-2 dark:border-stone-700"
aria-label={t(props.lang, "published_content/go_to_description")} aria-labelled-by="label-go-to-description"
> >
<svg viewBox="0 0 512 512" class="fill-current" aria-hidden="true"> <svg width="1.25rem" height="1.25rem" viewBox="0 0 512 512" class="fill-current" aria-hidden>
<path <path
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"
></path> ></path>
</svg> </svg>
<span class="hidden" id="label-go-to-description">{t(props.lang, "published_content/go_to_description")}</span
>
</a> </a>
<button <button
data-dark-mode data-dark-mode
class="text-link my-1 h-9 w-9 border-l border-stone-300 p-2 dark:border-stone-700" class="text-link my-1 hidden border-l border-stone-300 p-2 dark:border-stone-700"
aria-label={t(props.lang, "published_content/toggle_dark_mode")} aria-labelled-by="label-toggle-dark-mode"
aria-hidden
> >
<svg viewBox="0 0 512 512" class="hidden fill-current dark:block" aria-hidden="true"> <svg
width="1.25rem"
height="1.25rem"
viewBox="0 0 512 512"
class="hidden fill-current dark:block"
aria-hidden
>
<path <path
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z" d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"
></path> ></path>
</svg> </svg>
<svg viewBox="0 0 512 512" class="block fill-current dark:hidden" aria-hidden="true"> <svg
width="1.25rem"
height="1.25rem"
viewBox="0 0 512 512"
class="block fill-current dark:hidden"
aria-hidden
>
<path <path
d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z" d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"
></path> ></path>
</svg> </svg>
<span class="hidden" id="label-toggle-dark-mode">{t(props.lang, "published_content/toggle_dark_mode")}</span>
</button> </button>
</div> </div>
</div> </div>
@ -132,6 +147,7 @@ const thumbnail =
<section <section
id="game-information" id="game-information"
class="mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200" class="mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200"
aria-label={t(props.lang, "game/information_aria_label")}
> >
<Authors lang={props.lang}> <Authors lang={props.lang}>
{authorsList.map((author) => <UserComponent user={author} lang={props.lang} />)} {authorsList.map((author) => <UserComponent user={author} lang={props.lang} />)}
@ -206,12 +222,12 @@ const thumbnail =
</h2> </h2>
<Prose> <Prose>
<Markdown of={props.description} /> <Markdown of={props.description} />
<CopyrightedCharacters copyrightedCharacters={copyrightedCharacters} lang={props.lang} /> <CopyrightedCharacters copyrightedCharacters={props.copyrightedCharacters} lang={props.lang} />
</Prose> </Prose>
</section> </section>
<div class="pr-3 text-right print:hidden"> <div class="pr-3 text-right print:hidden">
<a href="#top" class="text-link inline-flex items-center underline" <a href="#top" class="text-link inline-flex items-center underline"
><svg class="mr-1 h-6 w-6 fill-current" viewBox="0 0 384 512" aria-hidden="true" ><svg width="1.25rem" height="1.25rem" class="mr-1 fill-current" viewBox="0 0 384 512" aria-hidden
><path ><path
d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z" d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"
></path></svg ></path></svg
@ -274,7 +290,10 @@ const thumbnail =
} }
{props.posts.mastodon ? <MastodonComments lang={props.lang} {...props.posts.mastodon} /> : null} {props.posts.mastodon ? <MastodonComments lang={props.lang} {...props.posts.mastodon} /> : null}
</main> </main>
<div class="pt-6 text-center text-xs text-black dark:text-white"> <div
class="pt-6 text-center text-xs text-black dark:text-white"
aria-label={t(props.lang, "published_content/copyright_aria_label")}
>
<span>{t(props.lang, "published_content/copyright_year", (props.pubDate || new Date()).getFullYear())} | </span> <span>{t(props.lang, "published_content/copyright_year", (props.pubDate || new Date()).getFullYear())} | </span>
<a class="hover:underline focus:underline" href="/licenses.txt" target="_blank" <a class="hover:underline focus:underline" href="/licenses.txt" target="_blank"
>{t(props.lang, "published_content/licenses")}</a >{t(props.lang, "published_content/licenses")}</a

View file

@ -12,7 +12,6 @@ import UserComponent from "../components/UserComponent.astro";
import CopyrightedCharacters from "../components/CopyrightedCharacters.astro"; import CopyrightedCharacters from "../components/CopyrightedCharacters.astro";
import Prose from "../components/Prose.astro"; import Prose from "../components/Prose.astro";
import MastodonComments from "../components/MastodonComments.astro"; import MastodonComments from "../components/MastodonComments.astro";
import { formatCopyrightedCharacters } from "../utils/format_copyrighted_characters";
type Props = CollectionEntry<"stories">["data"]; type Props = CollectionEntry<"stories">["data"];
@ -23,7 +22,6 @@ const series = props.series && (await getEntry(props.series));
const authorsList = await getEntries(props.authors); const authorsList = await getEntries(props.authors);
const commissionersList = props.commissioner && (await getEntries(props.commissioner)); const commissionersList = props.commissioner && (await getEntries(props.commissioner));
const requestersList = props.requester && (await getEntries(props.requester)); const requestersList = props.requester && (await getEntries(props.requester));
const copyrightedCharacters = await formatCopyrightedCharacters(props.copyrightedCharacters);
const relatedStories = (await getEntries(props.relatedStories)).filter((story) => !story.data.isDraft); const relatedStories = (await getEntries(props.relatedStories)).filter((story) => !story.data.isDraft);
const relatedGames = (await getEntries(props.relatedGames)).filter((game) => !game.data.isDraft); const relatedGames = (await getEntries(props.relatedGames)).filter((game) => !game.data.isDraft);
const categorizedTags = Object.fromEntries( const categorizedTags = Object.fromEntries(
@ -82,45 +80,62 @@ const wordCount = props.wordCount?.toString();
<div <div
class="pointer-events-auto sticky top-6 flex rounded-full bg-white px-1 py-1 shadow-md dark:bg-black print:hidden" class="pointer-events-auto sticky top-6 flex rounded-full bg-white px-1 py-1 shadow-md dark:bg-black print:hidden"
> >
<a <a href={series ? series.data.url : "/stories/1"} class="text-link my-1 p-2" aria-labelled-by="label-return-to">
href={series ? series.data.url : "/stories/1"} <svg width="1.25rem" height="1.25rem" viewBox="0 0 512 512" class="fill-current" aria-hidden>
class="text-link my-1 h-9 w-9 p-2"
aria-label={series
? t(props.lang, "published_content/return_to_series", series.data.name)
: t(props.lang, "story/return_to_stories")}
>
<svg viewBox="0 0 512 512" class="fill-current" aria-hidden="true">
<path <path
d="M48.5 224H40c-13.3 0-24-10.7-24-24V72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8H48.5z" d="M48.5 224H40c-13.3 0-24-10.7-24-24V72c0-9.7 5.8-18.5 14.8-22.2s19.3-1.7 26.2 5.2L98.6 96.6c87.6-86.5 228.7-86.2 315.8 1c87.5 87.5 87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3c-62.2-62.2-162.7-62.5-225.3-1L185 183c6.9 6.9 8.9 17.2 5.2 26.2s-12.5 14.8-22.2 14.8H48.5z"
></path> ></path>
</svg> </svg>
<span class="hidden" id="label-return-to"
>{
series
? t(props.lang, "published_content/return_to_series", series.data.name)
: t(props.lang, "story/return_to_stories")
}</span
>
</a> </a>
<a <a
href="#description" href="#description"
class="text-link my-1 h-9 w-9 border-l border-stone-300 p-2 dark:border-stone-700" class="text-link my-1 border-l border-stone-300 p-2 dark:border-stone-700"
aria-label={t(props.lang, "published_content/go_to_description")} aria-labelled-by="label-go-to-description"
> >
<svg viewBox="0 0 512 512" class="fill-current" aria-hidden="true"> <svg width="1.25rem" height="1.25rem" viewBox="0 0 512 512" class="fill-current" aria-hidden>
<path <path
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z" d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"
></path> ></path>
</svg> </svg>
<span class="hidden" id="label-go-to-description">{t(props.lang, "published_content/go_to_description")}</span
>
</a> </a>
<button <button
data-dark-mode data-dark-mode
class="text-link my-1 h-9 w-9 border-l border-stone-300 p-2 dark:border-stone-700" class="text-link my-1 hidden border-l border-stone-300 p-2 dark:border-stone-700"
aria-label={t(props.lang, "published_content/toggle_dark_mode")} aria-labelled-by="label-toggle-dark-mode"
aria-hidden
> >
<svg viewBox="0 0 512 512" class="hidden fill-current dark:block" aria-hidden="true"> <svg
width="1.25rem"
height="1.25rem"
viewBox="0 0 512 512"
class="hidden fill-current dark:block"
aria-hidden
>
<path <path
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z" d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"
></path> ></path>
</svg> </svg>
<svg viewBox="0 0 512 512" class="block fill-current dark:hidden" aria-hidden="true"> <svg
width="1.25rem"
height="1.25rem"
viewBox="0 0 512 512"
class="block fill-current dark:hidden"
aria-hidden
>
<path <path
d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z" d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"
></path> ></path>
</svg> </svg>
<span class="hidden" id="label-toggle-dark-mode">{t(props.lang, "published_content/toggle_dark_mode")}</span>
</button> </button>
</div> </div>
</div> </div>
@ -133,31 +148,33 @@ const wordCount = props.wordCount?.toString();
(prev && !prev.data.isDraft) || (next && !next.data.isDraft) ? ( (prev && !prev.data.isDraft) || (next && !next.data.isDraft) ? (
<div class="print:hidden"> <div class="print:hidden">
<div id="story-nav-top" class="my-4 grid grid-cols-2 justify-items-stretch gap-2"> <div id="story-nav-top" class="my-4 grid grid-cols-2 justify-items-stretch gap-2">
{prev ? ( {prev && !prev.data.isDraft ? (
<a <a
href={`/stories/${prev.slug}`} href={`/stories/${prev.slug}`}
class="text-link flex items-center justify-center border-r border-stone-400 px-1 py-3 font-light underline dark:border-stone-600" class="text-link flex items-center justify-center border-r border-stone-400 px-1 py-3 font-light underline dark:border-stone-600"
aria-label={t(props.lang, "story/previous_story_aria_label")}
> >
<svg class="mr-1 h-5 w-5 fill-current" viewBox="0 0 320 512"> <svg width="1.25rem" height="1.25rem" class="mr-1 fill-current" viewBox="0 0 320 512" aria-hidden>
<path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z" /> <path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z" />
</svg> </svg>
<span>Previous: {prev.data.shortTitle || prev.data.title}</span> <span>{t(props.lang, "story/previous_story", prev.data.shortTitle || prev.data.title)}</span>
</a> </a>
) : ( ) : (
<div class="h-full border-r border-stone-400 dark:border-stone-600" /> <div class="h-full border-r border-stone-400 dark:border-stone-600" aria-hidden />
)} )}
{next ? ( {next && !next.data.isDraft ? (
<a <a
href={`/stories/${next.slug}`} href={`/stories/${next.slug}`}
class="text-link flex items-center justify-center px-1 py-3 font-light underline" class="text-link flex items-center justify-center px-1 py-3 font-light underline"
aria-label={t(props.lang, "story/next_story_aria_label")}
> >
<span>Next: {next.data.shortTitle || next.data.title}</span> <span>{t(props.lang, "story/next_story", next.data.shortTitle || next.data.title)}</span>
<svg class="ml-1 h-5 w-5 fill-current" viewBox="0 0 320 512"> <svg width="1.25rem" height="1.25rem" class="ml-1 fill-current" viewBox="0 0 320 512" aria-hidden>
<path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" /> <path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" />
</svg> </svg>
</a> </a>
) : ( ) : (
<div /> <div aria-hidden />
)} )}
</div> </div>
<hr class="mx-auto mb-5 w-full max-w-2xl border-stone-400 dark:border-stone-600" /> <hr class="mx-auto mb-5 w-full max-w-2xl border-stone-400 dark:border-stone-600" />
@ -174,6 +191,7 @@ const wordCount = props.wordCount?.toString();
<section <section
id="story-information" id="story-information"
class="mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200" class="mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200"
aria-label={t(props.lang, "story/information_aria_label")}
> >
<Authors lang={props.lang}> <Authors lang={props.lang}>
{authorsList.map((author) => <UserComponent user={author} lang={props.lang} />)} {authorsList.map((author) => <UserComponent user={author} lang={props.lang} />)}
@ -260,7 +278,7 @@ const wordCount = props.wordCount?.toString();
</h2> </h2>
<Prose> <Prose>
<Markdown of={props.description} /> <Markdown of={props.description} />
<CopyrightedCharacters copyrightedCharacters={copyrightedCharacters} lang={props.lang} /> <CopyrightedCharacters copyrightedCharacters={props.copyrightedCharacters} lang={props.lang} />
</Prose> </Prose>
</section> </section>
{ {
@ -283,12 +301,12 @@ const wordCount = props.wordCount?.toString();
) : null ) : null
} }
<div class="pr-3 text-right print:hidden"> <div class="pr-3 text-right print:hidden">
<a href="#top" class="text-link inline-flex items-center underline" <a href="#top" class="text-link inline-flex items-center underline" aria-labelledby="label-to-top"
><svg class="mr-1 h-6 w-6 fill-current" viewBox="0 0 384 512" aria-hidden="true" ><svg width="1.25rem" height="1.25rem" class="mr-1 fill-current" viewBox="0 0 384 512" aria-hidden
><path ><path
d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z" d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"
></path></svg ></path></svg
><span>{t(props.lang, "published_content/to_top")}</span></a ><span id="label-to-top">{t(props.lang, "published_content/to_top")}</span></a
> >
</div> </div>
{ {
@ -300,11 +318,12 @@ const wordCount = props.wordCount?.toString();
<a <a
href={`/stories/${prev.slug}`} href={`/stories/${prev.slug}`}
class="text-link flex items-center justify-center border-r border-stone-400 px-1 py-3 font-light underline dark:border-stone-600" class="text-link flex items-center justify-center border-r border-stone-400 px-1 py-3 font-light underline dark:border-stone-600"
aria-label={t(props.lang, "story/previous_story_aria_label")}
> >
<svg class="mr-1 h-5 w-5 fill-current" viewBox="0 0 320 512"> <svg width="1.25rem" height="1.25rem" class="mr-1 fill-current" viewBox="0 0 320 512" aria-hidden>
<path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z" /> <path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z" />
</svg> </svg>
<span>Previous: {prev.data.shortTitle || prev.data.title}</span> <span>{t(props.lang, "story/previous_story", prev.data.shortTitle || prev.data.title)}</span>
</a> </a>
) : ( ) : (
<div class="h-full border-r border-stone-400 dark:border-stone-600" /> <div class="h-full border-r border-stone-400 dark:border-stone-600" />
@ -313,9 +332,10 @@ const wordCount = props.wordCount?.toString();
<a <a
href={`/stories/${next.slug}`} href={`/stories/${next.slug}`}
class="text-link flex items-center justify-center px-1 py-3 font-light underline" class="text-link flex items-center justify-center px-1 py-3 font-light underline"
aria-label={t(props.lang, "story/next_story_aria_label")}
> >
<span>Next: {next.data.shortTitle || next.data.title}</span> <span>{t(props.lang, "story/next_story", next.data.shortTitle || next.data.title)}</span>
<svg class="ml-1 h-5 w-5 fill-current" viewBox="0 0 320 512"> <svg width="1.25rem" height="1.25rem" class="ml-1 fill-current" viewBox="0 0 320 512" aria-hidden>
<path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" /> <path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" />
</svg> </svg>
</a> </a>
@ -383,7 +403,10 @@ const wordCount = props.wordCount?.toString();
} }
{props.posts.mastodon ? <MastodonComments lang={props.lang} {...props.posts.mastodon} /> : null} {props.posts.mastodon ? <MastodonComments lang={props.lang} {...props.posts.mastodon} /> : null}
</main> </main>
<div class="pt-6 text-center text-xs text-black dark:text-white"> <div
class="pt-6 text-center text-xs text-black dark:text-white"
aria-label={t(props.lang, "published_content/copyright_aria_label")}
>
<span>{t(props.lang, "published_content/copyright_year", (props.pubDate || new Date()).getFullYear())} | </span> <span>{t(props.lang, "published_content/copyright_year", (props.pubDate || new Date()).getFullYear())} | </span>
<a class="hover:underline focus:underline" href="/licenses.txt" target="_blank" <a class="hover:underline focus:underline" href="/licenses.txt" target="_blank"
>{t(props.lang, "published_content/licenses")}</a >{t(props.lang, "published_content/licenses")}</a

View file

@ -8,7 +8,7 @@ const MAX_ITEMS = 8;
interface LatestItemsEntry { interface LatestItemsEntry {
type: string; type: string;
thumbnail: CollectionEntry<"stories">["data"]["thumbnail"]; thumbnail?: ImageMetadata;
href: string; href: string;
title: string; title: string;
altText: string; altText: string;
@ -57,13 +57,9 @@ const latestItems: LatestItemsEntry[] = [
<GalleryLayout pageTitle="Gallery"> <GalleryLayout pageTitle="Gallery">
<meta slot="head-description" property="og:description" content="Bad Manners || Welcome to my gallery!" /> <meta slot="head-description" property="og:description" content="Bad Manners || Welcome to my gallery!" />
<h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">My gallery</h1> <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">My gallery</h1>
<p class="my-4 block md:hidden"> <p class="my-4">
Welcome to my gallery! You can expect lots of safe vore/endosoma ahead. Use the navigation menu above to navigate Welcome to my gallery! You can expect lots of safe vore/endosoma ahead. Use the navigation menu to navigate through
through my content. my content.
</p>
<p class="my-4 hidden md:block" aria-hidden>
Welcome to my gallery! You can expect lots of safe vore/endosoma ahead. Use the navigation menu to the left to
navigate through my content.
</p> </p>
<ul class="list-disc pl-8"> <ul class="list-disc pl-8">
<li><a class="text-link underline" href="/stories/1">Read my stories!</a></li> <li><a class="text-link underline" href="/stories/1">Read my stories!</a></li>