From b2519ebb88deab0ac8bc9bf88e6e304f466657e6 Mon Sep 17 00:00:00 2001 From: korentomas Date: Fri, 24 Apr 2026 14:06:36 -0300 Subject: [PATCH 1/2] =?UTF-8?q?Add=20theme-colored=20sparkle=20accent=20(?= =?UTF-8?q?=E2=9C=A6)=20for=20emphasis=20in=20prose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New --sparkle CSS variable per theme. Terminal gets yellow-lime (#d8ff55), paper gets burnt sienna, ink/plum get light gold, amber goes brighter, blueprint picks warm tan. - .sparkle class applies color, trims size, slight vertical nudge. - SparkleText component auto-wraps ✦ in any body string so content can stay in site-config.ts as plain strings. - Applied to /interests, /now, /then, and book notes. Type ✦ anywhere in a body string (interests.body, NOW/THEN section.body, BOOKS note) and it renders in the theme's accent. --- app/components/SparkleText.tsx | 22 ++++++++++++++++++++++ app/routes/books.tsx | 7 ++++++- app/routes/interests.tsx | 3 ++- app/routes/now.tsx | 3 ++- app/routes/then.tsx | 3 ++- app/styles/global.css | 17 +++++++++++++++++ 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 app/components/SparkleText.tsx diff --git a/app/components/SparkleText.tsx b/app/components/SparkleText.tsx new file mode 100644 index 0000000..e16a6ef --- /dev/null +++ b/app/components/SparkleText.tsx @@ -0,0 +1,22 @@ +import { Fragment } from "react"; + +const SPARKLE = "✦"; + +export function SparkleText({ text }: { text: string }) { + if (!text.includes(SPARKLE)) return <>{text}; + const parts = text.split(SPARKLE); + return ( + <> + {parts.map((part, i) => ( + + {i > 0 && ( + + )} + {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; From 3d0e051fdb7d28788069f1dc205d812946ab7b60 Mon Sep 17 00:00:00 2001 From: korentomas Date: Fri, 24 Apr 2026 14:13:43 -0300 Subject: [PATCH 2/2] Extend SparkleText to seven sparkle characters; apply to bio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SparkleText now matches any of: ✦ ✧ ✻ ✽ ✶ ✴ ✳ (four-pointed + asterisk/starburst varieties). Only the character itself is wrapped in the .sparkle span; surrounding text is unchanged. - SiteHeader's tagline now renders SITE.bio through SparkleText so the always-visible bio line can carry an accent. --- app/components/Layout.tsx | 5 ++++- app/components/SparkleText.tsx | 29 ++++++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) 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 index e16a6ef..45ef994 100644 --- a/app/components/SparkleText.tsx +++ b/app/components/SparkleText.tsx @@ -1,22 +1,25 @@ import { Fragment } from "react"; -const SPARKLE = "✦"; +// 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 (!text.includes(SPARKLE)) return <>{text}; - const parts = text.split(SPARKLE); + if (!SPARKLES.some((s) => text.includes(s))) return <>{text}; + const parts = text.split(SPARKLE_RE); return ( <> - {parts.map((part, i) => ( - - {i > 0 && ( - - )} - {part} - - ))} + {parts.map((part, i) => + SPARKLES.includes(part as (typeof SPARKLES)[number]) ? ( + + ) : ( + {part} + ), + )} ); }