Files
com.gregkilmer.www/services/site/src/components/blog/LeadPost.astro
T
Claude 7aa2910b12 Add light mode that follows the device setting
Dark stays the default. A device set to light gets the picks from the
light motif board: the Dawn chart sky (dawn wash over faint star chart
lines, atlas marks in the hero), deep gold line seals, Midnight, violet and
gold dust, the deep gold to teal filament and the morning sky placeholder.
Large standalone seals (404, Inquiries, Inquiry sent, the blog's lead) are
gold foil in both modes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
2026-10-18 18:54:56 +00:00

241 lines
6.6 KiB
Plaintext

---
/**
* The Blog's top slot: the featured or latest post in the one featured
* panel. The cover fills the left side when the post has one; otherwise the
* blog's seal art stands in.
*/
import Button from "@gk/ui/components/Button.astro";
import GiltCorners from "@gk/ui/components/GiltCorners.astro";
import Motif from "@gk/ui/components/Motif.astro";
import Seal from "@gk/ui/components/Seal.astro";
import Star from "@gk/ui/components/Star.astro";
import StarPath from "@gk/ui/components/StarPath.astro";
import StatusPill from "@gk/ui/components/StatusPill.astro";
import TickRing from "@gk/ui/components/TickRing.astro";
import TickScale from "@gk/ui/components/TickScale.astro";
import { filamentBanner } from "@gk/ui/components/seals.ts";
import { dateLine, nowStatuses, pageLinks, type Post } from "../../lib/blog.ts";
import { mediaSrc, type SiteIndex } from "../../lib/site.ts";
import PageChips from "./PageChips.astro";
interface Props {
post: Post;
label: "Featured" | "Latest";
site: SiteIndex;
}
const { post, label, site: s } = Astro.props;
const href = s.href(post) ?? "#";
const cover = mediaSrc(post.data.cover);
const links = pageLinks(s, post);
const now = nowStatuses(s, post);
---
<article class="lead" aria-labelledby="lead-title">
<div class:list={["art", { photo: cover }]}>
{
cover ? (
<>
<img src={cover} alt={post.title} class="cover" onerror="this.remove()" />
<span class="shade" aria-hidden="true" />
<span class="mat" aria-hidden="true" />
</>
) : (
<>
<Motif src={filamentBanner} alt="" aria-hidden="true" class="banner" />
<StarPath strength="wild" steps={7} centerStar={false} class="path" seed={post.slug} />
<TickRing size={196} class="ring">
<Seal name="transmutation" size={150} class="seal" foil />
</TickRing>
</>
)
}
<span class="no" aria-hidden="true">
<TickScale width={150} height={10} count={30} />
<span>No. 01 · {label}</span>
</span>
</div>
<div class="text">
<span class="meta">
<Star size={12} />
<span class="kicker">{label} · {post.data.topic}</span>
<span class="date">{dateLine(post)}</span>
{now.map((st) => <StatusPill>Now · {st.data.kind}</StatusPill>)}
</span>
<h2 id="lead-title" class="title"><a href={href}>{post.title}</a></h2>
<span class="underline" aria-hidden="true"></span>
{post.data.summary && <p class="summary">{post.data.summary}</p>}
{links.length > 0 && <span class="chips"><PageChips links={links} max={4} /></span>}
<Button href={href} arrow class="go">{post.data.postType === "build log" ? "Read the build log" : "Read the post"}</Button>
</div>
<GiltCorners />
</article>
<style>
.lead {
position: relative;
display: flex;
flex-wrap: wrap;
overflow: hidden;
background: var(--gk-midnight);
border: 1px solid rgba(var(--gk-gold-rgb), 0.55);
border-radius: var(--gk-radius);
box-shadow:
inset 0 0 0 4px var(--gk-midnight),
inset 0 0 0 5px rgba(var(--gk-gold-rgb), 0.35);
}
.art {
flex: 1 1 440px;
min-height: 320px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background:
radial-gradient(ellipse 70% 90% at 100% 0%, rgba(var(--gk-violet-rgb), calc(0.28 * var(--gk-wash))), transparent 70%),
radial-gradient(ellipse 55% 80% at 0% 70%, rgba(var(--gk-teal-deep-rgb), calc(0.18 * var(--gk-wash))), transparent 70%),
repeating-linear-gradient(0deg, rgba(var(--gk-star-rgb), 0.035) 0 1px, transparent 1px 40px),
repeating-linear-gradient(90deg, rgba(var(--gk-star-rgb), 0.035) 0 1px, transparent 1px 40px),
var(--gk-sky);
}
.art.photo {
min-height: 340px;
background: var(--gk-sky);
}
.banner {
position: absolute;
width: 150%;
max-width: none;
opacity: 0.32;
pointer-events: none;
}
.art :global(.path) {
position: absolute;
left: -10%;
width: 120%;
top: 50%;
transform: translateY(-50%) rotate(-16deg);
opacity: 0.8;
}
.art :global(.seal) {
filter: drop-shadow(0 0 16px rgba(var(--gk-gold-rgb), 0.35));
}
/* Glow is for the night sky; on Starlight the foil stands on its own. */
@media (prefers-color-scheme: light) {
.art :global(.seal) {
opacity: 1;
filter: none;
}
}
.cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.shade {
position: absolute;
inset: 0;
background:
linear-gradient(to top, rgba(var(--gk-sky-rgb), 0.88), transparent 42%),
linear-gradient(to bottom, rgba(var(--gk-sky-rgb), 0.45), transparent 30%);
}
.mat {
position: absolute;
inset: 14px;
border: 1px solid rgba(var(--gk-gold-rgb), 0.5);
border-radius: 6px;
}
.no {
position: absolute;
left: 24px;
bottom: 20px;
display: flex;
flex-direction: column;
gap: 6px;
font-family: var(--gk-font-display);
font-size: 12px;
letter-spacing: 0.2em;
color: var(--gk-gold-bright);
}
.text {
flex: 1 1 420px;
min-width: 0;
padding: 36px;
display: flex;
flex-direction: column;
gap: 12px;
justify-content: center;
}
.meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px 10px;
}
.kicker {
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 600;
color: var(--gk-gold-bright);
text-transform: uppercase;
}
.date {
font-size: 12px;
color: var(--gk-muted);
}
.title {
margin: 0;
font-size: 32px;
line-height: 1.2;
letter-spacing: 0.03em;
}
.title a {
color: var(--gk-text);
text-decoration: none;
}
.title a:hover {
color: var(--gk-gold-bright);
}
.underline {
display: block;
width: 180px;
height: 2px;
border-radius: 2px;
background:
radial-gradient(circle at 18% 50%, var(--gk-teal-bright) 0 2px, transparent 3px),
linear-gradient(90deg, var(--gk-gold), rgba(var(--gk-gold-rgb), 0.15) 70%, transparent);
box-shadow: 0 0 8px rgba(var(--gk-gold-rgb), 0.35);
}
.summary {
margin: 4px 0 0;
font-size: 15px;
color: var(--gk-soft);
line-height: 1.6;
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.text :global(.go) {
align-self: flex-start;
margin-top: 6px;
}
@media (max-width: 640px) {
.art {
flex-basis: 100%;
min-height: 240px;
}
.art.photo {
min-height: 240px;
}
.text {
padding: 26px 22px 30px;
}
.title {
font-size: 26px;
}
}
</style>