6c7e577633
Extends the lone security thread into three arcs that read as skill pillars - SECURITY (6), NETWORKING (5), PLATFORM (7) - so every post carries a numbered badge and the catalogue advertises three areas of depth rather than a loose list. Adds the badge to the post detail header too, styled to match the cards.
145 lines
3.9 KiB
Plaintext
145 lines
3.9 KiB
Plaintext
---
|
|
import Layout from "../../layouts/Layout.astro";
|
|
import { getPosts, formatDate } from "../../lib/blog";
|
|
import { readingTime } from "../../lib/reading";
|
|
import { series } from "../../data/series";
|
|
import { render } from "astro:content";
|
|
import type { GetStaticPaths } from "astro";
|
|
|
|
export const getStaticPaths = (async () => {
|
|
const posts = await getPosts();
|
|
return posts.map((entry) => ({ params: { slug: entry.id }, props: { entry } }));
|
|
}) satisfies GetStaticPaths;
|
|
|
|
const { entry } = Astro.props;
|
|
const { Content } = await render(entry);
|
|
---
|
|
|
|
<Layout title={entry.data.title} path={`/blog/${entry.id}/`} description={entry.data.summary}>
|
|
<article class="post">
|
|
<div class="container post__inner">
|
|
<a class="post__back mono" href="/blog/">← All posts</a>
|
|
|
|
<header class="post__head" data-reveal>
|
|
<div class="post__meta mono">
|
|
{series[entry.id] && (
|
|
<>
|
|
<span class="post__series">{series[entry.id].name} · {String(series[entry.id].number).padStart(2, "0")}</span>
|
|
<span class="post__sep">·</span>
|
|
</>
|
|
)}
|
|
<time datetime={entry.data.date.toISOString()}>{formatDate(entry.data.date)}</time>
|
|
<span class="post__sep">·</span>
|
|
<span>{readingTime(entry.body)}</span>
|
|
</div>
|
|
<h1 class="post__title">{entry.data.title}</h1>
|
|
<ul class="post__tags">
|
|
{entry.data.tags.map((t) => (
|
|
<li><a class="tag" href={`/blog/tags/${t}/`}>{t}</a></li>
|
|
))}
|
|
</ul>
|
|
</header>
|
|
|
|
{entry.data.hero && (
|
|
<figure class="post__hero" data-reveal>
|
|
<img
|
|
src={`${entry.data.hero}?v=2`}
|
|
alt={entry.data.heroAlt ?? entry.data.title}
|
|
width="1600"
|
|
height="1073"
|
|
loading="eager"
|
|
decoding="async"
|
|
/>
|
|
</figure>
|
|
)}
|
|
|
|
<div class="prose post__body" data-reveal>
|
|
<Content />
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</Layout>
|
|
|
|
<style>
|
|
.post__inner {
|
|
max-width: 48rem;
|
|
padding-block: var(--space-7);
|
|
}
|
|
.post__hero {
|
|
margin: var(--space-6) 0 var(--space-7);
|
|
border-radius: var(--radius);
|
|
overflow: hidden;
|
|
border: 1px solid var(--border);
|
|
box-shadow: var(--shadow), 0 0 30px var(--accent-glow);
|
|
background: var(--surface);
|
|
}
|
|
.post__hero img {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
aspect-ratio: 1600 / 1073;
|
|
}
|
|
.post__back {
|
|
display: inline-block;
|
|
font-size: var(--step--1);
|
|
color: var(--text-dim);
|
|
margin-bottom: var(--space-6);
|
|
}
|
|
.post__back:hover {
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
}
|
|
.post__meta {
|
|
font-size: 0.78rem;
|
|
color: var(--text-faint);
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
.post__series {
|
|
color: var(--accent-2);
|
|
border: 1px solid var(--border-magenta);
|
|
border-radius: 3px;
|
|
padding: 2px 7px;
|
|
letter-spacing: 0.1em;
|
|
font-size: 0.66rem;
|
|
}
|
|
.post__sep {
|
|
opacity: 0.6;
|
|
}
|
|
.post__title {
|
|
margin-top: var(--space-3);
|
|
font-size: var(--step-4);
|
|
max-width: 26ch;
|
|
}
|
|
.post__tags {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: var(--space-4) 0 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.45rem;
|
|
}
|
|
.post__tags a:hover {
|
|
border-color: var(--accent-line);
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
}
|
|
.post__body {
|
|
margin-top: var(--space-7);
|
|
}
|
|
/* Diagram images (Markdown ``) sit on a constant light card so the
|
|
fixed-colour SVGs stay legible in dark mode — mirrors the Diagram.astro component. */
|
|
.post__body :global(img[src^="/diagrams/"]) {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
margin: var(--space-6) auto;
|
|
padding: var(--space-5);
|
|
background: #f6f8fb;
|
|
border: 1px solid rgba(2, 6, 23, 0.1);
|
|
border-radius: var(--radius);
|
|
}
|
|
</style>
|