Better CSS

This commit is contained in:
Andras Schmelczer 2026-05-27 19:41:04 +01:00
parent 31648541a2
commit 7d0f895074
11 changed files with 564 additions and 34 deletions

View file

@ -41,6 +41,8 @@ const isDecorativeLink = Boolean(href) && decorative;
href={href}
tabindex={isDecorativeLink ? -1 : undefined}
aria-label={isDecorativeLink ? (ariaLabel ?? alt) : undefined}
data-uncropped-preview
data-preview-label={ariaLabel ?? alt}
>
<Picture
src={src}
@ -49,6 +51,7 @@ const isDecorativeLink = Boolean(href) && decorative;
fallbackFormat="jpg"
widths={widths}
sizes={sizes}
quality="high"
loading={loading}
decoding="async"
fetchpriority={fetchpriority}

View file

@ -63,6 +63,7 @@ const videoHeight = item.type === 'video' ? (item.poster?.height ?? 720) : undef
formats={['avif', 'webp']}
widths={[480, 720, 960, 1280, 1600, 1920]}
sizes="(max-width: 700px) calc(100vw - 2 * clamp(20px, 4vw, 32px)), (max-width: 1100px) min(calc(100vw - 4rem), 56rem), 56rem"
quality="high"
loading="lazy"
decoding="async"
/>

View file

@ -57,6 +57,8 @@ for (const root of document.querySelectorAll('.post-thumbnail--iframe')) {
<div
class:list={['post-thumbnail', iframeSrc && 'post-thumbnail--iframe']}
style={iframeSrc ? `--post-thumbnail-aspect: ${aspectRatio}` : undefined}
data-uncropped-preview
data-preview-label={post.data.title}
>
<Picture
src={post.data.thumbnail.src}
@ -65,6 +67,7 @@ for (const root of document.querySelectorAll('.post-thumbnail--iframe')) {
fallbackFormat="jpg"
widths={[640, 960, 1280, 1600, 1920]}
sizes="(max-width: 700px) calc(100vw - 3rem), (max-width: 1100px) calc(100vw - 4rem), 56rem"
quality="high"
loading="eager"
fetchpriority="high"
decoding="async"

View file

@ -52,6 +52,12 @@ const hasCode = !!post.body && /(^|[^`])`[^`\n]+`|```/m.test(post.body);
const h2Headings = headings.filter((h) => h.depth === 2);
const showToc = h2Headings.length >= 3;
// Don't repeat the banner image at the end — PostThumbnail already rendered it.
const thumbnailSrc = post.data.thumbnail.src.src;
const trailingMedia = post.data.media.filter(
(item) => item.type === 'video' || item.src.src !== thumbnailSrc,
);
const personId = absoluteUrl('/about/#person');
const blogPosting = {
@ -152,7 +158,7 @@ const personJsonLd = buildPersonJsonLd();
<Content />
</div>
<PostMedia items={post.data.media} />
<PostMedia items={trailingMedia} />
{
related.length > 0 && (

View file

@ -156,12 +156,12 @@ export function buildPersonJsonLd(extra?: Record<string, unknown>) {
// Responsive image config shared by entry listings. Centralized here so a
// change to one breakpoint set is a single edit, not two component changes.
export const ARTICLE_THUMBNAIL = {
widths: [120, 180, 240, 320, 480],
widths: [160, 240, 320, 480, 640],
sizes: '(max-width: 700px) clamp(64px, 22vw, 80px), (max-width: 960px) 7rem, 8rem',
};
export const PROJECT_THUMBNAIL = {
widths: [240, 320, 480, 640, 800],
widths: [320, 480, 640, 800, 960, 1200, 1280],
sizes:
'(max-width: 700px) calc(100vw - 40px), (max-width: 960px) calc((100vw - 64px - 1rem) / 2), calc((min(100vw - 64px, 72rem) - 2rem) / 3)',
};

View file

@ -35,8 +35,9 @@ const startingPointsAnnotated = startingPoints.map((post) => ({
}));
const startingPointThumbnail = {
widths: [120, 180, 240, 320],
sizes: '(max-width: 700px) 4rem, (max-width: 960px) 28vw, 10rem',
widths: [160, 240, 320, 480, 640, 800],
sizes:
'(max-width: 700px) 4rem, (max-width: 960px) calc((100vw - 64px - 1.5rem) / 3), calc((min(100vw - 64px, 72rem) - 3rem) / 5)',
};
const personImage = await optimizeOgImage(defaultOg);

View file

@ -24,16 +24,15 @@ const personJsonLd = buildPersonJsonLd();
<Base jsonLd={personJsonLd}>
<section class="home-intro">
<p class="eyebrow">A notebook, written after the fact</p>
<p class="eyebrow">Engineering notes</p>
<h1>
<span class="home-intro-name">Andras Schmelczer</span> writes about projects, the tradeoffs
behind them, and what hindsight changed.
<span class="home-intro-name">Andras Schmelczer</span> — software engineer. Writeups
of finished projects, with the tradeoffs left in.
</h1>
<p>
Most of these started because I couldn't yet do the thing. An 8-bit ALU, a mobile
GPU, a single static HTML file, a cross-language ABI, three editors I didn't
control. The <a href="/about/">About page</a> is where I describe what I keep reaching
for; the posts below are the evidence.
Most started because I couldn't yet do the thing: an 8-bit ALU, a mobile GPU, a
single static HTML file, a cross-language ABI. The <a href="/about/">About page</a>
covers the patterns I keep returning to.
</p>
</section>

View file

@ -660,10 +660,11 @@
}
.entry-thumbnail img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 300ms ease;
object-position: center center;
}
a.entry-thumbnail {
@ -675,11 +676,6 @@
border-color: var(--color-rule-strong);
}
.article-list > li:hover .entry-thumbnail img,
.article-list > li:focus-within .entry-thumbnail img {
transform: scale(1.02);
}
.article-list > li:focus-within .entry-thumbnail {
border-color: var(--color-rule-strong);
}
@ -807,15 +803,6 @@
aspect-ratio: 4 / 3;
}
.project-card .project-thumbnail img {
transition: transform 300ms ease;
}
.project-card:hover .project-thumbnail img,
.project-card:focus-within .project-thumbnail img {
transform: scale(1.02);
}
.project-card__summary {
grid-area: summary;
display: flex;
@ -997,11 +984,6 @@
line-height: var(--leading-snug);
}
.starting-points > li:hover .entry-thumbnail img,
.starting-points > li:focus-within .entry-thumbnail img {
transform: scale(1.02);
}
.starting-points > li:focus-within .entry-thumbnail {
border-color: var(--color-rule-strong);
}
@ -1048,6 +1030,7 @@
.post-thumbnail--iframe img {
object-fit: cover;
object-position: center center;
border: 0;
border-radius: 0;
}