Improve age-restricted hyperlinks, clean up markup, and add navigation icons

This commit is contained in:
Bad Manners 2024-08-25 16:18:13 -03:00
parent c55c82633d
commit 57c2c7c649
31 changed files with 279 additions and 138 deletions

View file

@ -28,6 +28,7 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
</p>
<div class="flex w-full max-w-md flex-col-reverse justify-evenly gap-y-5 px-6 pt-5 sm:max-w-2xl sm:flex-row">
<button
style={{display: "none"}}
data-modal-reject
id="age-verification-reject"
class="rounded bg-stone-400 py-3 text-lg text-stone-900 hover:bg-stone-500 hover:text-stone-50 focus:bg-stone-500 focus:text-stone-50 sm:px-9 dark:bg-stone-300 dark:text-stone-900 dark:hover:bg-stone-600 dark:hover:text-stone-50 dark:focus:bg-stone-600 dark:focus:text-stone-50"
@ -35,6 +36,7 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
Cancel
</button>
<button
style={{display: "none"}}
data-modal-accept
id="age-verification-accept"
class="rounded bg-bm-500 py-3 text-lg text-stone-900 hover:bg-stone-500 hover:text-stone-50 focus:bg-stone-500 focus:text-stone-50 sm:px-9 dark:bg-bm-400 dark:text-stone-900 dark:hover:bg-stone-600 dark:hover:text-stone-50 dark:focus:bg-stone-600 dark:focus:text-stone-50"
@ -49,13 +51,18 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
<script>
const ENABLE_VIEW_TRANSITIONS = false;
type AgeVerified = "true" | undefined;
const ageRestrictedModalSetup = () => {
const modal = document.querySelector<HTMLElementTagNameMap["div"]>("body > div#modal-age-restricted");
const modal = document.querySelector<HTMLElementTagNameMap["div"]>("div#modal-age-restricted");
// Not an age-restricted page
if (!modal) {
throw new Error("Missing #modal-age-restricted element! Make sure that it's a direct child of body.");
return;
}
let ageVerified: "true" | undefined = localStorage.ageVerified;
if (modal !== document.querySelector("body > div#modal-age-restricted")) {
throw new Error("#modal-age-restricted must be a direct child of the body element!");
}
let ageVerified: AgeVerified = localStorage.ageVerified;
if (ageVerified !== "true") {
const rejectButton = modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-reject]")!;
const onRejectButtonClick = (e: MouseEvent) => {
@ -63,7 +70,9 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
location.href = "about:blank";
};
rejectButton.addEventListener("click", onRejectButtonClick);
modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-accept]")!.addEventListener(
rejectButton.style.removeProperty("display");
const acceptButton = modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-accept]")!;
acceptButton.addEventListener(
"click",
(e: MouseEvent) => {
e.preventDefault();
@ -72,10 +81,16 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
localStorage.ageVerified = ageVerified;
document.body.style.overflow = "auto";
document.querySelectorAll("body > :not(#modal-age-restricted)").forEach((el) => el.removeAttribute("inert"));
document.body.querySelectorAll<HTMLElementTagNameMap["a"]>("a[href][data-age-restricted]").forEach((el) => {
let newHref = new URL(el.href);
newHref.searchParams.set("ageVerified", "true");
el.href = newHref.href;
});
modal.style.display = "none";
},
{ once: true },
);
acceptButton.style.removeProperty("display");
rejectButton.focus();
}
};

View file

@ -1,4 +1,4 @@
---
---
<script is:inline>function a(){let b=document,c="#modal-age-restricted",d="true";localStorage.ageVerified!==d&&((b.body.style.overflow="hidden"),b.querySelectorAll("body > :not("+c+")").forEach(e=>e.setAttribute("inert",d)),(b.querySelector("body > "+c).style.display="block"));}document.addEventListener("astro:after-swap",a);a()</script>
<script is:inline>function a(){let b=document,c="#modal-age-restricted",d="true",e=b.querySelector("body > "+c),f="ageVerified",g="searchParams",h=localStorage;new URL(b.location)[g].get(f)===d&&(h[f]=d);e&&(h[f]===d?b.querySelectorAll("a[href][data-age-restricted]").forEach(x=>{let y=new URL(x.href);y[g].set(f,d);x.href=y.href}):((b.body.style.overflow="hidden"),b.querySelectorAll("body > :not("+c+")").forEach(x=>x.setAttribute("inert",d)),(e.style.display="block")))};document.addEventListener("astro:after-swap",a);a()</script>

View file

@ -24,7 +24,7 @@ const charactersPerUser = copyrightedCharacters ? await formatCopyrightedCharact
t(lang, "characters/characters_are_copyrighted_by", user, characters[0] === "" ? [] : characters)
}
>
<UserComponent class="p-name" lang={lang} user={user} />
<UserComponent lang={lang} user={user} />
</CopyrightedCharactersItem>
))}
</ul>

View file

@ -6,10 +6,10 @@ import DarkModeScriptInline from "./DarkModeScriptInline.astro";
<script>
const ENABLE_VIEW_TRANSITIONS = false;
type ColorScheme = "auto" | "dark" | "light";
type ColorScheme = "auto" | "dark" | "light" | undefined;
const colorSchemeSetup = () => {
let colorScheme: ColorScheme | undefined = localStorage.colorScheme;
let colorScheme: ColorScheme = localStorage.colorScheme;
if (!colorScheme || colorScheme === "auto") {
colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
@ -25,10 +25,10 @@ import DarkModeScriptInline from "./DarkModeScriptInline.astro";
localStorage.colorScheme = colorScheme;
};
document.querySelectorAll<HTMLElementTagNameMap["button"]>("button[data-dark-mode]").forEach((button) => {
button.addEventListener("click", toggleColorScheme);
button.classList.remove("hidden");
button.style.removeProperty("display");
button.setAttribute("aria-hidden", "false");
button.addEventListener("click", toggleColorScheme);
});
};
if (ENABLE_VIEW_TRANSITIONS) {
@ -36,4 +36,4 @@ import DarkModeScriptInline from "./DarkModeScriptInline.astro";
} else {
colorSchemeSetup();
}
</script>
</script>

View file

@ -84,6 +84,7 @@ const { link, instance, user, postId } = Astro.props;
<a
data-author
class="p-author h-card u-url text-link flex items-center text-lg hover:underline focus:underline"
rel="nofollow"
target="_blank"
>
<picture>
@ -95,6 +96,7 @@ const { link, instance, user, postId } = Astro.props;
<a
data-post-link
class="u-url text-link my-1 flex items-center text-sm font-light hover:underline focus:underline"
rel="nofollow"
target="_blank"
>
<time class="dt-published mr-1" data-published-date aria-label="Publish date"></time>

View file

@ -1,38 +0,0 @@
---
---
<nav>
<ul>
<li>
<a
class="hover:text-green-800 hover:underline focus:text-green-800 focus:underline dark:hover:text-bm-300 dark:focus:text-bm-300"
href="/">Home</a
>
</li>
<li>
<a
class="hover:text-green-800 hover:underline focus:text-green-800 focus:underline dark:hover:text-bm-300 dark:focus:text-bm-300"
href="/stories/1">Stories</a
>
</li>
<li>
<a
class="hover:text-green-800 hover:underline focus:text-green-800 focus:underline dark:hover:text-bm-300 dark:focus:text-bm-300"
href="/games">Games</a
>
</li>
<li>
<a
class="hover:text-green-800 hover:underline focus:text-green-800 focus:underline dark:hover:text-bm-300 dark:focus:text-bm-300"
href="/tags">Tags</a
>
</li>
<li>
<a
class="hover:text-green-800 hover:underline focus:text-green-800 focus:underline dark:hover:text-bm-300 dark:focus:text-bm-300"
href="/search">Search</a
>
</li>
</ul>
</nav>

View file

@ -1,5 +1,6 @@
---
import type { CollectionEntry } from "astro:content";
import clsx from "clsx";
import type { Lang } from "../i18n";
import { getUsernameForLang } from "../utils/get_username_for_lang";
@ -17,10 +18,10 @@ const link = user.data.preferredLink ? user.data.links[user.data.preferredLink]!
{
link ? (
<a rel={rel} href={link} class:list={[className, "h-card u-url text-link underline"]} target="_blank">
<a rel={clsx(rel, "nofollow")} href={link} class:list={[className, "h-card u-url p-name text-link underline"]} target="_blank">
{username}
</a>
) : (
<span class:list={[className, "h-card"]}>{username}</span>
<span class:list={[className, "h-card p-name"]}>{username}</span>
)
}

View file

@ -0,0 +1,13 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 448 512">
<path d="M96 0C43 0 0 43 0 96L0 416c0 53 43 96 96 96l288 0 32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-64c17.7 0 32-14.3 32-32l0-320c0-17.7-14.3-32-32-32L384 0 96 0zm0 384l256 0 0 64L96 448c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16zm16 48l192 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-192 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z" />
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M184 48l144 0c4.4 0 8 3.6 8 8l0 40L176 96l0-40c0-4.4 3.6-8 8-8zm-56 8l0 40L64 96C28.7 96 0 124.7 0 160l0 96 192 0 128 0 192 0 0-96c0-35.3-28.7-64-64-64l-64 0 0-40c0-30.9-25.1-56-56-56L184 0c-30.9 0-56 25.1-56 56zM512 288l-192 0 0 32c0 17.7-14.3 32-32 32l-64 0c-17.7 0-32-14.3-32-32l0-32L0 288 0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-128z"
></path>
</SVGIcon>

View file

@ -0,0 +1,13 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 640 512">
<path d="M192 64C86 64 0 150 0 256S86 448 192 448l256 0c106 0 192-86 192-192s-86-192-192-192L192 64zM496 168a40 40 0 1 1 0 80 40 40 0 1 1 0-80zM392 304a40 40 0 1 1 80 0 40 40 0 1 1 -80 0zM168 200c0-13.3 10.7-24 24-24s24 10.7 24 24l0 32 32 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-32 0 0 32c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-32-32 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l32 0 0-32z" />
</SVGIcon>

View file

@ -0,0 +1,13 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z" />
</SVGIcon>

View file

@ -0,0 +1,14 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path d="M345 39.1L472.8 168.4c52.4 53 52.4 138.2 0 191.2L360.8 472.9c-9.3 9.4-24.5 9.5-33.9 .2s-9.5-24.5-.2-33.9L438.6 325.9c33.9-34.3 33.9-89.4 0-123.7L310.9 72.9c-9.3-9.4-9.2-24.6 .2-33.9s24.6-9.2 33.9 .2zM0 229.5L0 80C0 53.5 21.5 32 48 32l149.5 0c17 0 33.3 6.7 45.3 18.7l168 168c25 25 25 65.5 0 90.5L277.3 442.7c-25 25-65.5 25-90.5 0l-168-168C6.7 262.7 0 246.5 0 229.5zM144 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
/>
</SVGIcon>