7 Commits
Author SHA1 Message Date
andwati d931b47af2 Allow Cloudflare Web Analytics in the CSP
Cloudflare auto-injects beacon.min.js, which the CSP was blocking.
2026-10-07 11:29:43 +03:00
andwati e0f0ae7c7f Fix GA measurement ID and CSP, redirect legacy Hugo URLs
Use the GA4 measurement ID Search Console expects and allow Google Tag Manager/Analytics in the CSP so gtag is not blocked. Add 301s from the old /posts, /writings, /tags, /archive, /about and /index.xml URLs to their current routes to clear the Search Console 404s.
2026-10-07 11:20:31 +03:00
andwati ec6401ac38 Replace Astro/Strapi site with Next.js static export
- Fresh structure: Next.js App Router + Tailwind v4 at the repo root, exported
  as static files (output: export) and served by static-web-server
- Remove the Strapi CMS, Astro app, Pagefind search, comments, old scripts,
  Lighthouse config and compose files; content in content/ is now plain
  markdown with YAML frontmatter
- Pages: About, Writing, Reading (3D bookshelf) and Portfolio, styled after
  adammaj.com
- SEO: metadata and canonicals, JSON-LD, generated OG images, sitemap, robots,
  RSS and JSON feeds, llms.txt
- Umami via PUBLIC_UMAMI_SCRIPT_URL / PUBLIC_UMAMI_WEBSITE_ID (build args)
- Dockerfile builds the export and serves it with sws.toml (security headers,
  clean URLs, .well-known, OG image content type, immutable asset caching)
- Prettier and cspell for content with a husky/lint-staged pre-commit hook
- Old redirect list dropped to start from a clean slate
2026-09-30 12:48:14 +03:00
andwati 12dd32ea4f Polish nav, transitions, layout, SEO, a11y, LaTeX, and Lighthouse CI
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
2026-09-12 20:40:16 +03:00
andwati d958a298f7 feat(cms): implement Git publishing for content changes and add entrypoint script
- Added a Docker entrypoint script to manage content repository cloning and updates.
- Updated server configuration to include public URL and proxy settings.
- Enhanced content file utilities to return file paths for written entries.
- Introduced GitPublisher class for handling Git operations and deployments.
- Added tests for Git publishing functionality.
- Updated Dockerfile and docker-compose for production setup.
- Added new public PGP key and security.txt for contact information.
- Created new About and Archive pages in the site.
- Updated site configuration to include new navigation links.
2026-09-01 16:50:26 +03:00
andwati 1b7c4039e3 Migrate legacy Zola posts into content/writings/ (additive)
scripts/migrate-posts-to-writings.mjs converts all 20 posts under
content/posts/ into the new writings schema — normalizes dates to
bare TOML local-dates, converts the one unambiguous Zola shortcode in
use (youtube embeds) to a plain <iframe>, and strips the now-meaningless
{% raw %}/{% endraw %} escape tags. content/posts/ itself is untouched;
this only adds content/writings/*.

Also generates 301 redirects from the old /posts/<slug>/ URLs to the
new /writings/<slug>/ ones into sws.toml, inside marker comments so
re-running the script regenerates that block idempotently instead of
duplicating it.

Two posts (from-c-to-machine-code, self-hosting-gitea-and-mirroring-github)
use note/tip/warning/danger callout shortcodes that have no Astro
equivalent yet — flagged by the script rather than guessed at, since
that needs a real component once the design system exists.

Verified: astro check clean, full site builds (80 pages, all real
migrated posts render), redirect generation confirmed idempotent
across repeated runs.
2026-08-30 19:10:53 +03:00
andwati f87b818201 Serve security headers via static-web-server, drop Cloudflare deploy path
Production (Dokploy/Docker) was sending no security headers at all -
static/_headers and wrangler.toml were Cloudflare Pages/Workers
config that static-web-server never reads. Added sws.toml with the
same CSP/security headers and the two redirects from _redirects,
wired into the Dockerfile. Removed wrangler.toml, static/_headers,
static/_redirects, the wrangler devDependency/deploy script, and
related .gitignore/.dockerignore entries since Cloudflare deploy is
no longer used.

Also fixed a console error in theme.js where the Utterances
postMessage call could fire before the comments iframe navigated to
utteranc.es.
2026-08-19 12:31:44 +03:00