Add Tippy.js tooltips to focusable elements

This commit is contained in:
Bad Manners 2024-09-07 18:28:08 -03:00
parent a2fbf908aa
commit 09934a9f8e
Signed by: badmanners
GPG key ID: 8C88292CCB075609
11 changed files with 73 additions and 29 deletions

20
package-lock.json generated
View file

@ -27,6 +27,7 @@
"sanitize-html": "^2.13.0",
"tailwindcss": "^3.4.9",
"tiny-decode": "^0.1.3",
"tippy.js": "^6.3.7",
"toml": "^3.0.0",
"typescript": "^5.5.4"
},
@ -1686,6 +1687,16 @@
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ=="
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz",
@ -6737,6 +6748,15 @@
"entities": "^4.4.0"
}
},
"node_modules/tippy.js": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.9.0"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",

View file

@ -34,6 +34,7 @@
"sanitize-html": "^2.13.0",
"tailwindcss": "^3.4.9",
"tiny-decode": "^0.1.3",
"tippy.js": "^6.3.7",
"toml": "^3.0.0",
"typescript": "^5.5.4"
},

View file

@ -6,6 +6,6 @@ tags:
- name: commission
description: Stories made as part of a commission to someone else.
- name: { en: flash fiction, tok: lipu lili }
description: Short-format stories of no more than 2,500 words.
description: Short-format stories with no more than 2,500 words.
- name: toki pona
description: Stories written in toki pona, the language of good.

View file

@ -143,6 +143,7 @@ const isCurrentRoute = (path: string) =>
</main>
</div>
</BaseLayout>
<style>
nav ul li a,
nav ul li button {
@ -159,3 +160,17 @@ const isCurrentRoute = (path: string) =>
height: 1ex;
}
</style>
<script>
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
const clipboardItems = document.querySelectorAll<HTMLElement>("[data-clipboard]");
tippy(clipboardItems, {
content: (el) => (el as HTMLElement).dataset.clipboard!,
theme: "bm",
});
clipboardItems.forEach((el) => el.removeAttribute("title"));
</script>

View file

@ -82,6 +82,10 @@ const tags = props.tags.map<{ id: string; name: string }>((tag) => {
const thumbnail =
props.thumbnail &&
(await getImage({ src: props.thumbnail, width: props.thumbnailWidth, height: props.thumbnailHeight }));
const returnTo = series
? t(props.lang, "published_content/return_to_series", series.data.name)
: props.labelReturnTo.title;
---
<BaseLayout pageTitle={props.title} lang={props.lang}>
@ -115,39 +119,24 @@ const thumbnail =
<div
class="pointer-events-auto sticky top-6 flex rounded-full bg-white px-1 py-1 shadow-md dark:bg-black print:hidden"
>
<a
href={series ? series.data.link : props.labelReturnTo.link}
class="text-link my-1 p-2"
aria-labelledby="label-return-to"
>
<a href={series ? series.data.link : props.labelReturnTo.link} class="text-link my-1 p-2" aria-label={returnTo}>
<IconArrowBack width="1.25rem" height="1.25rem" />
<span class="sr-only select-none" id="label-return-to"
>{
series ? t(props.lang, "published_content/return_to_series", series.data.name) : props.labelReturnTo.title
}</span
>
</a>
<a
href="#description"
class="text-link my-1 border-l border-stone-300 p-2 dark:border-stone-700"
aria-labelledby="label-go-to-description"
aria-label={t(props.lang, "published_content/go_to_description")}
>
<IconCircleInfo width="1.25rem" height="1.25rem" />
<span class="sr-only select-none" id="label-go-to-description"
>{t(props.lang, "published_content/go_to_description")}</span
>
</a>
<button
data-dark-mode
style={{ display: "none" }}
class="text-link my-1 border-l border-stone-300 p-2 dark:border-stone-700"
aria-labelledby="label-toggle-dark-mode"
aria-label={t(props.lang, "published_content/toggle_dark_mode")}
>
<IconSun width="1.25rem" height="1.25rem" class="hidden dark:block" />
<IconMoon width="1.25rem" height="1.25rem" class="block dark:hidden" />
<span class="sr-only select-none" id="label-toggle-dark-mode"
>{t(props.lang, "published_content/toggle_dark_mode")}</span
>
</button>
</div>
</div>
@ -195,15 +184,15 @@ const thumbnail =
}
<h1
id="section-title"
class="p-name px-1 pt-4 font-serif text-4xl font-semibold text-stone-800 dark:text-stone-100"
class="p-name px-1 pt-4 font-serif text-4xl font-medium text-stone-800 dark:text-stone-200"
aria-label={props.labelTitleSection}
>
{props.title}
</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] w-1/2 rounded-sm bg-stone-800 dark:bg-stone-100" />
<hr class="mb-3 ml-[2px] mt-2 h-[4px] w-1/2 rounded-sm bg-stone-800 dark:bg-stone-200" />
<section
id="section-information"
class="p-summary mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200"
class="p-summary mt-1 space-y-2 px-2 font-serif font-light italic text-stone-700 dark:text-stone-300"
aria-label={props.labelInformationSection}
>
<slot name="section-information" />
@ -402,3 +391,14 @@ const thumbnail =
</div>
</div>
</BaseLayout>
<script>
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
tippy("#toolbox-buttons :is(a, button)[aria-label]", {
content: (el) => el.getAttribute("aria-label")!,
placement: "bottom",
theme: "bm",
});
</script>

View file

@ -30,6 +30,7 @@ const games = await Promise.all(
class="u-url text-link hover:underline focus:underline"
href={`/games/${game.slug}`}
title={t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning)}
data-clipboard={t(game.data.lang, "game/warnings", game.data.platforms, game.data.contentWarning)}
>
{game.data.thumbnail ? (
<div class="flex aspect-[630/500] max-w-[288px] justify-center">

View file

@ -102,7 +102,12 @@ const latestItems: LatestItemsEntry[] = await Promise.all(
{
latestItems.map((entry) => (
<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}>
<a
class="u-url text-link hover:underline focus:underline"
href={entry.href}
title={entry.altText}
data-clipboard={entry.altText}
>
{entry.thumbnail ? (
<div class="flex aspect-square max-w-[192px] justify-center">
<Image

View file

@ -88,6 +88,7 @@ const totalPages = Math.ceil(page.total / page.size);
class="u-url text-link hover:underline focus:underline"
href={`/stories/${story.slug}`}
title={t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}
data-clipboard={t(story.data.lang, "story/warnings", story.data.wordCount, story.data.contentWarning)}
>
{story.data.thumbnail ? (
<div class="flex aspect-square max-w-[192px] justify-center">

View file

@ -102,6 +102,7 @@ if (uncategorizedTagsSet.size > 0) {
class="rounded-full bg-bm-300 px-3 py-1 text-sm text-black shadow-sm hover:underline focus:underline dark:bg-bm-600 dark:text-white"
href={`/tags/${id}`}
title={description}
data-clipboard={description}
>
{name}
</a>

View file

@ -4,19 +4,19 @@
/* Tippy tooltips */
.tippy-box[data-theme~="bm"] {
@apply bg-stone-800 font-sans text-sm text-stone-50 dark:bg-zinc-900 dark:text-zinc-100;
@apply bg-stone-900 font-sans text-sm text-stone-50 dark:bg-zinc-700 dark:text-zinc-50;
}
.tippy-box[data-theme~="bm"][data-placement^="top"] > .tippy-arrow::before {
@apply border-t-stone-800 dark:border-t-zinc-900;
@apply border-t-stone-900 dark:border-t-zinc-700;
}
.tippy-box[data-theme~="bm"][data-placement^="bottom"] > .tippy-arrow::before {
@apply border-b-stone-800 dark:border-b-zinc-900;
@apply border-b-stone-900 dark:border-b-zinc-700;
}
.tippy-box[data-theme~="bm"][data-placement^="left"] > .tippy-arrow::before {
@apply border-l-stone-800 dark:border-l-zinc-900;
@apply border-l-stone-900 dark:border-l-zinc-700;
}
.tippy-box[data-theme~="bm"][data-placement^="right"] > .tippy-arrow::before {
@apply border-r-stone-800 dark:border-r-zinc-900;
@apply border-r-stone-900 dark:border-r-zinc-700;
}
@layer components {

View file

@ -70,5 +70,5 @@ export default {
}),
},
},
plugins: [require("@tailwindcss/typography")],
plugins: [typography],
};