mirror of
https://github.com/andwati/andwati.com.git
synced 2026-10-27 13:22:15 +00:00
Nav bar & layout consistency - Base.astro's `wide` layout now defaults on for every page (was opt-in), fixing the nav bar wrapping differently depending on page width - Icon buttons for search and theme toggle (magnifying glass, sun/moon), replacing the plain "Search" text link and the "◐" glyph - Search is now a Cmd/Ctrl+K modal over Pagefind (lazy-loaded on first open) instead of a separate /search/ page, matching the old site - Fixed the article page's TOC sitting with unwanted left padding relative to the header (grid was center-justified instead of start-justified) Smooth navigation - Added Astro's ClientRouter for animated page transitions - Every page-local script that only ran once at module load (three.js hero, minimap, comments, book-spine carousel, scroll-to-top, reading progress, theme toggle, Pagefind init) now re-initializes via astro:page-load, with AbortController-based cleanup so window/document listeners don't accumulate across client-side navigations Restored from the legacy Zola site - Reading progress bar (article pages) and scroll-to-top button - Share buttons (X, Bluesky, LinkedIn, Reddit, HN, email) on writings - Prev/next post navigation with the old label/title layout - Utterances comments, wired to andwati/andwati.com with dark/light theme sync - YouTube embeds: any plain <iframe src="...youtube-nocookie.com..."> is now auto-wrapped in a responsive 16:9 container, gets loading="lazy", and emits VideoObject JSON-LD — no shortcode needed - LaTeX: $inline$ and $$block$$ math renders to static HTML via KaTeX at build time (no client-side JS shipped for it) - Tags page redesigned as a centered, alphabetical tag cloud Removed the related-posts section per request. Accessibility & contrast - --accent (#d1502f) measured 4.0:1 on the light background, just under WCAG AA's 4.5:1 for normal text — darkened to #a84025 (5.7:1), plus matching light-mode-only fixes for the two hljs syntax colors and the tip/warning callout colors that had the same problem in light mode - Fixed heading-order violations (h1 straight to h3 with nothing at h2) across the homepage, writings/portfolio/bookshelf/blogs lists, and tag pages — introduced a shared .entry-title class for the h2 list titles - Fixed inconsistent title-link underline colors: a plain <a> carries its own default underline (in its own text color) instead of matching its parent heading's accent-colored one — h1/h2/h3 anchors now explicitly inherit the heading's decoration - Bookshelf spine colors are generated per-title from a hash; the lightness-based text-color heuristic could still produce sub-4.5:1 contrast for some hues (e.g. green) — now computes real WCAG contrast and adjusts Performance - The three.js homepage hero was the single largest main-thread cost site-wide (dragging that page's Lighthouse performance score to the low 80s while every other page scored 95+): three.js is now dynamically imported only once the browser is idle (WebGL-support check split into its own tiny module so unsupported browsers never fetch it at all), the render loop is capped to ~30fps, and hover raycasting only runs on an actual pointer move instead of every frame SEO & structured data - Self-referential canonical links site-wide (with override support for posts first published elsewhere) - Sitewide WebSite/Person JSON-LD, plus per-article BlogPosting, BreadcrumbList, and (when applicable) VideoObject JSON-LD - robots.txt explicitly welcomes AI/LLM crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot, etc.) and points at llms.txt - Added an Atom feed (RSS and JSON Feed already existed) - Sitemap now sets <lastmod> per entry from its own date/updated frontmatter instead of leaving every URL at build time Analytics & comments infra - Self-hosted Umami wiring via PUBLIC_UMAMI_SCRIPT_URL/WEBSITE_ID (apps/site/.env.example), no-ops until both are set - sws.toml's CSP updated to allow utteranc.es and analytics.andwati.com — without this both were silently blocked in production Lighthouse CI - @lhci/cli wired up against the built site (mobile + desktop configs), `pnpm run lighthouse`, and a GitHub Actions workflow to run it on every push/PR — reporting only for now, no failing thresholds
119 lines
3.8 KiB
Markdown
119 lines
3.8 KiB
Markdown
# content/
|
|
|
|
This directory is the canonical source of truth for all site content. It is
|
|
shared between `apps/site` (Astro reads it directly at build time) and
|
|
`apps/cms` (Strapi's custom provider reads/writes these same files — its
|
|
SQLite database is only a disposable cache/index, never the source of
|
|
record). The site must always be buildable straight from this directory with
|
|
Strapi entirely absent.
|
|
|
|
Frontmatter is TOML (`+++ ... +++`), matching the legacy Zola posts under
|
|
`content/posts/` to ease migration. Each content type below is one file (or a
|
|
directory with `index.md` for entries with co-located assets), one frontmatter
|
|
schema.
|
|
|
|
## `writings/` (blog / essays)
|
|
|
|
```toml
|
|
+++
|
|
title = "..."
|
|
description = "..."
|
|
author = "andwati"
|
|
date = 2026-04-08
|
|
updated = 2026-04-10 # optional, only when the post has been revised
|
|
draft = false
|
|
canonical_url = "" # optional, only if first published elsewhere
|
|
[taxonomies]
|
|
tags = ["..."]
|
|
[extra]
|
|
series = "..." # optional
|
|
series_index = 1 # required if `series` is set
|
|
llms_description = "..." # optional per-post override of the site-wide one
|
|
+++
|
|
```
|
|
|
|
## `portfolio/` (projects / professional work)
|
|
|
|
```toml
|
|
+++
|
|
title = "..."
|
|
description = "..."
|
|
role = "..."
|
|
date_start = 2025-01-01
|
|
date_end = 2025-06-01 # optional, omit if ongoing
|
|
outcome = "one-line result"
|
|
draft = false
|
|
[[links]]
|
|
label = "Live"
|
|
url = "https://..."
|
|
[[links]]
|
|
label = "Source"
|
|
url = "https://..."
|
|
[taxonomies]
|
|
tags = ["..."] # stack / tech tags
|
|
+++
|
|
```
|
|
|
|
Body is a free-form Markdown case study.
|
|
|
|
## `bookshelf/` (books *and* papers)
|
|
|
|
`kind` selects which identifier fields apply — books use `isbn`, papers use
|
|
`doi`/`arxiv_id`. Book covers are fetched from Open Library by ISBN and
|
|
cached into `apps/site/public/covers/` (served at `/covers/...`), never
|
|
fetched at page-render time. Papers have no reliable cover source, so
|
|
`cover_image` is set manually for them, if at all; Semantic Scholar is only
|
|
used to backfill a missing title/authors by DOI/arXiv id.
|
|
|
|
Note: the field is named `read_status`, not `status` — Strapi's
|
|
content-manager API treats a top-level `status` key as its own reserved
|
|
draft/publish parameter even with `draftAndPublish` disabled, so an
|
|
attribute literally named `status` silently breaks writes. Don't rename it
|
|
back.
|
|
|
|
```toml
|
|
+++
|
|
title = "..."
|
|
kind = "book" # "book" | "paper"
|
|
authors = ["..."]
|
|
isbn = "9780000000000" # books only
|
|
doi = "10.1000/xyz123" # papers only
|
|
arxiv_id = "2101.00001" # papers only, optional
|
|
url = "https://..." # papers only: landing page / source link
|
|
cover_image = "/covers/slug.jpg"
|
|
rating = 5 # optional, 1-5
|
|
read_status = "read" # "reading" | "read" | "abandoned"
|
|
date_started = 2026-01-01
|
|
date_finished = 2026-01-20 # optional, omit if still in progress
|
|
[taxonomies]
|
|
tags = ["..."] # genre / topic
|
|
+++
|
|
```
|
|
|
|
Body is a free-form personal review/notes.
|
|
|
|
## `blogs/` (curated external blogs)
|
|
|
|
```toml
|
|
+++
|
|
title = "..." # the blog/site's name
|
|
url = "https://..."
|
|
feed_url = "https://.../rss.xml"
|
|
+++
|
|
```
|
|
|
|
Body is a one-to-two sentence blurb on why you follow it. No live feed
|
|
fetching — this list is hand-curated and static.
|
|
|
|
## Markdown extras (all body content)
|
|
|
|
- **LaTeX**: `$inline$` and `$$block$$` math renders to static HTML/MathML
|
|
at build time (`marked-katex-extension` in `toml-content-loader.ts`) — no
|
|
client-side JS shipped for it.
|
|
- **Callouts**: `{% <note> %} ... {% </note> %}` (also `tip`/`warning`/
|
|
`danger`) renders as a styled callout box.
|
|
- **YouTube embeds**: paste a plain `<iframe src="https://www.youtube-
|
|
nocookie.com/embed/VIDEO_ID" ...></iframe>` — it's automatically wrapped
|
|
in a responsive 16:9 container, gets `loading="lazy"`, and emits
|
|
`VideoObject` structured data. No shortcode needed.
|