mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Phase 5: every page of the site, the resume PDF and RSS
Landing, About, Now, Resume (with a generated PDF), Work and case studies, Inquiries and the sent page, Library and the Shelf, Workshop and craft pages, Tabletop, Blog with topics and posts, RSS, the Store, product lines, the order form and the 404, all rendered from the content service's notes. Schema: experience previousRoles, skills stack core items, page stats, make tags and deck favorite. Icon names from notes map to brand icons in one place, and HTML comments are dropped when Markdown renders. 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:
@@ -0,0 +1,232 @@
|
||||
---
|
||||
/**
|
||||
* 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 Chip from "@gk/ui/components/Chip.astro";
|
||||
import GiltCorners from "@gk/ui/components/GiltCorners.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";
|
||||
|
||||
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" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<img 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" />
|
||||
</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">{links.map((l) => <Chip href={l.href}>{l.label}</Chip>)}</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(201, 162, 74, 0.55);
|
||||
border-radius: var(--gk-radius);
|
||||
box-shadow:
|
||||
inset 0 0 0 4px var(--gk-midnight),
|
||||
inset 0 0 0 5px rgba(201, 162, 74, 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(106, 47, 224, 0.28), transparent 70%),
|
||||
radial-gradient(ellipse 55% 80% at 0% 70%, rgba(20, 163, 154, 0.18), transparent 70%),
|
||||
repeating-linear-gradient(0deg, rgba(244, 242, 237, 0.035) 0 1px, transparent 1px 40px),
|
||||
repeating-linear-gradient(90deg, rgba(244, 242, 237, 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(201, 162, 74, 0.35));
|
||||
}
|
||||
.cover {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.shade {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(to top, rgba(14, 21, 34, 0.88), transparent 42%),
|
||||
linear-gradient(to bottom, rgba(14, 21, 34, 0.45), transparent 30%);
|
||||
}
|
||||
.mat {
|
||||
position: absolute;
|
||||
inset: 14px;
|
||||
border: 1px solid rgba(201, 162, 74, 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(201, 162, 74, 0.15) 70%, transparent);
|
||||
box-shadow: 0 0 8px rgba(201, 162, 74, 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>
|
||||
Reference in New Issue
Block a user