---
id: T02
parent: S04
milestone: M016
provides: []
requires: []
affects: []
key_files: ["frontend/src/components/TableOfContents.tsx", "frontend/src/App.css"]
key_decisions: ["Active styles use --color-accent-focus (15% opacity) vs hover's --color-accent-subtle (10% opacity) plus font-weight 500 for visual distinction", "rootMargin 0px 0px -70% 0px triggers active state when section enters top 30% of viewport"]
patterns_established: []
drill_down_paths: []
observability_surfaces: []
duration: ""
verification_result: "Frontend build passes with zero errors: cd frontend && npm run build (exit 0, 3.4s)"
completed_at: 2026-04-03T05:54:07.440Z
blocker_discovered: false
---

# T02: Added IntersectionObserver scroll-spy to ToC highlighting the active section as user scrolls through technique page content

> Added IntersectionObserver scroll-spy to ToC highlighting the active section as user scrolls through technique page content

## What Happened
---
id: T02
parent: S04
milestone: M016
key_files:
  - frontend/src/components/TableOfContents.tsx
  - frontend/src/App.css
key_decisions:
  - Active styles use --color-accent-focus (15% opacity) vs hover's --color-accent-subtle (10% opacity) plus font-weight 500 for visual distinction
  - rootMargin 0px 0px -70% 0px triggers active state when section enters top 30% of viewport
duration: ""
verification_result: passed
completed_at: 2026-04-03T05:54:07.440Z
blocker_discovered: false
---

# T02: Added IntersectionObserver scroll-spy to ToC highlighting the active section as user scrolls through technique page content

**Added IntersectionObserver scroll-spy to ToC highlighting the active section as user scrolls through technique page content**

## What Happened

Added useState for activeId and a useEffect that creates an IntersectionObserver watching all section/subsection heading elements. The observer uses rootMargin '0px 0px -70% 0px' so a section is considered active when it enters the top 30% of the viewport. On intersection, the topmost intersecting entry wins. The active ID drives conditional class names on each link. Added two CSS rules for the active state using --color-accent-focus background and font-weight 500, visually distinct from the hover state.

## Verification

Frontend build passes with zero errors: cd frontend && npm run build (exit 0, 3.4s)

## Verification Evidence

| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `cd frontend && npm run build` | 0 | ✅ pass | 3400ms |


## Deviations

Added non-null assertion on intersecting[0] after length > 0 guard — TypeScript couldn't narrow through .sort() chain.

## Known Issues

None.

## Files Created/Modified

- `frontend/src/components/TableOfContents.tsx`
- `frontend/src/App.css`


## Deviations
Added non-null assertion on intersecting[0] after length > 0 guard — TypeScript couldn't narrow through .sort() chain.

## Known Issues
None.
