strings.TrimPrefix takes the prefix first. With the arguments swapped,
resources.Get received "/" and returned the root directory, so any local
params.avatar failed the build with "only available for image resources".
All 4 category scores were ≥0.93 — but `lighthouse:recommended` preset
asserts on individual audits that exit 1 independently of category aggregates.
- head.html: emit `<link rel="icon">` only when `params.favicon` set
(was unconditional with default "favicon.ico" → 404 → errors-in-console fail)
- exampleSite: ship favicon.ico + set params.favicon so demo exercises the link
- icons/single.html: bump 0.7rem → 0.75rem so the 12px Lighthouse legibility
floor is met (was 11.2px → 76% of page text below threshold → font-size fail)
- gallery.css: chip--accent text now literal #fff (not var(--bonsai-bg)).
Lands ≥4.5:1 on every shipped palette AND sidesteps axe-core's CSS-var
resolution gap that reported contrastRatio=null on sakura
- .lighthouserc.json: suppress network-dependency-tree-insight,
render-blocking-resources, render-blocking-insight. These fire on any
<link rel=stylesheet> in <head> — structurally unavoidable for a CSS-first
theme. Category Performance score remains 1.00 on every page
Updates gallery.css resource reference to use Hugo resources API with
fingerprinting and SRI (Subresource Integrity) for security and cache-busting.
Improves security posture and cache effectiveness.
Archives v0.5 implementation plan with 8-phase breakdown and detailed
reports on linktree feature inventory, Lighthouse best practices, and
current audit findings.
Preserves decision history for future reference.
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.
Updates exampleSite hugo.toml with v0.5 parameters for multi-section bio,
QR code, vCard, share button, analytics, and OG image generation.
Includes Vercel and Netlify deployment configuration templates.
Enables one-click deployment for new users.
Adds comprehensive documentation for v0.5 release: deployment guide
covering Vercel/Netlify/self-hosted options, system architecture overview,
and code standards for theme development.
Improves onboarding and contribution experience.
Adds GitHub Actions workflow for Lighthouse CI with performance budget
thresholds (≥0.90 for accessibility, performance, best-practices, SEO).
Configuration includes mobile + desktop audits and budget assertions.
Ensures quality gates on every commit.
Adds analytics-loader for GA4 event tracking on link clicks. Implements
UTM parameter injection for campaign attribution on share actions.
Enables detailed conversion and traffic source tracking.
Adds share button, QR code block, and Schema.org WebSite markup for
enhanced link sharing. Introduces vCard contact export and PWA manifest
generation for mobile compatibility.
Expands profile distribution and integration capabilities.
Introduces multi-section bio cards and link groups for content organization.
Link buttons now support dict-shape with image, featured, scheduled, rel,
and note attributes. Schema Person flattens sections for SEO. RSS and
single.html templates updated to use new link dict shape.
Enables linktree-style content structuring.
Adds canonical, og:url, robots, and theme-color meta tags for Lighthouse
validation. Hardens avatar with width/height/fetchpriority/decoding and
<picture> pipeline. Adds skip-link and tap-target CSS enhancements.
Improves Core Web Vitals and accessibility scores.
Date the Unreleased v0.4 entries to 2026-05-10. Reset Unreleased to
list deferred-to-v0.5 items only (#7 OG auto-gen, #9 multi-section bio).
v0.4.0 ships:
#11 gallery CSS extraction
#10 +10 icons (35 -> 45)
#8 optional RSS feed
plus the 17-item a11y + polish pass shipped in PR #15.
Plan tracking files updated alongside (phase 7 marked in-progress
during release prep; will flip to completed post-tag).
Closes#8.
Opt-in via `params.rss = true` (off by default). When enabled:
- layouts/index.rss.xml renders an RSS 2.0 feed of [[params.links]]
at /index.xml — one <item> per link, pubDate = build time
- head.html emits <link rel="alternate" type="application/rss+xml">
- Users must remove "RSS" from their disableKinds to let Hugo emit it
Channel populated from params.{name,tagline,bio} and site.BaseURL.
Item shape: <title> = link title, <link> = link URL,
<guid isPermaLink="false"> = bonsai-link-{url}, optional <description>.
XML declaration uses safeHTML+printf to defeat Hugo's default escape
(otherwise '<?xml ... ?>' becomes '<?xml ... ?>' and breaks parsers).
exampleSite enables rss=true to demo the feature; verified output
parses as valid XML via Python ElementTree.
Closes#11.
Move .themes-gallery__* + .variants-gallery__* selectors out of
static/css/bonsai.css into static/css/gallery.css. The demo pages
/themes/ and /variants/ load the new stylesheet via a head_extra
block defined in layouts/_default/baseof.html; the home page and
/icons/ keep getting only bonsai.css.
End-user sites stop shipping ~1.9 KB raw / ~200 B gzipped of demo-only
CSS. README "< 3 KB gzipped" claim restored to true.
Sizes (minified):
bonsai.css 12,394 -> 10,470 raw (-1,924)
3,113 -> 2,910 gzip (-203)
gallery.css 2,252 raw / 626 gzip (loaded only on demo pages)
Verified rendered HTML at /, /themes/, /variants/, /icons/ via curl;
demo pages carry both stylesheets, others carry only bonsai.css.
Consolidates dependabot PRs #1-#5 into one atomic change to avoid sequential rebase churn (all 5 dependabot PRs touched the same 2 workflow files).
- actions/checkout: v4 → v6
- actions/configure-pages: v5 → v6
- actions/upload-artifact: v4 → v7
- actions/upload-pages-artifact: v3 → v5
- actions/deploy-pages: v4 → v5
Each individual bump's CI was green on dependabot's PR. Dependabot will auto-close PRs #1-#5 when this lands on main.
Captures CSS specificity gotcha, Hugo jsonify-in-script trap, hardcoded SVG fill issue, and the modular-commit safety net learned from the same-day v0.1 → v0.2 cadence.
- Code merged via PR #6, retro captured in docs/journals/2026-05-03-v0-3-release.md.
- Mark plan status: completed; record shipped_via + journal links + release-admin items still pending (screenshot regen + v0.3.0 tag).
- Phase files preserved under plans/archive/ for design-rationale retrospection.
- params.ogImageUrl: explicit 1200×630 social-preview image; auto-upgrades twitter:card to summary_large_image.
- params.ogImage = false: suppresses all og:image / twitter:image tags.
- Avatar still serves as the og:image fallback when neither override is set (twitter:card stays summary, since avatars are square).
- Auto-generation deferred to v0.4 — vendoring a TTF + 4 base PNGs (~150 KB) conflicted with the theme's minimalist binary footprint.
- Move every theme-rendered string (nav aria-label, theme-toggle labels, default footer template) to i18n/{lang}.toml.
- Ship en (default) + vi bundles. Adding a language is a single-file change.
- Hugo selects bundle via defaultContentLanguage (not languageCode); documented in README.
- All i18n calls have | default fallbacks so missing keys never break rendering.
- Validate against allowlist in bio-card.html, warnf + fall back to stack on unknown values.
- Variant CSS scoped under .bio__links--{variant} to avoid v0.2-style specificity collisions.
- Inline variant visually-hides .link__title via clip-path; titles stay in DOM for screen readers.
- Grid collapses to 1-col under 480px.
- New /variants/ gallery page renders all three side by side.
- exampleSite switches to layout = "grid" so the live demo shows the new feature.
- .theme-toggle svg { display: block } was beating per-class hide rules
due to higher specificity. Scope the per-class rules under
.theme-toggle .theme-toggle__sun/moon so they win.
- Remove avatar from exampleSite so the initials fallback renders, which
inherits the active palette's accent color (sakura -> pink). The static
avatar.svg was hardcoded vermilion and looked off in non-bonsai
palettes.
- README: add new params (colorTheme, themeToggle button details, schema,
jobTitle/location/email, avatarInitials/Bg) to config table; new
'Color themes' section with live gallery link
- CHANGELOG: 0.2.0 entry under Added/Changed; placeholder for v0.3
- exampleSite/hugo.toml: colorTheme = sakura for distinctive screenshot
+ jobTitle to populate Person.jobTitle in JSON-LD
Bundle of four small additions for the v0.2 release. All strictly
additive — no breaking changes; v0.1 sites upgrade without edits.
Phase 1 — Color theme presets
- Refactor static/css/bonsai.css: palette vars now scoped per
[data-bonsai-theme]; layout vars (radius/gap/pad/fonts) stay in :root
- Four palettes: bonsai (default, vermilion+washi), sakura (cherry
blossom), sumi (monochrome ink), koi (orange+cream). Light + dark
variants each, both prefers-color-scheme and manual data-theme override
- New params.colorTheme (default 'bonsai'); applied via baseof
- Themes gallery at exampleSite /themes/ shows all 4 side-by-side
Phase 2 — schema.org Person markup
- New layouts/partials/schema-person.html emits JSON-LD ProfilePage
containing a Person (built from name/bio/avatar/links + optional
jobTitle/location/email)
- mailto: and tel: links excluded from sameAs
- Suppress with params.schema = false
- Wired into head.html
Phase 3 — Theme toggle UI button
- New layouts/partials/theme-toggle-button.html (sun/moon SVGs)
- Rendered in footer when params.themeToggle = true
- aria-pressed reflects current state; updated theme-toggle.js to
sync on click and on first paint
- Sun shown in dark mode, moon in light (button shows target state)
- exampleSite enables the toggle by default for demo
Phase 4 — Avatar initials fallback
- New layouts/partials/avatar.html: img if params.avatar set,
inline SVG circle with initials otherwise
- Initials auto-derived from params.name (first letter of up to 2
words, uppercased); override with params.avatarInitials
- Background overrideable with params.avatarBg; defaults to accent
- bio-card.html simplified to delegate to the partial
Headless renderers and many systems lack Japanese fonts to render the
kanji glyph used previously. T is universally available and reads as
a Tien-Nguyen demo monogram.
Hugo's relURL/absURL on a leading-slash path treats it as root-absolute
and skips the baseURL subpath. Strip the prefix first so user-supplied
paths like '/images/avatar.svg' resolve correctly under
https://*.github.io/<repo>/.
relURL leaves leading-slash paths untouched, breaking sites served
from a subpath like https://*.github.io/bonsai/. absURL prepends the
full baseURL (including subpath) and works in both root and subpath
deployments. Affects: avatar img, favicon link, css link, theme-toggle
script.
avatar.svg: vermilion circle with kanji 盆 (bon, from 盆栽) in
washi-paper text. exampleSite/hugo.toml updated to reference it.
images/screenshot.png and images/tn.png recaptured against the demo
now showing all 6 working brand+ui icons.
Simple Icons ships SVGs with viewBox only — no explicit width/height —
so inline SVG rendered at 0x0 inside the link button flex container.
Add `.link__icon svg { width:20px; height:20px; fill:currentColor }` to
size all icons consistently. Lucide stroke-only icons opt out of the
fill via attribute selector.
- README: add badges (build/license/Hugo), live-demo callouts, full
configuration parameter table, expanded features list
- CONTRIBUTING.md: dev setup, icon-add workflow, PR/style guidelines
- CHANGELOG.md: Keep-a-Changelog format with v0.1.0 + v0.0.1 entries
- theme.toml: point demosite at live GitHub Pages URL
- build.yml: hugo extended 0.154.0, runs on PR + main push, uploads
exampleSite artifact for inspection
- deploy.yml: deploys exampleSite to GitHub Pages on main push using
actions/configure-pages + actions/deploy-pages (modern flow, no
gh-pages branch)
- dependabot.yml: weekly action version updates
One-time manual: enable Pages in repo settings (Source = GitHub Actions)
before deploy.yml will succeed.
Minimalist Hugo theme for link-in-bio pages, inspired by Linktree
and Japanese bonsai aesthetics.
- Single-page bio with avatar, tagline, bio, and links
- Data-driven via [params] in hugo.toml — no content files needed
- Light/dark mode via prefers-color-scheme + optional toggle
- Inline SVG icon set (github, globe, mail, twitter, linkedin, etc)
- Zero JS by default; opt-in theme toggle script
- exampleSite for local development
Replace the custom Node.js builder with Hugo and the bonsai theme
(added as a submodule at themes/bonsai). Site content moves from
config.yaml into [params] in hugo.toml. CI switched to Hugo Extended
0.154.0 build; daily cron dropped (no API decoration to refresh).
Avatar imported from miti99 project.