chore(release): v0.5.0 changelog + bump min_version + README

Adds v0.5.0 release notes to changelog highlighting linktree parity,
Lighthouse improvements, and new opt-in features. Bumps minimum Hugo
version requirement in theme.toml. Updates README with v0.5 feature
summary.

Prepares release for publication.
This commit is contained in:
2026-05-15 19:51:23 +07:00
parent 7cd9833860
commit daa715a702
3 changed files with 124 additions and 9 deletions
+38 -3
View File
@@ -4,9 +4,44 @@ All notable changes to this project are documented here. Format follows [Keep a
## [Unreleased]
### Deferred to v0.5
- **Auto-generated OG images** (#7) — moved again from v0.4 because no candidate path meets the ≤ 30 KB binary budget. Needs measured prototype before committing.
- **Multi-section bio** (#9) — schema design call deferred per the issue's own "2026-Q3 candidate" recommendation.
## [0.5.0] — 2026-05-15
Linktree-parity + Lighthouse release. Closes #7 (auto-OG, infra-only) and #9 (multi-section bio). All additions are strictly opt-in or transparent; v0.4 sites upgrade with no config edits.
### Added
- **Multi-section bio** (#9) — opt-in `[[params.sections]]` with `title`, `description`, per-section `layout` override, and `[[sections.links]]`. Flat `[[params.links]]` still works (sections win + Hugo `warnf` on collision). Schema-person `sameAs` and RSS feed flatten across sections.
- **Per-link enhancements** — five new optional fields: `image` (thumbnail), `featured` (accent-bordered emphasis), `startDate`/`endDate` (build-time visibility window), `rel` (appended to safe `noopener noreferrer` baseline), `note` (small caption under title).
- **Share button** — `params.share = true` renders a `<button class="bio__share">` using `navigator.share` with `navigator.clipboard.writeText` fallback + ARIA-live toast. ~700 B JS loaded `defer` only when opted in.
- **QR code block** — `params.qr = true` renders a `<details>` collapsible with a build-time-generated QR PNG of `Permalink` via Hugo's built-in `images.QR` (Hugo ≥ 0.140).
- **vCard download** — `params.vcard = true` serves `/vcard.vcf` and renders a "Save contact" link. Requires registering the `VCard` output format in `hugo.toml` (see exampleSite).
- **Opt-in GA4 click analytics** — `[params.analytics]` block with `measurementId`, `trackClicks`, `utmSource`, `utmMedium`, `utmCampaign`. Auto-emits `data-analytics-event="link:<slug>"` per link and injects UTM query strings into external link hrefs at build time. **GA4 cookies require GDPR/PECR/CPRA consent** — see `docs/deployment-guide.md`.
- **Hugo image pipeline** — avatar now emits `<picture>` with AVIF + WebP + JPEG variants at 1x + 2x densities when `params.avatar` resolves as a local resource. External-URL and SVG-initials branches unchanged.
- **Fingerprinted + SRI-protected assets** — `bonsai.css`, `gallery.css`, `theme-toggle.js`, `share.js` now ship via `resources.Get | resources.Minify | resources.Fingerprint "sha384"` with `integrity=` + `crossorigin="anonymous"` attributes. Enables 1-year `Cache-Control: immutable` headers.
- **Auto-OG image (infra-only)** (#7) — `params.ogAuto = true` with `ogAutoBase` + `ogAutoFont` triggers a build-time `images.Filter`/`images.Text` overlay of `name` + `tagline` on a user-supplied base PNG. Theme ships template logic only — no vendored font or base PNG (30 KB binary-budget gate per validation session 1).
- **WebSite JSON-LD** — opt-in `params.schemaWebSite = true` emits a second `<script type="application/ld+json">` of `@type: WebSite` alongside existing Person schema.
- **Web manifest** — opt-in `params.manifest = true` registers a `Manifest` Hugo output format and emits `<link rel="manifest">`. Derives `name`/`theme_color`/`background_color`/icons from existing params.
- **Skip-link** — first child of `<body>` for keyboard navigation. Hidden off-screen until focused.
- **`<meta>` hardening** — `<link rel="canonical">`, `<meta property="og:url">`, `<meta name="robots">` (default `index,follow`), opt-in `<meta name="theme-color">`. Optional avatar `<link rel="preload">` for local-path avatars.
- **Avatar `<img>` attributes** — `width="112" height="112" fetchpriority="high" decoding="async"` for CLS prevention + LCP prioritization.
- **Tap-target enforcement** — `.bio__links--inline .link` now ≥ 48×48 px with ≥ 8 px gap per Lighthouse SEO / WCAG 2.5.5.
- **Hreflang alternates** — emitted automatically when site has > 1 language configured.
- **Deploy templates** — `exampleSite/static/_headers` (Netlify / Cloudflare Pages) + `exampleSite/vercel.json` with CSP, security headers, and 1-year `Cache-Control: immutable` on hashed assets.
- **`docs/deployment-guide.md`** — 4-host coverage (Netlify, Cloudflare Pages, Vercel, GitHub Pages + Cloudflare Worker overlay) with rationale per security header and GA4 consent guidance.
- **`docs/system-architecture.md`** + **`docs/code-standards.md`** — formal contributor docs.
- **`.github/workflows/lighthouse-ci.yml` + `.lighthouserc.json`** — Lighthouse CI gating. Thresholds **≥ 0.90 across all 4 categories** — tightened from the user's "≥ 80" brief during validation session 1.
- **7 new i18n keys** — `skip_to_content`, `share_label`, `share_copied`, `share_failed`, `show_qr`, `qr_alt`, `save_contact`. Both `en` and `vi` bundles updated.
### Changed
- **Min Hugo version: `0.140.0`** (was `0.128.0`). Required for `images.QR` and modern AVIF processing.
- **CSS + JS file location**: `static/css/*.css``assets/css/*.css`, `static/js/*.js``assets/js/*.js`. Required for the new resource pipeline. End-users see no change — Hugo serves fingerprinted output at `/css/*.{hash}.css`.
- **Sections/links collision** — when both `params.sections` and `params.links` are set, sections render and a `warnf` nudges the user.
- **OG image cascade** — order is now: `ogImage=false` → suppress · `ogImageUrl` → explicit · `ogAuto` + assets → auto-generated · `avatar` → fallback.
### Removed
- None. All changes additive or backward-compatible.
### Deferred
- None. v0.4's deferred items (#7 auto-OG, #9 multi-section) both shipped.
## [0.4.0] — 2026-05-10
+85 -5
View File
@@ -2,7 +2,7 @@
[![build](https://github.com/tiennm99/bonsai/actions/workflows/build.yml/badge.svg)](https://github.com/tiennm99/bonsai/actions/workflows/build.yml)
[![license](https://img.shields.io/github/license/tiennm99/bonsai)](LICENSE)
[![Hugo](https://img.shields.io/badge/hugo-%E2%89%A50.128-ff4088?logo=hugo)](https://gohugo.io)
[![Hugo](https://img.shields.io/badge/hugo-%E2%89%A50.140-ff4088?logo=hugo)](https://gohugo.io)
A minimalist Hugo theme for link-in-bio pages, inspired by [Linktree](https://linktr.ee) and the Japanese art of [bonsai](https://en.wikipedia.org/wiki/Bonsai) — *small, curated, intentional*.
@@ -14,15 +14,32 @@ Bonsai treats your bio page the same way: a quiet, well-pruned page that surface
## Features
**Core**
- **Single-page bio** — name, avatar, tagline, links. Nothing else.
- **Data-driven links** — defined in `[[params.links]]`; no content files required.
- **Data-driven links** — `[[params.links]]` flat list, OR `[[params.sections]]` for grouped sections with headings (v0.5).
- **45 icons out of the box** — 32 brand (GitHub, Mastodon, Bluesky, Spotify, Figma, Matrix…) + 13 utility (mail, globe, rss, heart, download…). Vendored from [Simple Icons](https://simpleicons.org) and [Lucide](https://lucide.dev).
- **Light & dark mode** — respects `prefers-color-scheme`; optional toggle.
- **Zero JavaScript by default** — pure HTML + CSS; opt-in JS for theme toggle only.
- **Fast** — < 3 KB gzipped CSS, no web fonts (system stack), no runtime fetches.
- **Accessible** — semantic HTML, focus-visible outlines, `prefers-reduced-motion`.
- **Zero JavaScript by default** — pure HTML + CSS; opt-in JS only for theme toggle / share button / GA4.
- **Fast** — ~2.4 KB gzipped CSS, no web fonts (system stack), no runtime fetches.
- **Accessible** — semantic HTML, skip-link, 48×48 tap targets, `focus-visible` outlines, `prefers-reduced-motion`, WCAG-AA palettes.
- **Responsive** — mobile-first, looks right at every viewport.
**Linktree-parity opt-ins (v0.5)**
- **Per-link enhancements** — `image` thumbnails, `featured` flag, `startDate`/`endDate` scheduled visibility, custom `rel`, `note` caption.
- **Share button** — `navigator.share` with clipboard fallback + ARIA-live toast (`params.share`).
- **QR code** — build-time PNG of the page Permalink via `images.QR` (`params.qr`).
- **vCard download** — generated `.vcf` at `/vcard.vcf` for one-click contact import (`params.vcard`).
- **GA4 click analytics** — `[params.analytics]` block; auto-emits `data-analytics-event` + UTM injection. *GA4 sets cookies — consent is your responsibility.*
**Lighthouse hardening (v0.5)**
- **`<picture>` avatar** — AVIF + WebP + JPEG at 1x/2x via Hugo's image processor.
- **Fingerprinted CSS/JS** with SRI `integrity=` — enables 1-year `Cache-Control: immutable`.
- **Canonical + `og:url` + theme-color + robots meta** emitted by default.
- **Hreflang alternates** automatic for multi-language sites.
- **Web manifest + WebSite JSON-LD** — opt-in via `params.manifest` / `params.schemaWebSite`.
- **Deploy templates** for Netlify / Cloudflare Pages / Vercel / GitHub Pages — see [`docs/deployment-guide.md`](./docs/deployment-guide.md).
- **Lighthouse CI** workflow gating PRs at ≥ 0.90 across Performance, A11y, Best-Practices, SEO.
## Quick Start
### As a Git submodule (simplest)
@@ -98,8 +115,25 @@ disableKinds = ["taxonomy", "term", "RSS", "sitemap", "404"]
| `layout` | string | `stack` | Link arrangement: `stack`, `grid`, or `inline`. See [Layout variants](#layout-variants). |
| `themeToggle` | bool | `false` | Render a sun/moon button in the footer + load the toggle script. |
| `rss` | bool | `false` | Render an RSS 2.0 feed of `[[params.links]]` at `/index.xml` and emit `<link rel="alternate">` in `<head>`. Requires removing `RSS` from `disableKinds`. |
| `share` | bool | `false` | Render a page share button using `navigator.share` with clipboard fallback. Loads `share.js` (~700 B) only when enabled. |
| `qr` | bool | `false` | Render a collapsible `<details>` block with a build-time QR code (PNG) of the page Permalink. Requires Hugo ≥ 0.140 (`images.QR`). |
| `vcard` | bool | `false` | Serve a vCard at `/vcard.vcf` and render a "Save contact" link. **Requires registering the `VCard` output format in `hugo.toml`** — see [output-format setup](#output-format-setup) below. |
| `manifest` | bool | `false` | Emit a `<link rel="manifest">` and serve `/manifest.webmanifest`. **Requires registering the `Manifest` output format in `hugo.toml`** — see [output-format setup](#output-format-setup). |
| `schemaWebSite` | bool | `false` | Emit a second JSON-LD block (`@type: WebSite`) alongside the always-on `Person` schema. Helps rich-snippet eligibility. |
| `shortName` | string | first 12 chars of `name` | PWA `short_name` in `manifest.webmanifest`. Set when your name exceeds 12 chars. |
| `themeBackground` | string (CSS color) | `#f4efe6` | PWA splash `background_color` in `manifest.webmanifest`. |
| `ogAuto` | bool | `false` | Generate a 1200×630 OG image at build time by overlaying `name` + `tagline` text on a user-supplied base PNG. Requires `ogAutoBase` + `ogAutoFont`. Infra-only in v0.5: theme does not vendor base PNG / TTF font. |
| `ogAutoBase` | string (assets path) | — | Path under `assets/` to a 1200×630 base PNG used by `ogAuto`. Example: `og/my-base.png` resolves to `assets/og/my-base.png`. |
| `ogAutoFont` | string (assets path) | — | Path under `assets/` to a TTF font used by `ogAuto` text overlay. Subset to Latin-only is recommended (≤ 15 KB). |
| `analytics` | object | — | Opt-in Google Analytics 4 — see [Analytics](#analytics) below. |
| `ogImage` | bool | `true` | Set `false` to suppress all `og:image` / `twitter:image` tags. |
| `ogImageUrl` | string (URL) | — | Explicit OG preview image (1200×630 recommended). Overrides the avatar fallback and upgrades Twitter card to `summary_large_image`. |
| `ogAuto` | bool | `false` | Generate a 1200×630 OG image at build time by overlaying `name` + `tagline` text on a user-supplied base PNG. Requires `ogAutoBase` + `ogAutoFont`. Infra-only in v0.5: theme does not vendor base PNG / TTF font (binary-budget gate). |
| `ogAutoBase` | string (assets path) | — | Path under `assets/` to a 1200×630 base PNG used by `ogAuto`. Example: `og/my-base.png` resolves to `assets/og/my-base.png`. |
| `ogAutoFont` | string (assets path) | — | Path under `assets/` to a TTF font used by `ogAuto` text overlay. Subset to Latin-only is recommended (≤ 15 KB). |
| `themeColor` | string (CSS color) | — | Optional `<meta name="theme-color">`. Tints mobile browser chrome (iOS Safari, Android Chrome). Set to your palette's accent for cohesive look. |
| `robots` | string | `index,follow` | Value of `<meta name="robots">`. Set `noindex,nofollow` to hide the site from search engines. |
| `preloadAvatar` | bool | `true` | Preload `params.avatar` as the LCP image via `<link rel="preload">`. Skipped automatically when avatar is an external URL or unset. |
| `schema` | bool | `true` | Emit schema.org `Person` JSON-LD in `<head>`. Set `false` if you provide your own. |
| `jobTitle` | string | — | Optional `Person.jobTitle` field for JSON-LD. |
| `location` | string | — | Optional `Person.address` field for JSON-LD. |
@@ -155,6 +189,52 @@ disableKinds = ["taxonomy", "term", "sitemap", "404"]
The theme renders one `<item>` per link. `pubDate` is the build time (links lack intrinsic dates), so the feed updates whenever the site rebuilds — fine for a curated bio, less ideal for high-frequency feeds.
## Output-format setup
`params.vcard` and `params.manifest` rely on custom Hugo output formats. Without the registration below, the theme emits the `<link rel="manifest">` / "Save contact" anchor but Hugo doesn't generate the file itself.
Add to `hugo.toml`:
```toml
[mediaTypes."text/vcard"]
suffixes = ["vcf"]
[mediaTypes."application/manifest+json"]
suffixes = ["webmanifest"]
[outputs]
home = ["HTML", "RSS", "Manifest"] # add "VCard" if params.vcard = true
[outputFormats.VCard]
mediaType = "text/vcard"
baseName = "vcard"
isPlainText = true
notAlternative = true
[outputFormats.Manifest]
mediaType = "application/manifest+json"
baseName = "manifest"
isPlainText = true
notAlternative = true
```
Only include the blocks you need. See `exampleSite/hugo.toml` for a working reference.
## Analytics
Off by default. To enable Google Analytics 4 click tracking and UTM injection on external links:
```toml
[params.analytics]
measurementId = "G-XXXXXXXXXX" # required to enable
trackClicks = true # default; emits data-analytics-event="link:<slug>" + click listener
utmSource = "bio" # if set, appended to external link hrefs at build time
utmMedium = "bio"
# utmCampaign = "summer-launch"
```
> ⚠️ **GA4 sets cookies.** Sites serving EU / UK / California visitors require explicit user consent before loading `gtag.js`. Bonsai does not ship a consent banner — pair with a consent management platform (Klaro!, Cookiebot, OneTrust) or skip GA4 entirely. See [`docs/deployment-guide.md`](./docs/deployment-guide.md#analytics--consent).
## i18n
Theme-rendered strings (nav landmark, theme-toggle labels, default footer) live in `i18n/{lang}.toml`. Bundles for `en` and `vi` ship with the theme.
+1 -1
View File
@@ -6,7 +6,7 @@ homepage = "https://github.com/tiennm99/bonsai"
demosite = "https://tiennm99.github.io/bonsai/"
tags = ["bio", "linktree", "minimal", "japanese", "personal", "landing", "icons"]
features = ["responsive", "dark mode", "fast", "no-js optional", "data-driven links"]
min_version = "0.128.0"
min_version = "0.140.0"
[author]
name = "tiennm99"