Give the GK monogram a light mode version

In light mode the header, footer and post placeholders show the monogram
on a pale disc with deep gold rings and ink letters, with a soft edge so
it sits apart from the Starlight page. The light file is written by the
light motifs script.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
Claude
2026-10-18 20:30:32 +00:00
parent 98c1d81031
commit 08df07f221
7 changed files with 43 additions and 9 deletions
+14 -1
View File
@@ -1,7 +1,8 @@
/**
* Writes the light and foil versions of the motif SVGs from the dark originals
* in src/assets/motifs, using the inks Greg picked on the light motif board
* (2026-10-18). Run after a motif changes: node packages/ui/scripts/light-motifs.mjs
* (2026-10-18), and the light GK monogram in src/assets/brand/light. Run after
* a motif or the monogram changes: node packages/ui/scripts/light-motifs.mjs
*
* light/ deep gold line: the dark inks swapped for Starlight-page inks, glow removed
* foil/ the dark seal with its gold as foil, glow kept
@@ -63,3 +64,15 @@ for (const f of files) {
}
}
console.log(`${files.length} motifs written`);
// The GK monogram on Starlight: a pale disc, deep gold rings and ink letters.
const MONOGRAM = {
...LIGHT,
"#131C2B": "#FFFFFF",
"#1F3B49": "#ECE6D8",
"#15232F": "#FBF9F4",
};
const brand = join(dir, "../brand");
mkdirSync(join(brand, "light"), { recursive: true });
writeFileSync(join(brand, "light", "gk-monogram.svg"), recolor(readFileSync(join(brand, "gk-monogram.svg"), "utf8"), MONOGRAM));
console.log("monogram written");
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

+8 -1
View File
@@ -2,12 +2,13 @@
/** The site footer: the header divider again, then the monogram line and links. */
import monogram from "../assets/brand/gk-monogram.svg?url";
import Divider from "./Divider.astro";
import Motif from "./Motif.astro";
const year = new Date().getFullYear();
---
<footer class="gk-site-footer">
<div class="gk-wrap"><Divider seed="footer" /></div>
<div class="gk-wrap row">
<span class="mark"><img src={monogram} alt="" width="40" height="40" />© {year} Greg Kilmer · Web Development · a Patiun LLC company</span>
<span class="mark"><Motif src={monogram} alt="" width="40" height="40" />© {year} Greg Kilmer · Web Development · a Patiun LLC company</span>
<span class="links">
<a href="/inquiries" class="gk-text strong">Start an inquiry</a>
<a href="/about" class="gk-text gk-quiet">About</a>
@@ -37,6 +38,12 @@ const year = new Date().getFullYear();
align-items: center;
gap: 12px;
}
.mark :global(img) {
border-radius: 50%;
}
:global(:root[data-theme="light"]) .mark :global(img) {
box-shadow: 0 1px 3px rgba(19, 28, 43, 0.14);
}
.links {
display: flex;
flex-wrap: wrap;
+6 -2
View File
@@ -38,7 +38,7 @@ const decs = [60, 30, 0, -30].map((d, i) => ({ at: (i / 4) * 100, label: `${d >
store ? (
<nav aria-label="Store" class="nav">
<a href="/store" class="brand" aria-current={store.current === "/store" ? "page" : undefined}>
<img src={monogram} alt="" width="72" height="72" class="mono" />
<Motif src={monogram} alt="" width="72" height="72" class="mono" loading="eager" />
<span class="words">
<span class="name store-name">Store.GregKilmer.com</span>
<span class="store-tag">a Patiun LLC workshop</span>
@@ -54,7 +54,7 @@ const decs = [60, 30, 0, -30].map((d, i) => ({ at: (i / 4) * 100, label: `${d >
) : (
<nav aria-label="Primary" class="nav">
<a href="/" class="brand" aria-current={aria("home")}>
<img src={monogram} alt="" width="72" height="72" class="mono" />
<Motif src={monogram} alt="" width="72" height="72" class="mono" loading="eager" />
<span class="words">
<span class="name">GREG KILMER</span>
<span class="tag">DEVELOPER<Star size={8} tone="teal" />MAKER<Star size={8} tone="teal" />STORYTELLER</span>
@@ -160,6 +160,10 @@ const decs = [60, 30, 0, -30].map((d, i) => ({ at: (i / 4) * 100, label: `${d >
border-radius: 50%;
transition: filter 0.25s ease;
}
/* On Starlight the pale disc needs a soft edge to sit apart from the page. */
:global(:root[data-theme="light"]) .mono {
box-shadow: 0 1px 4px rgba(19, 28, 43, 0.14);
}
.brand:hover .mono,
.brand:focus-visible .mono {
filter: drop-shadow(0 0 6px rgba(var(--gk-gold-bright-rgb), 0.7)) drop-shadow(0 0 16px rgba(var(--gk-teal-rgb), 0.45));
+9 -3
View File
@@ -46,10 +46,16 @@ export type SealName = keyof typeof seals;
export { filamentBanner };
// Light mode and foil versions, written by scripts/light-motifs.mjs.
// Light mode and foil versions (and the light monogram), written by scripts/light-motifs.mjs.
const byFile = (m: Record<string, string>) => Object.fromEntries(Object.entries(m).map(([p, u]) => [p.slice(p.lastIndexOf("/") + 1), u]));
const darkFiles = byFile(import.meta.glob<string>("../assets/motifs/*.svg", { query: "?url", import: "default", eager: true }));
const lightFiles = byFile(import.meta.glob<string>("../assets/motifs/light/*.svg", { query: "?url", import: "default", eager: true }));
const darkFiles = byFile({
...import.meta.glob<string>("../assets/motifs/*.svg", { query: "?url", import: "default", eager: true }),
...import.meta.glob<string>("../assets/brand/gk-monogram.svg", { query: "?url", import: "default", eager: true }),
});
const lightFiles = byFile({
...import.meta.glob<string>("../assets/motifs/light/*.svg", { query: "?url", import: "default", eager: true }),
...import.meta.glob<string>("../assets/brand/light/*.svg", { query: "?url", import: "default", eager: true }),
});
const foilFiles = byFile(import.meta.glob<string>("../assets/motifs/foil/*.svg", { query: "?url", import: "default", eager: true }));
const foilLightFiles = byFile(import.meta.glob<string>("../assets/motifs/foil-light/*.svg", { query: "?url", import: "default", eager: true }));
const fileOf = Object.fromEntries(Object.entries(darkFiles).map(([f, u]) => [u, f]));
@@ -4,6 +4,7 @@
* monogram, which also stands in when there is no cover or it fails to load.
*/
import monogram from "@gk/ui/assets/brand/gk-monogram.svg?url";
import Motif from "@gk/ui/components/Motif.astro";
interface Props {
src?: string;
@@ -16,7 +17,7 @@ const { src, alt = "", width = 176, height = 120, class: className } = Astro.pro
const mark = Math.round(Math.min(width, height) / 2);
---
<span class:list={["thumb", className]} style={`--w:${width}px;--h:${height}px`}>
<img src={monogram} alt="" width={mark} height={mark} class="mono" aria-hidden="true" />
<Motif src={monogram} alt="" width={mark} height={mark} class="mono" aria-hidden="true" />
{src && <img src={src} alt={alt} loading="lazy" decoding="async" class="photo" onerror="this.remove()" />}
</span>
@@ -7,6 +7,7 @@
import monogram from "@gk/ui/assets/brand/gk-monogram.svg?url";
import Chip from "@gk/ui/components/Chip.astro";
import Icon from "@gk/ui/components/Icon.astro";
import Motif from "@gk/ui/components/Motif.astro";
import Star from "@gk/ui/components/Star.astro";
import type { StatusView } from "../../lib/home.ts";
import { formatDate, type SiteIndex } from "../../lib/site.ts";
@@ -25,7 +26,7 @@ const postHref = post ? s.href(post) : null;
<img src={v.cover} alt="" class="thumb photo" loading="lazy" decoding="async" width="56" height="56" />
) : (
<span class="thumb tile" aria-hidden="true">
<img src={monogram} alt="" width="28" height="28" />
<Motif src={monogram} alt="" width="28" height="28" />
</span>
)
}