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.
4.8 KiB
Bonsai v0.2: Same-Day Polish Release Caught CSS Specificity and Hugo's JSON Escape Trap
Date: 2026-05-03 20:15
Severity: Medium
Component: Theme toggle UI, schema.org markup, CSS cascade, SVG assets
Status: Resolved
What Happened
Shipped v0.2.0 same day as v0.1.0 (7 hours apart): 4 color theme presets (sakura/sumi/koi via CSS custom properties), schema.org Person JSON-LD, theme-toggle button with sun/moon icons, avatar initials fallback SVG. All 5 phases completed in ~6h. Tagged release; live demo + themes gallery live. No breaking changes — all features opt-in via params.
The Brutal Truth
Adding features that fast after a critical v0.1 release felt reckless but the modularity kept us honest. We caught two ugly bugs only because the screenshot review was rigorous. A CSS specificity silent failure sneaked into production; a Hugo templating anti-pattern almost shipped JSON garbage into <script> tags. Both were low-level enough to silently break on edge cases. The discipline of small additive commits meant rollback was one revert, not six.
Technical Details
-
Hugo
jsonifydouble-encodes inside<script>context — Hugo's auto-escape pipeline treats<script>block content as JavaScript string literal. Output{{ $payload | jsonify }}becomes"{\"@context\":...}"(stringified JSON string) instead of raw JSON. Fix: pipe throughsafeJSto mark output as safe for JS context:{{ $payload | jsonify | safeJS }}. Manifests as broken schema.org payloads when parsed by search crawlers. -
CSS specificity silent battle on
.theme-toggle—.theme-toggle svg { display: block }(specificity 0,1,1) silently beat.theme-toggle__sun { display: none }(specificity 0,1,0). Both icons rendered at once, toggling did nothing visible. Caught only because screenshot diff showed two moons stacked. Fix: scope all per-icon rules under parent:.theme-toggle .theme-toggle__sun { display: none }to match the broader selector's context. Lesson: CSS specificity debugs invisibly. -
Hardcoded color in
exampleSite/static/images/avatar.svg— vermilionfill="#8b3a2b"locked the avatar to one palette; theme toggle recolored the site but avatar stayed red. Removed avatar from exampleSite entirely to showcase the new initials fallback feature. Felt like a loss until we realized the demo got better — validates that initials auto-inherit palette accent and live-update on theme toggle. -
Small + additive release cadence builds confidence in rollback — v0.1 was large. v0.2 added 4 features in 5 commits. When specificity bug surfaced at final review, the fix was a 2-line CSS change in a single commit. If all v0.2 work were one megacommit, rollback would've nuked 6 hours of work. Discipline in commit granularity pays off.
Root Cause Analysis
- Hugo template quirks aren't obvious from docs.
jsonifyworks fine in template interpolation but breaks in<script>. Needed to test the actual rendered HTML, not just the template logic. - CSS cascade assumed matching specificity. Both selectors hit the toggle element; only one was checked. No linting caught this because both selectors are valid — the bug is order + specificity interaction.
- Static assets assumed single-theme context. SVG fill attributes don't inherit from CSS custom properties by default. Should've validated asset reuse under theme toggle during phase 4 review.
- Commit discipline was the real safety net. Each phase was one clean commit. Enabled fast iteration without fear of losing work.
Lessons Learned
-
Test rendered HTML, not template output. For JSON-LD and
<script>blocks, inspect the final HTML in DevTools. Auto-escape traps are invisible in template files. -
CSS specificity bugs hide best in visual tests. Automated CSS linting misses specificity collisions. Screenshot diffs catch them immediately.
-
Static SVG assets don't theme well. If an SVG must recolor under theme toggle, either: remove the fill attribute and inherit from CSS, or generate it dynamically. Hardcoded fills are a gotcha.
-
Same-day releases are only safe if phases are modular. v0.1→v0.2 worked because phases had minimal coupling. Try this with a monolithic feature and you're fighting merge conflicts and rollback chaos.
Next Steps
- Monitor schema.org validation via Google Search Console (confirm JSON-LD is parsed correctly after safeJS fix).
- Consider CSS specificity audit for all
.theme-toggle__*variants before v0.3 (preventive). - For future static assets: add a validation step in screenshot capture that checks for hardcoded colors that don't inherit.
- v0.3 candidates: layout variants (cards/grid), OG image generation, i18n setup. Keep the same cadence: small, additive, low blast radius.
Owner: @tiennm99
Timeline: Next release pending catalog PR #708 feedback