3.7 KiB
Constellation Hover Focus Mode - Scratchpad
Understanding
The feature requires a global dimming effect when hovering constellation nodes or skill pills. Currently:
- Constellation internal highlighting already works well via
useConstellationHighlight- dims non-connected nodes to 0.15 opacity within the SVG. - Skill pill hover (
RepeatMedicationsSubsection) callsonHighlight(skill.id)→ flows tosetHighlightedNodeId→ passed toCareerConstellationas prop → triggersapplyGraphHighlight(skillId). - Timeline item hover calls
onHighlight(entity.id)→ same flow → highlights that role in constellation. - Constellation node hover calls
onNodeHover(roleId)→setHighlightedRoleId→ highlights matching timeline item viaisHighlightedFromGraphprop.
What's Missing
The cross-component dimming doesn't exist yet:
- When hovering a constellation node: timeline items and skill pills don't dim (only the matching timeline item highlights)
- When hovering a skill pill: the constellation highlights but timeline items and other skill pills don't dim
- No global "focus mode" overlay or coordinated dimming across all three areas
Architecture Decision
Approach: CSS class-based global dimming with React context
Rather than a heavy context, I'll use the existing DashboardLayout state pattern:
- Add a
globalFocusIdstate toDashboardLayout(the orchestrator) - Add a
globalFocusTypeto know if it's a skill or role focus - Pass this down to timeline, skill pills, and constellation
- Each component applies a dimming class/style when not related to the focused ID
- Use the existing
connectedMapdata (already built in constellation) to resolve relationships
Key insight: When a skill is focused, the "related" timeline items are those whose entity.skills array contains that skill ID. When a role is focused, the "related" skills are those in that role's entity.skills. This data is already in timelineEntities.
Dimming approach: CSS transitions on opacity. Apply opacity: 0.25 to non-related elements, keep related ones at full opacity. Use transition: opacity 0.15s for smooth enter/exit.
Implementation (COMPLETED)
All implemented in a single commit (47b52b5):
-
DashboardLayout — Added
globalFocusIdstate, lookup maps (skillToRoles,roleToSkills,nodeTypeById), and computedfocusRelatedIds: Set<string> | null. BothhandleNodeHighlightandhandleNodeHovernow setglobalFocusId. -
useConstellationInteraction — Removed
d.type !== 'skill'guard ononNodeHoverso skill node hovers also propagate to parent for global focus. -
TimelineInterventionsSubsection — Receives
focusRelatedIds, computesisDimmedByFocusper entity, passes toExpandableCardShell. -
ExpandableCardShell — New
isDimmedByFocusprop appliesopacity: 0.25with 150ms transition. -
RepeatMedicationsSubsection —
focusRelatedIdsflows throughCategorySection→SkillRow, each skill row dims if not in related set. -
LastConsultationCard — Dims when
focusRelatedIdsis active and consultation.id not in set. -
CareerConstellation — New
globalFocusActiveprop + SVG classconstellation-focus-activetriggers CSS axis dimming. -
index.css — CSS rules dim
.axis-line,.year-tick,.year-labelto 0.25 opacity whenconstellation-focus-activeclass is present. Reduced-motion override removes transitions.
Verification
npm run typecheck— PASSnpm run lint— PASS (5 pre-existing warnings only)npm run build— PASS
Remaining
- Playwright MCP visual verification (reviewer task)
- Manual QA: hover each source (constellation node, skill pill, timeline item), verify dimming/restore