diff --git a/app/components/Layout.tsx b/app/components/Layout.tsx index 45b04b8..615ddbd 100644 --- a/app/components/Layout.tsx +++ b/app/components/Layout.tsx @@ -1,6 +1,7 @@ import { Link, useLocation } from "@remix-run/react"; import { SITE } from "~/utils/site-config"; import { ThemePicker } from "./ThemePicker"; +import { SparkleText } from "./SparkleText"; export function TopNav({ current }: { current?: string }) { const links = [ @@ -44,7 +45,9 @@ export function SiteHeader() { {SITE.name}

)} -

{SITE.bio}

+

+ +

); diff --git a/app/components/SparkleText.tsx b/app/components/SparkleText.tsx new file mode 100644 index 0000000..45ef994 --- /dev/null +++ b/app/components/SparkleText.tsx @@ -0,0 +1,25 @@ +import { Fragment } from "react"; + +// Any of these characters, typed inline in a body string, will render +// wrapped in a .sparkle span (theme-accent color). Surrounding text is +// left as-is. +const SPARKLES = ["✦", "✧", "✻", "✽", "✶", "✴", "✳"] as const; +const SPARKLE_RE = new RegExp(`([${SPARKLES.join("")}])`, "g"); + +export function SparkleText({ text }: { text: string }) { + if (!SPARKLES.some((s) => text.includes(s))) return <>{text}; + const parts = text.split(SPARKLE_RE); + return ( + <> + {parts.map((part, i) => + SPARKLES.includes(part as (typeof SPARKLES)[number]) ? ( + + ) : ( + {part} + ), + )} + + ); +} diff --git a/app/routes/books.tsx b/app/routes/books.tsx index 60f72e1..9b410bf 100644 --- a/app/routes/books.tsx +++ b/app/routes/books.tsx @@ -1,5 +1,6 @@ import type { MetaFunction } from "@vercel/remix"; import { BOOKS, SITE, SITE_URL } from "~/utils/site-config"; +import { SparkleText } from "~/components/SparkleText"; export const meta: MetaFunction = () => [ { title: `Books · ${SITE.name}` }, @@ -39,7 +40,11 @@ export default function Books() { {", "} {b.author} {b.rating && } - {b.note && {b.note}} + {b.note && ( + + + + )} ))} diff --git a/app/routes/interests.tsx b/app/routes/interests.tsx index c01c862..2d0e9fb 100644 --- a/app/routes/interests.tsx +++ b/app/routes/interests.tsx @@ -1,5 +1,6 @@ import type { MetaFunction } from "@vercel/remix"; import { INTERESTS, SITE, SITE_URL } from "~/utils/site-config"; +import { SparkleText } from "~/components/SparkleText"; export const meta: MetaFunction = () => [ { title: `Interests · ${SITE.name}` }, @@ -21,7 +22,7 @@ export default function Interests() { {INTERESTS.map((it) => (

{it.title}

-

{it.body}

+

))} diff --git a/app/routes/now.tsx b/app/routes/now.tsx index 9573284..ae361a0 100644 --- a/app/routes/now.tsx +++ b/app/routes/now.tsx @@ -1,6 +1,7 @@ import type { MetaFunction } from "@vercel/remix"; import { Link } from "@remix-run/react"; import { NOW, SITE, SITE_URL } from "~/utils/site-config"; +import { SparkleText } from "~/components/SparkleText"; export const meta: MetaFunction = () => [ { title: `Now · ${SITE.name}` }, @@ -29,7 +30,7 @@ export default function Now() {

{s.heading}

- {s.body} + {s.link && ( <> {" "} diff --git a/app/routes/then.tsx b/app/routes/then.tsx index 0090e73..4705221 100644 --- a/app/routes/then.tsx +++ b/app/routes/then.tsx @@ -1,6 +1,7 @@ import type { MetaFunction } from "@vercel/remix"; import { Link } from "@remix-run/react"; import { THEN, SITE, SITE_URL } from "~/utils/site-config"; +import { SparkleText } from "~/components/SparkleText"; export const meta: MetaFunction = () => [ { title: `Then · ${SITE.name}` }, @@ -41,7 +42,7 @@ export default function Then() {

{s.heading}

- {s.body} + {s.link && ( <> {" "} diff --git a/app/styles/global.css b/app/styles/global.css index 8d15c10..6e7e9a4 100644 --- a/app/styles/global.css +++ b/app/styles/global.css @@ -18,6 +18,7 @@ --rule: #1c3a1c; --link: #b0ff7d; --link-visited: #70c040; + --sparkle: #d8ff55; --grain: none; } @@ -29,6 +30,7 @@ --rule: #d8cfbc; --link: #0b4aa2; --link-visited: #6c3e9e; + --sparkle: #c9751c; /* Chunkier, more visible grain: larger tile (400), lower baseFrequency (bigger noise blobs), 3 octaves, higher alpha. Reads as real paper. */ --grain: url("data:image/svg+xml;utf8,"); @@ -42,6 +44,7 @@ --rule: #2a2520; --link: #8cc0ff; --link-visited: #c8a4d4; + --sparkle: #e8c67b; --grain: none; } @@ -53,6 +56,7 @@ --rule: #3a2a18; --link: #ffc870; --link-visited: #c88030; + --sparkle: #ffe6a0; --grain: none; } @@ -64,6 +68,7 @@ --rule: #334862; --link: #9ed4ff; --link-visited: #d4a8f0; + --sparkle: #ffd48a; --grain: none; } @@ -75,6 +80,7 @@ --rule: #3a2a45; --link: #e8b4f0; --link-visited: #b890c2; + --sparkle: #ffd48a; --grain: none; } @@ -315,6 +321,17 @@ footer { padding: 0 1.25rem; } +/* Sparkle accent: a theme-colored glyph for emphasis. + Write ✦ in any content string wrapped by and it + gets this color automatically. */ +.sparkle { + color: var(--sparkle); + font-size: 0.9em; + display: inline-block; + transform: translateY(-0.05em); + padding: 0 0.1em; +} + /* Theme picker (row of text buttons in the footer) */ .theme-picker { margin: 0.5rem 0 0;