Files
hoelee-blog/docs/design-guide.md
T
hoelee 0dae8b0daa
Deploy / build (push) Successful in 34s
fix(art): give both 2026-10-20 posts their artwork, and gate the build on it
The YuE2 and subtitles posts shipped text-only: render_from_art.mjs only READS
ogImage/banner, and after the 2026-10-19 bulk migration no script had to write
them, so both pages advertised /og-default.jpg and rendered no hero banner.

- new metaphors (in build_captions.py, frozen here via sync_prompts_catalog.py),
  captions, art-meta evidence, banner plan and the five derived files per slug
- frontmatter: ogImage + banner in EN and ZH for both posts
- scripts/check-assets.mjs is now step 1 of npm run build, so CI fails when a
  published post lacks a language-correct og card, banner, banner .webp or
  400/800 list thumb. It reported 16 problems on the unfixed tree, all here.
2026-10-20 23:37:52 +08:00

90 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design Guide — what the blog UI must have & must avoid
Read this **before** touching theme, layout, typography, color, or SEO markup. Priority order is fixed: **reading experience → SEO → brand → everything else.**
---
## 1. Non-negotiables (reading experience first)
| Rule | Spec |
|---|---|
| **Measure (line length)** | 65–75 characters per line. Single biggest readability win. |
| **Body typography** | 16–17px, `line-height: 1.6–1.7`, a clean sans (Inter / system-ui / Source Sans). |
| **Headings** | A character font (Plus Jakarta Sans / Space Grotesk) or keep Inter for minimalism. |
| **Code** | Proper monospace (JetBrains Mono / Fira Code) + distinct background block + **syntax highlighting + copy button + language label**. Non-negotiable for technical posts. |
| **Light + dark mode** | Both. Dev audience splits ~50/50. |
| **Color** | 2-accent palette (primary + semantic green/red for correct/wrong in tutorials). Avoid pure black / pure white. |
---
## 2. Required components
- **Sticky header** — name/logo + nav (Blog / Categories / About / RSS).
- **Article header** — title, pub date, reading time, category + tags, author card (photo, 1–2 line bio, LinkedIn + GitHub + git.hoelee.com links).
- **Author box / ProfilePage schema** on every post (see SEO below).
- **Related posts** (6 photo cards: 2 across on desktop, 1 across on phones with only the first 4 shown) — internal linking + keeps readers on-site. Each card shows the post's own language-specific share card, so an EN page never shows ZH art.
- **RSS link** in footer — how the technical audience follows me.
- **Custom og:image per post** — 1200×630, never the reused logo.
---
## 3. Responsive & media
- **Mobile-first**: design 390px, then desktop 1280px.
- **Astro Image** for automatic optimization/WebP (Core Web Vitals win).
- **Image format policy (measured 2026-10-16 — do not guess this one):**
| Asset | Served as | Why |
|---|---|---|
| `og:image` / `twitter:image` | **PNG** (1200×630) | Social scrapers (WhatsApp / LinkedIn / FB / Telegram) do not reliably render WebP share cards. Compatibility beats bytes here. |
| Article banner in-page | **WebP q78** + PNG fallback via `<picture>` | 6.02 MB → 2.52 MB across 70 banners (42%); JPEG would be *larger* (q85 = 129% of PNG) because the art is flat terminal-style with sharp text edges. |
| List thumbnails | **WebP q72 at 400w + 800w** (`/og/thumb/<name>-400.webp`) | Rows display at ~398px; they used to fetch the 1200×630 PNG (~45 KB). 400w is 5.8 KB, 800w is 16.1 KB. The homepage went from ~450 KB of thumbnails to ~62 KB. |
| Related-post cards | The post's **own share card** (`/og/<slug>.jpg`, ZH `/og/zh-<slug>.jpg`), `loading="lazy"` | The card must match the page language, and the thumb WebPs are keyed by base slug — using them would print EN art on ZH pages. 6 cards ≈ 390 KB, deferred below the fold. |
| Real photos (author, hardware) | **WebP q82** + JPEG fallback | Continuous-tone images are the opposite case from the flat art above. |
Regenerate all derivatives after adding a post: `uv run --with pillow python scripts/optimize-images.py`.
- **Every published post must point at its own art.** `scripts/check-assets.mjs` runs as the first
step of `npm run build` (so CI fails, not just warns) and rejects a post whose `ogImage` or
`banner` is missing or misnamed, or whose banner `.webp` / `-400` / `-800` thumbs are absent.
The renderers only *read* frontmatter — nothing writes these two fields for a new post, which is
how two 2026-10-20 posts shipped on `/og-default.jpg` with no hero banner.
- **Declare `og:image:width/height` explicitly** — mis-declared dimensions get preview cards rejected by WhatsApp/Telegram/LinkedIn.
- **Favicon**: reference all sizes (`.ico` + 32×32 + 192×192 PNG + apple-touch 180×180). "Favicon not showing" is usually browser cache, not a missing asset — `curl` the live files before "fixing".
---
## 4. Theme / stack decisions (settled)
- **Astro 5 static** (already scaffolded). Rationale: TypeScript/React skill surface, true static output, low attack surface, first-class i18n routing, and the repo itself is a portfolio artifact.
- **Theme base:** AstroPaper-style minimalism (light/dark, fuzzy search, RSS + sitemap defaults). Don't rebuild from scratch.
- **Skip for v1:** WordPress-headless (two systems), Next.js SSR/ISR (heavier than needed), self-built blog engine (the classic time sink).
---
## 5. SEO checklist (2026 — two lanes)
Optimize for **both** classic SEO *and* GEO (getting cited by Google AI Overviews / ChatGPT Search / Perplexity).
- **E-E-A-T first:** real `Person`/`ProfilePage` schema around the author bio, with `sameAs` → LinkedIn, GitHub, git.hoelee.com.
- **Per-post meta:** unique title (≤60 chars), description (≤155), canonical, og:image (1200×630) + `twitter:card`.
- **Sitemap.xml + RSS + robots.txt** — generated automatically.
- **Core Web Vitals:** static site → 90+ LCP/CLS/INP easy. Verify with headless-Chrome screenshot + Lighthouse on 390px **and** 1280px.
- **Internal linking:** related-posts module + link pillar posts (case studies) from every tutorial.
- **Question-shaped headings:** write H2/H3 as the actual question a learner types. Rewarded by Google *and* AI engines.
- **Name consistency:** same name ("Lee Teong Hoe" / "Mr Hoelee") + photo + handles across blog, LinkedIn, GitHub, git.hoelee.com. Fragmented identity is the #1 personal-brand SEO killer.
---
## 6. What to AVOID in design
| Anti-pattern | Why |
|---|---|
| Heavy JS / client-side rendering | Static output is the point — don't reintroduce runtime cost. |
| Reused logo as og:image | Already an issue on hoelee.com. Custom per post. |
| Anonymous/absent author card | Kills E-E-A-T. |
| Wall-of-text without code blocks | Technical posts need formatted, copyable code. |
| No RSS / sitemap | Both are free in Astro; omitting them is pure loss. |
| Orphaned pages / thin content | hoelee.com already has ~400; keep this sitemap clean. |
| Over-styled "agency look" | This is a personal engineering blog — credibility over flash. |