Files
portfolio/Ralph/progress.txt
T
admin 46cc22500b feat: US-004 - Role node redesign with clinical record pill badges
Role nodes now render as rounded rectangle pills (104x32px) with orgColor
badge styling, connector lines to timeline, and SVG drop shadow effects
on hover/pinned states.
2026-02-16 02:37:16 +00:00

73 lines
6.3 KiB
Plaintext

# Progress Log — Career Constellation Clinical Pathway Overhaul
# Branch: ralph/constellation-overhaul
# Started: 2026-02-16
## Codebase Patterns
- CareerConstellation.tsx is a D3 force-directed graph rendered in an SVG with React overlay buttons for accessibility
- D3 simulation uses forceSimulation with charge, link, x, y, and collide forces
- Module-level window.matchMedia reads for prefersReducedMotion and supportsCoarsePointer
- DashboardLayout manages constellation state: highlightedNodeId, pinnedNodeId via callbacks
- Work experience data in src/data/consultations.ts, skills in src/data/skills.ts, constellation-specific data in src/data/constellation.ts
- CSS layout: .pathway-columns is a grid that switches from 1fr (mobile) to minmax(0,1.15fr) minmax(0,1.5fr) at desktop breakpoint
- .pathway-graph-sticky has position: sticky; top: 12px; min-height: 100% for the graph column
- containerHeight prop drives graph height on desktop; on mobile (viewport < 1024px) uses MOBILE_FALLBACK_HEIGHT (360px)
- Use window.innerWidth for breakpoint checks, not container.clientWidth — the SVG container overflows on mobile
- Design tokens in index.css :root — use var(--accent), var(--border-light), var(--text-tertiary), etc.
- Use the d3-viz skill for all D3 rendering stories
- yScale domain reversal automatically flows through all timeline elements (guides, dots, labels, role positions, simulation forces) — no per-element changes needed
- Always use CSS custom properties (var(--border), var(--surface), var(--text-tertiary), etc.) for colours in D3 — never hardcode hex values
- SVG shadows: use <filter> with <feDropShadow> in <defs>, apply to <g> groups via .attr('filter', 'url(#filter-id)'), clear with .attr('filter', null)
- Role nodes are already pill-shaped rects (ROLE_WIDTH=104, ROLE_HEIGHT=32, ROLE_RX=16) with orgColor badge styling — check before re-implementing
## 2026-02-16 - US-001
- Reversed yScale domain from [minYear, maxYear] to [maxYear, minYear] so 2025 appears at top
- Updated buildScreenReaderDescription() to mention reverse-chronological order
- Files changed: src/components/CareerConstellation.tsx
- **Learnings for future iterations:**
- The yScale is the single source of truth for vertical positioning — reversing its domain is a one-line change that cascades to all D3 elements using it
- Year guide lines, year dots, year labels, role initial positions, and simulation forceY all reference yScale — no individual element updates needed
- buildScreenReaderDescription() is defined at module level (line ~63), not inside the component
---
## 2026-02-16 - US-002
- Removed fixed DESKTOP_HEIGHT/TABLET_HEIGHT/MOBILE_HEIGHT constants, replaced with MIN_HEIGHT (400) and MOBILE_FALLBACK_HEIGHT (360)
- Added containerHeight prop to CareerConstellation — DashboardLayout measures .chronology-stream via ResizeObserver and passes height
- getHeight() now takes containerHeight param: on mobile uses fallback, on desktop uses measured height with MIN_HEIGHT floor
- Used window.innerWidth for mobile breakpoint detection (container.clientWidth is unreliable due to SVG overflow)
- Files changed: src/components/CareerConstellation.tsx, src/components/DashboardLayout.tsx, src/index.css
- **Learnings for future iterations:**
- The CareerConstellation container div overflows on mobile — its clientWidth reports desktop-sized values even at 375px viewport. Always use window.innerWidth for responsive breakpoint checks in this component.
- ResizeObserver on .chronology-stream fires when cards expand/collapse, triggering height update in the graph — this is the key mechanism for dynamic sync.
- The dimensions useEffect depends on [containerHeight] so it re-runs when the measured height changes, updating the D3 scales.
- CSS grid column ratio was adjusted to minmax(0,1.15fr) minmax(0,1.5fr) to give the graph more horizontal space.
---
## 2026-02-16 - US-003
- Removed radial gradient background, replaced with clean var(--surface) fill
- Added 1px solid var(--border-light) border to the container div
- Refined timeline vertical rule to 1px stroke using var(--border) colour
- Replaced year dots (circles) with horizontal tick marks (6-8px lines extending right from timeline)
- Updated year labels fill to var(--text-tertiary)
- Made horizontal guide lines subtle: stroke-opacity 0.25, stroke-dasharray '3 4', using var(--border-light)
- Removed the entire SVG legend group (replacement HTML legend comes in US-008)
- Files changed: src/components/CareerConstellation.tsx
- **Learnings for future iterations:**
- All colours should use CSS custom property values (var(--border), var(--surface), etc.) rather than hardcoded hex values — the design system tokens are defined in index.css :root
- The legend was ~47 lines of D3 code; removing it is a significant net reduction. The HTML replacement in US-008 will be simpler React JSX
- Year ticks as horizontal lines are positioned with x1=timelineX, x2=timelineX+width — they extend right from the timeline axis, not centred on it
- The container div border + borderRadius + overflow:hidden creates a clean framed look for the SVG without needing an SVG-level border
---
## 2026-02-16 - US-004
- Added SVG filter defs for drop shadows: shadow-sm-filter (subtle, for hover/connected) and shadow-md-filter (stronger, for active/pinned)
- Updated applyGraphHighlight to apply shadow filters on role node `<g>` elements during highlight states
- Resting state: no filter; connected role: shadow-sm; active/pinned role: shadow-md with stroke-opacity 1 and stroke-width 1.5
- Note: most of US-004 (pill shape, orgColor styling, connector lines, focus rings, collision detection) was already implemented in prior iterations
- Files changed: src/components/CareerConstellation.tsx, Ralph/prd.json
- **Learnings for future iterations:**
- SVG drop shadows use `<filter>` with `<feDropShadow>` — apply to the parent `<g>` group, not the individual shape, for proper rendering
- Filter bounds need generous overflow (x/y -20%, width/height 140%+) to avoid clipping the shadow
- When clearing a filter, use `.attr('filter', null)` — not empty string
- The role node pill rendering (rect with rx/ry, orgColor fill at 0.12, border at 0.4) was built incrementally across US-003 and US-004 — check existing code before implementing to avoid duplication
---