fix: sanitize post title for valid CSS view-transition-name (#650)

This commit is contained in:
せいはな
2026-06-02 23:05:23 +08:00
committed by GitHub
parent e062c79248
commit 470bfb528e
3 changed files with 30 additions and 3 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
---
import type { CollectionEntry } from "astro:content";
import { getPostUrl } from "@/utils/getPostPaths";
import { slugifyStr } from "@/utils/slugify";
import { toTransitionName } from "@/utils/toTransitionName";
import Datetime from "./Datetime.astro";
type Props = {
@@ -22,7 +22,7 @@ const { title, description, ...props } = data;
"focus-visible:no-underline focus-visible:underline-offset-0",
]}
>
<Heading transition:name={slugifyStr(title.replaceAll(".", "-"))}>
<Heading transition:name={toTransitionName(title)}>
{title}
</Heading>
</a>
+2 -1
View File
@@ -8,6 +8,7 @@ import Tag from "@/components/Tag.astro";
import { getPostSlug, getPostUrl } from "@/utils/getPostPaths";
import { getSortedPosts } from "@/utils/getSortedPosts";
import { slugifyStr } from "@/utils/slugify";
import { toTransitionName } from "@/utils/toTransitionName";
import EditPost from "./_components/EditPost.astro";
import ShareLinks from "./_components/ShareLinks.astro";
import BackButton from "./_components/BackButton.astro";
@@ -111,7 +112,7 @@ const ogImage = ogImageUrl
data-pagefind-body
>
<h1
style={{ viewTransitionName: slugifyStr(title.replaceAll(".", "-")) }}
style={{ viewTransitionName: toTransitionName(title) }}
class="text-accent inline-block text-2xl font-bold sm:text-3xl"
>
{title}
+26
View File
@@ -0,0 +1,26 @@
import { slugifyStr } from "./slugify";
/**
* Produce a valid CSS <custom-ident> for view-transition-name.
* CSS idents only allow [a-zA-Z0-9_-] plus Unicode U+00A0+.
* Non-ASCII chars are hex-encoded, ASCII special chars (:, /, etc.)
* are replaced with hyphens to keep the browser from ignoring the name.
*/
export const toTransitionName = (str: string): string => {
const base = slugifyStr(str.replaceAll(".", "-"));
let result = base
// encode non-ASCII chars (Chinese, Japanese, etc.)
.replace(
/[^\x00-\x7F]/gu,
c => "u" + c.codePointAt(0)!.toString(16).padStart(6, "0")
)
// replace any remaining invalid chars (colons, slashes, etc.)
.replace(/[^a-zA-Z0-9_-]/g, "-")
// collapse consecutive hyphens and trim
.replace(/-+/g, "-")
.replace(/^-+|-+$/g, "");
// CSS ident must not start with a digit
if (/^\d/.test(result)) result = "p-" + result;
if (!result) result = "post";
return result;
};