32 KiB
Personal Blog Rewrite Plan
Goal
Replace the existing portfolio with a statically generated personal blog that still helps with hiring, but earns that trust through writing, project essays, and technical evidence instead of a conventional portfolio timeline.
The site should feel current, minimal, and carefully made. It should not imitate old plain-HTML blogs, but it should learn from why many respected technical blogs work: durable URLs, direct navigation, readable typography, fast pages, RSS, and content that carries the authority.
This plan incorporates the rendered review of 100 CS/technical blogs and a 10-lens review pass covering content, visual design, mobile ergonomics, accessibility, performance, information architecture, implementation risk, hiring signal, typography, and content migration.
Inputs
Existing Repo
The current repo is a custom TypeScript/Webpack single-page portfolio. The strongest reusable material is in:
src/data/projects/*.ts: project titles, date strings, summaries, longer paragraphs, media references, external linkssrc/data/media: screenshots, posters, videos, CV, BSc/MSc thesis PDFsstatic/no-change: favicons,robots.txt, 404 page, Open Graph image
The current implementation should be replaced rather than evolved. It depends on JavaScript for core content and is organized around timeline cards, expanded details, image viewers, theme controls, and decorative UI. The new site should ship little to no JavaScript and render normal pages as static HTML.
Rendered Blog Research
I installed Playwright and rendered 100 technical/personal CS blogs at desktop and phone widths. The artifacts are in:
/tmp/codex-blog-design-pass/index.html/tmp/codex-blog-design-pass/summary.json/tmp/codex-blog-design-pass/summary.csv
Useful metrics from the pass:
- Median desktop paragraph width: about
662px - Median mobile paragraph width: about
350px - Median paragraph font size:
16px - 73 of 100 exposed RSS or Atom
- Most strong personal blogs used very few card-like UI elements
- Several famous blogs had mobile overflow, which this site should explicitly avoid
- The best visual references used media as explanation, not decoration
Representative references:
- Writing-first archive: Simon Willison, Marc Brooker, Brandur, Armin Ronacher, Phil Eaton
- Minimal but current personal sites: Tom MacWright, Thorsten Ball, matklad, BurntSushi
- Technical visual writing: Red Blob Games, Distill, Bret Victor, Jay Alammar, fasterthanli.me
- Strong code-heavy writing: Ned Batchelder, Dave Cheney, Eli Bendersky, Laurence Tratt
Product Direction
The site should be a blog first and a hiring surface second.
The homepage should not say "hire me" first. It should establish a serious technical writer and builder through the quality of the archive. Recruiters should still quickly find the About page, CV, GitHub, LinkedIn, contact details, and the most relevant project essays.
The tone should be:
- Text-first
- Calm
- Precise
- Modern
- Functional
- Slightly editorial
- Never flashy
- Never intentionally dated
Avoid:
- Purple or blue-purple gradients
- Glowing blobs
- Timeline cards
- Hero illustrations
- Glassmorphism
- Decorative animations
- Dense nav chrome
- Flat boxes pretending to be a "retro" blog
- Landing-page copy
- Skill grids, logo walls, metric blocks, or resume-card layouts
Key Decisions
- Canonical essays live under
/writing/[slug]/, including project essays. /projects/is a complete text index of built work with stable anchors; it is not a second set of duplicate project detail pages.- Tags are Phase 1 metadata but tag pages can wait.
- The visual system uses a refined sans-serif voice, not Times New Roman and not browser defaults.
- Normal pages ship no client JavaScript. Any exception must be post-specific and justified.
- Images that need optimization live in
src/assetsand render through Astro image tooling.public/is for stable raw files such as PDFs, favicons, legacy assets, and videos/downloads. - The deployment plan must match the existing Forgejo workflow that builds
dist/and rsyncs it to/pages/schmelczer-dev.
Design Principles
Function Includes Reading
Minimal design is not an excuse for weak typography. The site should feel better on a phone than most personal blogs.
Core reading requirements:
- No horizontal scrolling at
320px,390px, or430px - Prose width around
36rem-42remon desktop - Mobile content width:
min(100% - 2 * var(--gutter), var(--measure)) - Mobile gutter:
clamp(16px, 5vw, 24px) - Body font size:
17pxon phones,17px-18pxon larger screens - Prose line height:
1.62-1.68 - Paragraph spacing tuned for long reading, not marketing skimming
- Code blocks scroll horizontally only inside the code block
- Figures, tables, long links, and code never force page overflow
- Header nav, footer links, RSS/contact links, project links, footnotes, backlinks, and pagination use a
44pxminimum hit area or equivalent padding
Typography
Use a refined sans-serif as the main voice. Do not use Times New Roman. Do not make the site look like default browser HTML.
Recommended direction:
- Body and UI:
Source Sans 3first, thenInter, then the system sans stack - Code:
IBM Plex Mono,JetBrains Mono, or a careful system monospace stack - No custom display font
- No negative letter-spacing
- Use font weights sparingly: regular, medium, semibold
Default recommendation:
font-family:
'Source Sans 3',
Inter,
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
Font loading policy:
- Start with the system stack or one self-hosted variable WOFF2 for
Source Sans 3 - Use only regular-to-semibold weights
- Use
font-display: swap - Preload only the single above-fold face if measurement proves it helps
- Avoid third-party font requests
Editorial type targets:
- Body:
17px, line-height1.65 - Article
h1: about2remon phones, restrained on desktop, line-height1.15-1.2 h2: line-height1.2-1.25, with generous space above- Metadata and captions:
14px-15px, line-height1.35-1.45 - Code: about
.88em, line-height1.55
Color
Use a quiet near-white background and near-black text. Let links and small metadata carry the color.
Candidate palette:
- Background:
#fbfaf7 - Text:
#181817 - Muted text:
#6b6860 - Rule/border:
#d9d5ca - Strong rule/focus:
#7f7668 - Link:
#285f74 - Link hover:
#8a4b2f - Code background:
#efede6 - Callout background:
#f4f1e8
The palette should feel warm and literary without becoming beige-heavy. The teal link is the only real accent. Purple is out.
Low-contrast tokens such as --rule, --code-bg, and --callout-bg must not be the only focus, active, or error indicator.
Link Treatment
Prose links should look like links.
.prose a {
color: var(--link);
text-decoration-line: underline;
text-decoration-thickness: 0.08em;
text-underline-offset: 0.18em;
}
Hover can shift color. Keyboard focus must use a visible 2px+ outline with offset. Do not remove outlines.
Layout
Use a simple centered layout with a narrow content column and a slightly wider media column.
Desktop:
- Page max width around
72rem - Main article column around
40rem - Figures can break out to
56rem-64remwhen useful - Header aligned with content, not full-width heavy navigation
- Footer minimal
Mobile:
- Single column
- Header wraps naturally; no menu JavaScript
- Nav uses
flex-wrap: wrap, small gaps, and no pill labels that can force overflow - Site title and nav may become two short rows
- Article title should not dominate the whole first viewport
- First paragraph should appear without excessive scrolling
- Metadata should be visible but quiet
UI Shape
Use rules, whitespace, indentation, and typographic hierarchy instead of cards.
Default primitives:
- Thin horizontal rules
- Muted metadata
- Hanging dates on desktop where appropriate
- Indented summaries
- Captions
- Narrow dividers
- Text rows for archive/project entries
Acceptable framed elements:
- Code blocks
- Tables
- Figures with captions
- Callouts for short notes
- A small "At a glance" metadata block inside project essays when it is content, not decoration
Avoid using cards for every post preview. Archive entries should mostly be text rows with date, title, tags, and one sentence.
Figures, Tables, and Code
Code blocks:
overflow-x: automax-width: 100%-webkit-overflow-scrolling: touch- No wrapping by default
- Mobile code font around
13px-14px padding: 1remborder: 1px solid var(--rule)border-radius: 6px- No box shadow
Tables:
- Small tables stay full width
- Wide tables live inside an overflow container
- Use tabular numerals where useful
- Use subtle row rules
- Avoid zebra striping unless the data genuinely needs it
Figures:
margin-block: 2rem- Captions in muted text at
14px-15px img,video,canvas, andsvgusemax-width: 100%; height: auto- Optional
border: 1px solid var(--rule)for screenshots - Wide figures only above tablet widths
- Avoid
width: 100vw; usemax-inline-size: min(100%, var(--measure-wide))
Media
Media should be evidence.
Good uses:
- A screenshot showing the UI state being discussed
- A short video demonstrating an engine, simulation, or interaction
- A diagram that explains architecture or data flow
- A small inline image that makes a project concrete
Bad uses:
- Decorative thumbnails for every post
- Hero images
- Cropped atmospheric images
- Large videos above the fold
- Autoplay
For project posts, use one strong media item near the top only when it immediately clarifies the subject. Prefer "evidence figure" or "lead figure" language, not "hero."
Videos:
- Never load videos in archive/listing pages
- Use posters
- Set explicit dimensions
- Use
preload="metadata"orpreload="none" - Provide captions, transcript, or a text summary when the video carries meaning
- Compress large MP4/WebM files before publishing
- Set a per-page media budget before launch
Information Architecture
Routes
/
/writing/
/writing/[slug]/
/projects/
/about/
/rss.xml
/sitemap-index.xml
Optional later:
/notes/
/tags/[tag]/
/now/
Project essays are canonical writing posts. The project index links to those posts and to demos/source/packages/papers. Do not create duplicate /projects/[slug]/ pages in the first version; use stable anchors such as /projects/#ad-astra for smaller items if needed.
Navigation
Persistent header:
WritingProjectsAboutRSS
Footer:
- CV
- GitHub
- RSS
Keep the header compact and text-only. Put dense hiring links in the About page and footer rather than in the primary nav.
Homepage
Purpose: establish an editorial surface first, then make the best built work easy to find.
Structure:
- Compact intro
- Latest writing
- Selected project essays
- Selected technical work
- Footer with RSS/contact links
Example intro direction:
I'm Andras Schmelczer. I write about building software systems, AI deployment, graphics, simulations, and tools.
No large hero. No headshot on the homepage unless it is tiny and secondary. Avoid badges, thumbnails, big feature blocks, and ranking language. "Selected" should be a quiet editorial choice, not a portfolio showcase.
Writing Index
Chronological archive with tasteful grouping.
Rules:
- Group by year
- Newest first
- Show all posts
- Tags are inline metadata
- Tag pages can wait, but tags must exist in frontmatter from Phase 1
- Use "selected" markers very sparingly, if at all
Each entry:
- Date
- Title
- One-sentence summary
- Tags
Avoid card grids. A reader should be able to scan the archive quickly on a phone.
Project Index
This is not the old portfolio timeline. It is an index of things built.
Groups:
- Selected projects
- Older and smaller projects
Each row:
- Name
- Problem, constraint, or essay angle
- Year or period
- Quiet technology metadata
- Primary link to essay when one exists
- Secondary links: demo, source, package, paper, video
Use technologies as evidence, not as the lead. Every legacy project should be accounted for either as a row or as part of a merged row, even when it does not deserve a full essay.
About
This is the hiring-focused page.
Sections:
- Short bio
- Quick facts: target role/domain, location/remote preference, availability if relevant, email, CV, GitHub, LinkedIn
- Best starting points: 3-5 strongest essays
- What I work on
- Selected technical work
- Technical strengths
- Experience/education summary
This page can be more direct:
- MSc in Computer Science
- Professional software engineering experience
- AI/ML systems, large-scale architecture, graphics, visualization, embedded projects
- Preference for complex, multidisciplinary systems
Keep it typographic and compact. No skill grids, logo walls, metric blocks, or resume cards.
Content Plan
Editorial Standard
The first version should not read like project cards converted to Markdown. The project material should become technical essays, some of which happen to come from past projects.
Every substantial post should answer:
- Does this teach something to a technical reader who is not evaluating me?
- Does the title make a specific promise?
- Is there a thesis, reader takeaway, and evidence?
- Is there at least one concrete constraint, number, diagram, code excerpt, or failure?
- Is the project age clearly framed?
- Are technologies evidence rather than the point?
- Would the post still be worth reading if my name and CV links disappeared?
Initial Essays
Use essay angles rather than project labels:
Designing an ML deployment API around best practicesfrom GreatAITile-based optimization for 2D SDF ray tracingfrom SDF-2DShared simulation code in a mobile multiplayer browser gamefrom decla.redA 50 FPS game engine on an ATtiny85from ad_astraSyncing state with immutable triesfrom life-towersGraph models for a real-time cooling simulationfrom the nuclear simulator and graph editor
Best recruiter starting set:
- GreatAI
- SDF-2D
- life-towers or nuclear-simulation
Secondary project rows or shorter notes:
- City simulation in Unity
- Forex prediction experiment
- My Notes Android app
- Platform game in C/SDL
- LED music visualizer
- Photo site generator
- Avoid
- Photo colour grader
Add 2-3 non-project technical notes shortly after launch so the site does not feel like a portfolio in blog clothing. Good candidates:
- Lessons from deploying ML systems
- Notes on mobile graphics performance
- What makes old side projects still worth writing about
- A debugging or architecture retrospective from recent work
Date Framing
Separate project dates from publication dates.
date: publication date for RSS and archive orderingupdated: material revision dateprojectPeriod: human text such asAutumn-Winter 2020sortDate: approximate ISO date for project ordering
Old projects should be framed as current reflection on past work, not as current products.
Post Template
Each project essay should follow a consistent loose structure:
---
title:
description:
date:
updated:
projectPeriod:
tags:
project:
role:
stack:
scale:
outcome:
links:
- label:
type:
url:
media:
- type:
src:
poster:
mp4:
webm:
alt:
caption:
transcript:
role: evidence
---
Intro: the thesis, why this is worth reading, and what the reader will learn.
## At a glance
Role, timeframe, stack, scale, outcome, links.
## The Problem
## Constraints
## Design
## What Worked
## What I Would Change
## Links
This should not become rigid. Some posts need a story; others need an architecture walkthrough.
Writing Style
Prefer:
- Specific constraints
- Numbers
- Concrete tradeoffs
- Architecture sketches
- What shipped
- What improved
- Who used it, if known
- What was measured
- What I personally owned
- What failed
- What changed your mind
- Short code excerpts when useful
Avoid:
- "I was passionate about..."
- Generic technology lists
- Resume phrasing
- Overexplaining old projects as if they are current products
- Hiding rough edges
Rough edges are useful if framed as engineering judgment.
Migration Plan
Migration Manifest
Create a migration manifest before rewriting content. It should map executable TS project records into static content.
Fields:
{
sourceProjectId: string;
sourceFile: string;
slug: string;
target: "essay" | "project-row" | "merged-project-row";
canonicalPath: string;
legacyAnchor?: string;
title: string;
essayTitle?: string;
publishedDate?: string;
projectPeriod: string;
sortDate: string;
bodySources: Array<"description" | "more">;
links: Array<{ label: string; type: "source" | "demo" | "package" | "paper" | "video" | "site"; url: string; download?: boolean }>;
media: Array<{ type: "image" | "video" | "preview"; src?: string; poster?: string; mp4?: string; webm?: string; externalUrl?: string; alt: string; caption: string }>;
downloads: Array<{ label: string; url: string }>;
}
Initial mapping:
| Source | Target | Notes |
|---|---|---|
great-ai.ts |
Essay | Use great-ai.png, MSc thesis PDF, PyPI/site links |
sdf2d.ts |
Essay | Use sdf2d.png, NPM, YouTube, demo; cross-link BSc thesis |
declared.ts |
Essay | Use decla-red.png, GitHub, demo, BSc thesis |
ad-astra.ts |
Essay | Use poster and MP4/WebM; GitHub link |
towers.ts |
Essay or selected project row | Use towers.png, GitHub, demo; focus on trie sync |
nuclear.ts + nuclear-editor.ts |
One essay or selected project row | Merge simulator/editor into one story with two screenshots |
city-simulation.ts |
Project row or short note | Use video only inside detail writing if kept |
forex.ts |
Project row or short note | Frame carefully as experiment, not financial claim |
my-notes.ts |
Older project row | Android/Markdown note app |
platform-game.ts |
Older project row | Early C/SDL learning story |
leds.ts |
Older project row | Raspberry Pi, FFT, LEDs |
photos.ts |
Older project row | Static photo site generator |
avoid.ts |
Older project row | First web game, external demo |
colors.ts |
Older project row | Color-grader proof of concept |
Migration copyedit rules:
- Decode HTML entities such as
—and& - Replace generic icon/helper labels with semantic labels
- Preserve external URLs
- Replace generic video alt text with project-specific alt/captions
- Keep BSc/MSc PDFs with explicit labels
- Cross-link shared thesis context between SDF-2D and decla.red
Link Labels
Normalize links to reader-facing labels:
SourceDemoPackagePaperThesisVideoProject site
Do not use helper-derived labels such as "Open in new tab" in content.
Technical Plan
Framework
Use Astro.
Reasons:
- Static generation by default
- Markdown content collections
- Little-to-no client JavaScript
- Good TypeScript fit for this repo
- Straightforward RSS/sitemap support
- Easy to preserve a content-first architecture
Do not use Typst as the primary web writing format for now. Typst's HTML export is still not the right foundation for this production blog. Use Markdown for posts. Typst can be revisited later for PDFs or long-form downloadable documents.
JavaScript Policy
Default: ship no client JavaScript.
Allowed later only if it materially improves a specific post:
- A small interactive diagram
- A progressive enhancement for a specific demo
- A tiny script for theme preference if dark mode is added
Hard rules:
- No global app shell
- No client-side routing
- No required JS for reading
- No
client:*islands in normal pages - No global view transitions
- No global prefetching
- No analytics script unless explicitly accepted as a measured exception
QA gates:
- Run Playwright with JavaScript disabled and verify reading, nav, RSS discovery, media access, archive scanning, and project links
- Fail CI if normal pages emit unexpected
_astro/*.jsor<script type="module"> - Grep built HTML for unexpected scripts
Proposed File Structure
astro.config.mjs
src/
assets/
projects/
great-ai/
sdf-2d/
declared/
ad-astra/
content/
config.ts
posts/
greatai-ai-deployment-api.md
sdf-2d-ray-tracing.md
declared-shared-simulation-code.md
ad-astra-attiny85-game-engine.md
life-towers-immutable-tries.md
nuclear-cooling-simulation.md
projects/
greatai.md
sdf-2d.md
declared.md
ad-astra.md
towers.md
nuclear-simulation.md
layouts/
Base.astro
Post.astro
Page.astro
components/
Header.astro
Footer.astro
ArticleList.astro
Figure.astro
ProjectLinks.astro
AtAGlance.astro
pages/
index.astro
writing/
index.astro
[slug].astro
projects/
index.astro
about.astro
rss.xml.ts
styles/
global.css
public/
favicon.ico
robots.txt
og-image.jpg
media/
downloads/
video/
legacy/
Use public/ only for stable raw files and downloads. Optimized post images should live in src/assets or beside content and render through Astro image tooling.
Content Collections
Define typed schemas for posts and projects in src/content/config.ts.
Controlled tags:
['ai', 'systems', 'graphics', 'simulation', 'embedded', 'web', 'tools', 'games'];
Post frontmatter:
{
title: string;
description: string;
date: Date;
updated?: Date;
draft?: boolean;
tags: string[];
selected?: boolean;
featuredOrder?: number;
project?: string;
projectPeriod?: string;
role?: string;
stack?: string[];
scale?: string;
outcome?: string;
audience?: "general" | "technical" | "recruiter-relevant";
links?: Array<{ label: string; type: string; url: string }>;
media?: Array<{
type: "image" | "video" | "diagram";
src?: string;
poster?: string;
mp4?: string;
webm?: string;
alt?: string;
decorative?: boolean;
caption?: string;
transcript?: string;
role?: "evidence" | "og" | "inline";
}>;
}
Project frontmatter:
{
sourceProjectId: string;
title: string;
description: string;
period: string;
sortDate: Date;
status?: string;
technologies: string[];
selected?: boolean;
essay?: string;
legacyAnchor?: string;
links: Array<{ label: string; type: string; url: string; download?: boolean }>;
media?: Array<{ type: string; src?: string; poster?: string; alt: string; caption: string }>;
downloads?: Array<{ label: string; url: string }>;
}
Schema rules:
- Meaningful media must have
altandcaption decorative: trueis allowed only with emptyalt- Videos with meaningful content should include a transcript or text summary
- Draft posts never appear in archives, RSS, sitemap, or selected lists
CSS Architecture
Use one global CSS file with carefully named custom properties. No Tailwind. No CSS framework.
CSS tokens:
:root {
color-scheme: light;
--font-sans: 'Source Sans 3', Inter, ui-sans-serif, system-ui, sans-serif;
--font-mono: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, monospace;
--bg: #fbfaf7;
--text: #181817;
--muted: #6b6860;
--link: #285f74;
--link-hover: #8a4b2f;
--rule: #d9d5ca;
--rule-strong: #7f7668;
--code-bg: #efede6;
--callout-bg: #f4f1e8;
--measure: 40rem;
--measure-wide: 62rem;
--gutter: clamp(16px, 5vw, 24px);
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-12: 3rem;
--text-caption: 0.875rem;
--text-body: 1.0625rem;
--text-title-mobile: 2rem;
--leading-prose: 1.65;
--leading-heading: 1.18;
}
Mobile CSS invariants:
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
overflow-x: hidden;
}
body {
margin: 0;
font-size: var(--text-body);
line-height: var(--leading-prose);
}
img,
video,
canvas,
svg {
max-width: 100%;
height: auto;
}
pre,
table {
max-width: 100%;
overflow-x: auto;
}
.prose {
max-inline-size: var(--measure);
padding-inline: var(--gutter);
margin-inline: auto;
}
.site-header,
.project-links,
.post-tags {
min-width: 0;
flex-wrap: wrap;
}
Use modern CSS, but conservatively:
clamp()for spacing and gutters, not viewport-scaled prose:focus-visibletext-wrap: balanceonly on short headings if supportedoverflow-wrap: anywherefor dangerous long links@media (prefers-reduced-motion)even though there should be little motion
Accessibility
Baseline requirements:
html lang="en"- Semantic landmarks:
header,nav,main,footer - One
h1per page - Ordered heading hierarchy
articlefor poststime datetimefor dates- Real list markup for archives/projects
aria-currentin nav- Skip link visible on focus
- Persistent
2px+:focus-visibleoutline with offset - Underlined links in prose
- Sufficient contrast
- Proper image alt text
figureandfigcaptionfor explanatory media- Captions or transcripts for meaningful video
controlson video- No autoplay, parallax, or animated scroll by default
prefers-reduced-motiondisables transitions and scroll behavior- No content hidden behind hover
- No JS-required navigation
- No horizontal page overflow at common phone widths
- Descriptive link text
QA should include keyboard-only testing, 200% zoom, forced-colors/high-contrast mode, mobile viewport checks, and a screen-reader landmark/heading smoke test.
Performance
Targets:
- No client JS on normal pages
- CSS under 20KB uncompressed if practical
- Optimized responsive images through Astro image tooling
- Lazy-load non-critical images
- Do not autoplay videos
- Keep homepage light
- Avoid third-party fonts
- Prefer local assets over embeds
Media handling:
- Put optimized post images in
src/assets - Keep PDFs, favicons, legacy stable files, and raw videos/downloads in
public/ - Prefer still images over videos in listing pages
- Use videos only inside article bodies
- Compress the existing MP4/WebM files before publishing if they remain large
- Add page weight reports for homepage, article, about, and projects pages
- Keep old PDFs as first-class downloads with labels such as
MSc thesisandBSc thesis
SEO and Feeds
Include:
- Page titles
- Meta descriptions
- Canonical URLs
- Open Graph metadata
<link rel="alternate" type="application/rss+xml">- Visible footer RSS link
- RSS feed at
/rss.xml - Sitemap via
@astrojs/sitemapdefault output, expected as/sitemap-index.xmlunless a custom/sitemap.xmlendpoint is added robots.txthumans.txtoptional
RSS rules:
- Newest first
- Exclude drafts
- Use absolute URLs
- Use stable GUIDs
- Include
updateddates when present - Include useful summaries at minimum
- If full-content RSS is used, normalize relative links and images
Deployment
The repo currently deploys through .forgejo/workflows/deploy.yml, not a generic GitHub Pages action. The workflow runs npm ci, npm run lint, npm run build, then rsyncs dist/ to /pages/schmelczer-dev.
Deployment requirements:
astro.config.mjssetssite: "https://schmelczer.dev"- No SSR adapter
- Build output remains
dist/ - Decide
trailingSlashexplicitly - Keep
baseempty for the root domain - Ensure Node version in
.nvmrcis compatible with Astro and the deployed workflow - Update lint/format scripts for
.astro,.md, config files, and TypeScript - Remove obsolete Webpack loaders and scripts during implementation
- Keep the lockfile committed
Recommended scripts:
{
"dev": "astro dev",
"lint": "astro check && prettier --check .",
"format": "prettier --write .",
"build": "astro check && astro build",
"preview": "astro preview"
}
Legacy URLs
Add a legacy URL and asset map before cutover.
Cover:
- Old hash anchors generated from project titles
/avoid- Existing root favicons
robots.txtog-image.jpg- Old PDF locations under
static/ - Any old project/demo URLs that should remain external rather than canonical
Hash URLs cannot be server-redirected reliably, so preserve old IDs on the project index where practical.
Implementation Phases
Phase 1: Scaffold
- Replace Webpack setup with Astro
- Add
astro.config.mjs - Add
src/content/config.ts - Add content collections
- Add global CSS tokens and mobile invariants before templates harden
- Add homepage, writing index, post page, projects index, about page
- Add RSS and sitemap integration
- Add initial no-JS checks
- Set up asset folders: optimized images in
src/assets, raw downloads/video inpublic
Phase 2: Migration Manifest
- Inventory every
src/data/projects/*.tsfile - Extract title, date string, description,
more, media, PDFs, and links - Normalize links into semantic labels
- Decode HTML entities
- Decide essay vs project row vs merged project row
- Preserve fuzzy project dates separately from publication dates
- Add legacy anchors
Phase 3: Content Migration
- Convert flagship project data into Markdown essays
- Create project collection rows for all legacy projects
- Preserve external links and downloads
- Add captions, alt text, transcripts or summaries where needed
- Choose one or two media items per flagship post
- Write the About page from scratch
- Add at least 2-3 non-project technical notes soon after the first project essay batch
Phase 4: Design Pass
- Tune typography on mobile first
- Test
320px,390px,430px, tablet, and desktop - Check first viewport of article pages
- Check code block behavior
- Check long URLs and long headings
- Check captions and figure sizing
- Remove any layout that feels card-heavy, retro-default, or portfolio-template-like
Phase 5: QA
- Run build
- Run accessibility checks with axe or pa11y if available
- Run Playwright screenshots for homepage, article, about, and projects
- Run Playwright assertion:
document.documentElement.scrollWidth <= window.innerWidthat320,390, and430 - Run Playwright with JavaScript disabled
- Verify no unintended JS bundles ship on static pages
- Inspect
dist/ - Verify static HTML routes exist
- Validate canonical/RSS/sitemap URLs
- Validate internal links
- Check no drafts appear in RSS or sitemap
- Check image alt coverage
- Check 404 behavior
- Report page weight for homepage, representative article, about, and projects
Phase 6: Recruiter Flow QA
- Can a recruiter find email and CV within 10 seconds on mobile?
- Can they identify the top three strongest technical pieces within two clicks?
- Does each flagship post establish role, depth, and outcome near the top?
- Does the About page communicate competence without feeling like a resume template?
- Does the homepage still feel like a blog rather than a hiring funnel?
Final Design Decision
Build a modern, quiet, sans-serif publishing site.
It should borrow the seriousness and durability of old technical blogs without copying their dated defaults. It should borrow the polish of modern personal sites without becoming a portfolio template. The experience should be: open on a phone, start reading immediately, trust the author more with every paragraph.