Improvements to a11y and scripts

This commit is contained in:
Bad Manners 2024-08-18 22:52:45 -03:00
parent a335aff2d3
commit d022fab5d6
17 changed files with 384 additions and 214 deletions

View file

@ -78,7 +78,7 @@ export const GET: APIRoute<Props, Params> = async ({ props: { story }, site }) =
commissionersList.map((commissioner) => u(commissioner)),
),
...copyrightedCharacters.map(({ user, characters }) =>
t(lang, "characters/characters_are_copyrighted_by", u(user), characters[0] == "" ? [] : characters),
t(lang, "characters/characters_are_copyrighted_by", u(user), characters[0] === "" ? [] : characters),
),
].reduce(async (promise, data) => {
if (!data) {

View file

@ -2,7 +2,7 @@
import { Image } from "astro:assets";
import { getCollection, getEntries, type CollectionEntry } from "astro:content";
import GalleryLayout from "../layouts/GalleryLayout.astro";
import { DEFAULT_LANG, t } from "../i18n";
import { t } from "../i18n";
import UserComponent from "../components/UserComponent.astro";
type GameWithPubDate = CollectionEntry<"games"> & { data: { pubDate: Date } };
@ -24,7 +24,7 @@ const games = await Promise.all(
<ul class="my-6 flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
{
games.map((game, i) => (
<li class="h-entry">
<li class="h-entry" lang={game.data.lang}>
<a
class="u-url text-link hover:underline focus:underline"
href={`/games/${game.slug}`}
@ -42,17 +42,31 @@ const games = await Promise.all(
</div>
) : null}
<div class="max-w-[288px] text-sm">
<span class="p-name">{game.data.title}</span>
<span class="p-name" aria-label="Title">
{game.data.title}
</span>
<br />
<time class="dt-published italic" datetime={game.data.pubDate.toISOString().slice(0, 10)}>
<time
class="dt-published italic"
datetime={game.data.pubDate.toISOString().slice(0, 10)}
aria-label="Publish date"
>
{game.data.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
</time>
</div>
</a>
<div style={{ display: "none" }}>
{game.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={DEFAULT_LANG} />
))}
<div class="sr-only">
<p class="p-category" aria-label="Category">
Game
</p>
<p class="p-summary" aria-label="Summary">
{t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning)}
</p>
<div aria-label="Authors">
{game.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={game.data.lang} />
))}
</div>
</div>
</li>
))

View file

@ -3,7 +3,7 @@ import type { ImageMetadata } from "astro";
import { type CollectionEntry, type CollectionKey, getCollection, getEntries } from "astro:content";
import { Image } from "astro:assets";
import GalleryLayout from "../layouts/GalleryLayout.astro";
import { DEFAULT_LANG, t, type Lang } from "../i18n";
import { t, type Lang } from "../i18n";
import UserComponent from "../components/UserComponent.astro";
const MAX_ITEMS = 10;
@ -60,7 +60,7 @@ const latestItems: LatestItemsEntry[] = await Promise.all(
href: `/games/${game.slug}`,
title: game.data.title,
authors: await getEntries(game.data.authors),
lang: DEFAULT_LANG,
lang: game.data.lang,
altText: t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning),
pubDate: game.data.pubDate,
}) satisfies LatestItemsEntry,
@ -99,7 +99,7 @@ const latestItems: LatestItemsEntry[] = await Promise.all(
<ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
{
latestItems.map((entry) => (
<li class="h-entry break-inside-avoid">
<li class="h-entry break-inside-avoid" lang={entry.lang}>
<a class="u-url text-link hover:underline focus:underline" href={entry.href} title={entry.altText}>
{entry.thumbnail ? (
<div class="flex aspect-square max-w-[192px] justify-center">
@ -113,20 +113,34 @@ const latestItems: LatestItemsEntry[] = await Promise.all(
</div>
) : null}
<div class="max-w-[192px] text-sm">
<span class="p-name">{entry.title}</span>
<span class="p-name" aria-label="Title">
{entry.title}
</span>
<br />
<span class="italic">
<span class="p-category">{entry.type}</span> &ndash;{" "}
<time class="dt-published" datetime={entry.pubDate.toISOString().slice(0, 10)}>
<span class="p-category" aria-label="Category">
{entry.type}
</span>{" "}
&ndash;{" "}
<time
class="dt-published"
datetime={entry.pubDate.toISOString().slice(0, 10)}
aria-label="Publish date"
>
{entry.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
</time>
</span>
</div>
</a>
<div style={{ display: "none" }}>
{entry.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={entry.lang} />
))}
<div class="sr-only">
<p class="p-summary" aria-label="Summary">
{entry.altText}
</p>
<div aria-label="Authors">
{entry.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={entry.lang} />
))}
</div>
</div>
</li>
))

View file

@ -35,7 +35,7 @@ const totalPages = Math.ceil(page.total / page.size);
<p class="my-4">The bulk of my content!</p>
<p class="text-center font-light text-stone-950 dark:text-white">
{
page.start == page.end
page.start === page.end
? `Displaying story #${page.start + 1}`
: `Displaying stories #${page.start + 1}${page.end + 1}`
} / {page.total}
@ -53,7 +53,7 @@ const totalPages = Math.ceil(page.total / page.size);
[...Array(totalPages).keys()]
.map((p) => p + 1)
.map((p) =>
p == page.currentPage ? (
p === page.currentPage ? (
<span class="border-r border-stone-400 px-4 py-1 font-semibold text-stone-900 dark:border-stone-500 dark:text-stone-50">
{p}
</span>
@ -78,7 +78,7 @@ const totalPages = Math.ceil(page.total / page.size);
<ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
{
page.data.map((story, i) => (
<li class="h-entry break-inside-avoid">
<li class="h-entry break-inside-avoid" lang={story.data.lang}>
<a
class="u-url text-link hover:underline focus:underline"
href={`/stories/${story.slug}`}
@ -96,17 +96,31 @@ const totalPages = Math.ceil(page.total / page.size);
</div>
) : null}
<div class="max-w-[192px] text-sm">
<span class="p-name">{story.data.title}</span>
<span class="p-name" aria-label="Title">
{story.data.title}
</span>
<br />
<time class="italic" datetime={story.data.pubDate.toISOString().slice(0, 10)}>
<time
class="dt-published italic"
datetime={story.data.pubDate.toISOString().slice(0, 10)}
aria-label="Publish date"
>
{story.data.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
</time>
</div>
</a>
<div style={{ display: "none" }}>
{story.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={story.data.lang} />
))}
<div class="sr-only">
<p class="p-category" aria-label="Category">
Story
</p>
<p class="p-summary" aria-label="Summary">
{t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}
</p>
<div aria-label="Authors">
{story.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={story.data.lang} />
))}
</div>
</div>
</li>
))
@ -124,7 +138,7 @@ const totalPages = Math.ceil(page.total / page.size);
[...Array(totalPages).keys()]
.map((p) => p + 1)
.map((p) =>
p == page.currentPage ? (
p === page.currentPage ? (
<span class="border-r border-stone-400 px-4 py-1 font-semibold text-stone-900 dark:border-stone-500 dark:text-stone-50">
{p}
</span>

View file

@ -31,7 +31,7 @@ uncategorizedTagsSet.forEach((tag) => draftOnlyTagsSet.delete(tag));
const uniqueSlugs = new Set<string>();
const categorizedTags = tagCategories
.sort((a, b) => {
if (a.data.index == b.data.index) {
if (a.data.index === b.data.index) {
throw new Error(`Found tag categories with same index value ${a.data.index} ("${a.id}", "${b.id}")`);
}
return a.data.index - b.data.index;

View file

@ -1,13 +1,14 @@
---
import type { GetStaticPaths } from "astro";
import { Image } from "astro:assets";
import { type CollectionEntry, type CollectionKey, getCollection } from "astro:content";
import { type CollectionEntry, type CollectionKey, getCollection, getEntries } from "astro:content";
import { Markdown } from "@astropub/md";
import { slug } from "github-slugger";
import GalleryLayout from "../../layouts/GalleryLayout.astro";
import Prose from "../../components/Prose.astro";
import { t, DEFAULT_LANG } from "../../i18n";
import { qualifyLocalURLsInMarkdown } from "../../utils/qualify_local_urls_in_markdown";
import UserComponent from "../../components/UserComponent.astro";
type EntryWithPubDate<C extends CollectionKey> = CollectionEntry<C> & { data: { pubDate: Date } };
@ -15,8 +16,8 @@ type Props = {
tag: string;
description?: string;
related?: string[];
stories: EntryWithPubDate<"stories">[];
games: EntryWithPubDate<"games">[];
stories: (EntryWithPubDate<"stories"> & { authors: CollectionEntry<"users">[] })[];
games: (EntryWithPubDate<"games"> & { authors: CollectionEntry<"users">[] })[];
};
type Params = {
@ -46,7 +47,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
(acc, category) => {
category.data.tags.forEach(({ name, description, related }) => {
related = related.filter((relatedTag) => {
if (relatedTag == name) {
if (relatedTag === name) {
console.warn(`WARNING: Tag "${name}" should not have itself as a related tag; removing...`);
return false;
}
@ -66,26 +67,42 @@ export const getStaticPaths: GetStaticPaths = async () => {
},
{} as Record<string, { description?: string; related?: string[] }>,
);
return [...tags]
.filter((tag) => !seriesTags.has(tag))
.map((tag) => ({
params: { slug: slug(tag) } satisfies Params,
props: {
tag,
description: tagDescriptions[tag]?.description,
related: tagDescriptions[tag]?.related,
stories: (
stories.filter(
(story) => !story.data.isDraft && story.data.pubDate && story.data.tags.includes(tag),
) as EntryWithPubDate<"stories">[]
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime()),
games: (
games.filter(
(game) => !game.data.isDraft && game.data.pubDate && game.data.tags.includes(tag),
) as EntryWithPubDate<"games">[]
).sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime()),
} satisfies Props,
}));
return await Promise.all(
[...tags]
.filter((tag) => !seriesTags.has(tag))
.map(async (tag) => ({
params: { slug: slug(tag) } satisfies Params,
props: {
tag,
description: tagDescriptions[tag]?.description,
related: tagDescriptions[tag]?.related,
stories: await Promise.all(
(
stories.filter(
(story) => !story.data.isDraft && story.data.pubDate && story.data.tags.includes(tag),
) as EntryWithPubDate<"stories">[]
)
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
.map(async (story) => ({
...story,
authors: await getEntries(story.data.authors),
})),
),
games: await Promise.all(
(
games.filter(
(game) => !game.data.isDraft && game.data.pubDate && game.data.tags.includes(tag),
) as EntryWithPubDate<"games">[]
)
.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime())
.map(async (game) => ({
...game,
authors: await getEntries(game.data.authors),
})),
),
} satisfies Props,
})),
);
};
const { props } = Astro;
@ -130,7 +147,7 @@ const totalWorksWithTag = t(
</h2>
<ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
{props.stories.map((story) => (
<li class="h-entry break-inside-avoid">
<li class="h-entry break-inside-avoid" lang={story.data.lang}>
<a
class="u-url text-link hover:underline focus:underline"
href={`/stories/${story.slug}`}
@ -147,9 +164,15 @@ const totalWorksWithTag = t(
</div>
) : null}
<div class="max-w-[192px] text-sm">
<span class="p-name">{story.data.title}</span>
<span class="p-name" aria-label="Title">
{story.data.title}
</span>
<br />
<time class="dt-published italic" datetime={story.data.pubDate.toISOString().slice(0, 10)}>
<time
class="dt-published italic"
datetime={story.data.pubDate.toISOString().slice(0, 10)}
aria-label="Publish date"
>
{story.data.pubDate.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
@ -158,6 +181,19 @@ const totalWorksWithTag = t(
</time>
</div>
</a>
<div class="sr-only">
<p class="p-category" aria-label="Category">
Story
</p>
<p class="p-summary" aria-label="Summary">
{t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}
</p>
<div aria-label="Authors">
{story.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={story.data.lang} />
))}
</div>
</div>
</li>
))}
</ul>
@ -172,7 +208,7 @@ const totalWorksWithTag = t(
</h2>
<ul class="flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
{props.games.map((game) => (
<li class="h-entry break-inside-avoid">
<li class="h-entry break-inside-avoid" lang={game.data.lang}>
<a
class="u-url text-link hover:underline focus:underline"
href={`/games/${game.slug}`}
@ -189,13 +225,32 @@ const totalWorksWithTag = t(
</div>
) : null}
<div class="max-w-[192px] text-sm">
<span class="p-name">{game.data.title}</span>
<span class="p-name" aria-label="Title">
{game.data.title}
</span>
<br />
<time class="dt-published italic" datetime={game.data.pubDate.toISOString().slice(0, 10)}>
<time
class="dt-published italic"
datetime={game.data.pubDate.toISOString().slice(0, 10)}
aria-label="Publish date"
>
{game.data.pubDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}
</time>
</div>
</a>
<div class="sr-only">
<p class="p-category" aria-label="Category">
Game
</p>
<p class="p-summary" aria-label="Summary">
{t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning)}
</p>
<div aria-label="Authors">
{game.authors.map((author) => (
<UserComponent rel="author" class="p-author" user={author} lang={game.data.lang} />
))}
</div>
</div>
</li>
))}
</ul>