4e13bf596c
Astro scopes compound selectors, so [data-theme=light] .hero__scrim inside a component style block could never match html[data-theme] - the light theme rendered dark scrims (embossed low-contrast copy). Wrap the theme prefix in :global() in Hero, ProjectCard and the blog index. Light 'blueprint' hero now reads clean.
214 lines
5.8 KiB
Plaintext
214 lines
5.8 KiB
Plaintext
---
|
|
import Layout from "../../layouts/Layout.astro";
|
|
import Section from "../../components/Section.astro";
|
|
import PostList from "../../components/PostList.astro";
|
|
import { getPosts, allTags, formatDate } from "../../lib/blog";
|
|
import { readingTime } from "../../lib/reading";
|
|
import { series } from "../../data/series";
|
|
import { site } from "../../data/site";
|
|
|
|
const posts = await getPosts();
|
|
const tags = allTags(posts);
|
|
|
|
// Featured = the security-series opener when present, else the newest post.
|
|
const featured = posts.find((p) => p.id === "secret-zero") ?? posts[0];
|
|
const rest = posts.filter((p) => p.id !== featured.id);
|
|
const featuredSeries = series[featured.id];
|
|
---
|
|
|
|
<Layout title="Blog" path="/blog" description="Notes on platform engineering, edge Kubernetes, GPUs and homelab infrastructure.">
|
|
<Section id="blog" eyebrow={`~/${site.handle}/blog`} index="*" title="Notes from the build.">
|
|
<p class="lead blog__intro" data-reveal>
|
|
Lessons from edge Kubernetes, GPUs, and running infrastructure like it matters.
|
|
</p>
|
|
|
|
{tags.length > 0 && (
|
|
<nav class="blog__tags" aria-label="Filter by tag" data-reveal>
|
|
<span class="mono blog__tags-label">tags:</span>
|
|
{tags.map((t) => (
|
|
<a class="tag blog__tag" href={`/blog/tags/${t}/`}>{t}</a>
|
|
))}
|
|
</nav>
|
|
)}
|
|
|
|
{featured && (
|
|
<a class="featured" href={`/blog/${featured.id}/`} data-reveal>
|
|
<div class="featured__body">
|
|
<div class="featured__meta mono">
|
|
{featuredSeries && (
|
|
<span class="featured__series">
|
|
{featuredSeries.name} · {String(featuredSeries.number).padStart(2, "0")}
|
|
</span>
|
|
)}
|
|
<time datetime={featured.data.date.toISOString()}>{formatDate(featured.data.date)}</time>
|
|
<span class="featured__sep">·</span>
|
|
<span>{readingTime(featured.body)}</span>
|
|
</div>
|
|
<h3 class="featured__title">{featured.data.title}</h3>
|
|
<p class="featured__summary">{featured.data.summary}</p>
|
|
<ul class="featured__tags" role="list">
|
|
{featured.data.tags.map((t) => (
|
|
<li class="tag">{t}</li>
|
|
))}
|
|
</ul>
|
|
<span class="featured__read mono">Read the post →</span>
|
|
</div>
|
|
{featured.data.hero && (
|
|
<div class="featured__art" aria-hidden="true">
|
|
<img
|
|
src={`${featured.data.hero}?v=2`}
|
|
alt=""
|
|
width="1600"
|
|
height="1073"
|
|
loading="eager"
|
|
decoding="async"
|
|
/>
|
|
</div>
|
|
)}
|
|
</a>
|
|
)}
|
|
|
|
<PostList posts={rest} />
|
|
</Section>
|
|
</Layout>
|
|
|
|
<style>
|
|
.blog__intro {
|
|
max-width: 50ch;
|
|
margin-bottom: var(--space-5);
|
|
}
|
|
.blog__tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
margin-bottom: var(--space-6);
|
|
}
|
|
.blog__tags-label {
|
|
font-size: var(--step--1);
|
|
color: var(--text-faint);
|
|
margin-right: 0.25rem;
|
|
}
|
|
.blog__tag:hover {
|
|
border-color: var(--accent-line);
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* featured card — text left, hero art right, scrimmed into the panel */
|
|
.featured {
|
|
position: relative;
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
|
|
background: var(--panel);
|
|
border: 1px solid var(--border-accent);
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
color: inherit;
|
|
text-decoration: none;
|
|
backdrop-filter: var(--panel-blur);
|
|
-webkit-backdrop-filter: var(--panel-blur);
|
|
margin-bottom: var(--space-6);
|
|
transition:
|
|
border-color 180ms var(--ease),
|
|
box-shadow 180ms var(--ease);
|
|
}
|
|
.featured:hover {
|
|
text-decoration: none;
|
|
border-color: var(--border-accent-hover);
|
|
box-shadow: var(--glow-card);
|
|
}
|
|
.featured__body {
|
|
padding: var(--space-6);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-3);
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
.featured__meta {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
font-size: 0.72rem;
|
|
color: var(--text-faint);
|
|
}
|
|
.featured__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;
|
|
}
|
|
.featured__sep {
|
|
opacity: 0.6;
|
|
}
|
|
.featured__title {
|
|
margin: 0;
|
|
font-family: var(--font-display);
|
|
font-size: var(--step-2);
|
|
font-weight: 600;
|
|
color: var(--text-strong);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
.featured__summary {
|
|
margin: 0;
|
|
color: var(--text-dim);
|
|
font-size: var(--step-0);
|
|
line-height: 1.65;
|
|
text-wrap: pretty;
|
|
}
|
|
.featured__tags {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: var(--space-2) 0 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem;
|
|
}
|
|
.featured__read {
|
|
margin-top: auto;
|
|
padding-top: var(--space-3);
|
|
font-size: 0.8rem;
|
|
color: var(--accent);
|
|
}
|
|
.featured__art {
|
|
position: relative;
|
|
min-height: 280px;
|
|
}
|
|
.featured__art img {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
/* scrim the art into the panel from the text side */
|
|
.featured__art::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 1;
|
|
background: linear-gradient(90deg, var(--panel-solid) 0%, transparent 45%);
|
|
}
|
|
:global([data-theme="light"]) .featured__art::before {
|
|
background: linear-gradient(90deg, var(--panel-solid) 0%, transparent 45%);
|
|
opacity: 0.85;
|
|
}
|
|
|
|
@media (max-width: 860px) {
|
|
.featured {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.featured__art {
|
|
order: -1;
|
|
min-height: 200px;
|
|
}
|
|
.featured__art::before {
|
|
background: linear-gradient(180deg, transparent 55%, var(--panel-solid) 100%);
|
|
}
|
|
}
|
|
</style>
|