content/auto-draft
needs attentionviewing older commit8b79d33 · incrementalPR #304reviewed 2026-07-14 18:10 UTC5H · 7M · 11L · 8I- Purpose
- Improve the blog post reading experience with reusable UI components (PostImage lightbox, sticky TOC sidebar) and add a Tarifas-module screenshot to the GDMTH article.
- Goal
- Ship PostImage + TableOfContents components to enhance GDMTH blog post visual presentation and navigation UX
- Sub-goals
- PostImage: click-to-zoom lightbox with keyboard and caption CTA support
- TableOfContents: sticky scroll-spy sidebar hidden on mobile, visible on xl+
- tarifa-gdmth.mdx: add Tarifas-module screenshot via PostImage
- page.tsx: article+aside flex layout; Button variant cancel→secondary fix
- What
- Added two new client components (PostImage lightbox, TableOfContents scroll-spy) and restructured the blog post page layout to support a sticky sidebar TOC on xl+ screens while keeping an inline TOC for mobile. The GDMTH article was updated with a product screenshot.
- Why
- Richer blog content (screenshots, interactive TOC) improves engagement and supports the content-pipeline's autonomous draft flow.
- Areas
- apps/web/src/components/blog+138−0apps/web/src/app/(marketing)/blog+129−20apps/web/content/blog+214−1apps/web/src/styles+8−2
- Blast
- ~500 lines across apps/web only (blog content + UI components); no API, domain, or infra changes.
Findings · 12
correctness5
No focus trap in lightbox dialog
apps/web/src/components/blog/PostImage.tsx:97
role=dialog aria-modal has no Tab trap. Keyboard users escape the modal. WCAG 2.1 SC 2.1.2 violation.
Dialog never receives initial focus on open
apps/web/src/components/blog/PostImage.tsx:97
When open becomes true, focus stays on the thumbnail button. Screen readers don't perceive the modal opened. Fix: focus the close button in a useEffect([open]).
Scroll-lock cleanup clears competing PostImage's lock
apps/web/src/components/blog/PostImage.tsx:52
Cleanup always resets body.style.overflow='' — clears another open lightbox's scroll lock if two PostImages coexist. Use classList.toggle instead of inline style.
IntersectionObserver rootMargin -70% misses last section on short pages
apps/web/src/app/(marketing)/blog/_components/TableOfContents.tsx:25
Last section heading may never enter the active zone if its section is shorter than 70% of viewport. Active state stays stuck on second-to-last item.
Initial active state stale after SPA route change
apps/web/src/app/(marketing)/blog/_components/TableOfContents.tsx:14
useState initializes to items[0].id once. On soft navigation reusing the layout, active briefly shows the first item of the old set.
security1
No CSP headers on marketing site (pre-existing)
Not introduced here but the raw img in the lightbox needs an img-src carve-out when CSP is eventually added.
conventions3
Multi-paragraph JSDoc violates one-line-max rule
apps/web/src/components/blog/PostImage.tsx:9
7-line JSDoc block. CLAUDE.md: 'Never write multi-paragraph docstrings or multi-line comment blocks — one short line max.'
Multi-paragraph JSDoc violates one-line-max rule
apps/web/src/app/(marketing)/blog/_components/TableOfContents.tsx:7
5-line JSDoc block. Same rule violation. Remove or condense to one line.
Multi-line comment on getToc function
apps/web/src/app/(marketing)/blog/[slug]/page.tsx:58
Three-line comment block. The only non-obvious part fits in one line.
improvement3
Empty aside occupies 240px layout at xl+ when toc < 4 items
apps/web/src/app/(marketing)/blog/[slug]/page.tsx:317
TableOfContents returns null but the aside wrapper with w-60 is always rendered. At xl+ this takes 240px of layout space with nothing inside.
open/zoomed two-boolean state encodes invalid combination
apps/web/src/components/blog/PostImage.tsx:37
open=false+zoomed=true is reachable. A single type LightboxState = 'closed'|'open'|'zoomed' eliminates the invalid state.
TOC threshold (>= 4) split across two files
apps/web/src/app/(marketing)/blog/[slug]/page.tsx:244
Changing the threshold requires edits in page.tsx and TableOfContents.tsx. Extract to a shared constant.
History · 14 commits
- c27ea57needs attentionincremental3H · 5M · 4L2026-08-04 17:46
- ff69645needs attentionincremental0H · 6M · 8L2026-07-28 18:21
- 0257d74safeincremental0H · 0M · 0L2026-07-20 23:00
- 0795a9dsafeincremental0H · 0M · 2L2026-07-20 22:42
- 5c57414needs attentionincremental2H · 7M · 10L2026-07-20 22:29
- 5a57a8fneeds attentionincremental0H · 5M · 8L2026-07-20 17:14
- cc9056eneeds attentionfull1H · 3M · 9L2026-07-20 16:40
- 8b79d33needs attentionincremental5H · 7M · 11L2026-07-14 18:10current
- f0c337cneeds attentionincremental1H · 1M · 5L2026-07-14 17:15
- 5c9de97needs attentionincremental0H · 5M · 7L2026-07-14 17:02
- 92cce08needs attentionincremental1H · 4M · 3L2026-07-14 02:00
- b7dd610needs attentionincremental0H · 6M · 7L2026-07-14 01:43
- 8c43554needs attentionfull0H · 2M · 1L2026-07-14 01:15
- 2149391blockedfull8H · 6M · 7L2026-07-10 17:30