feat: US-003 - Increase default skill visibility and reduce constellation column width
This commit is contained in:
+2
-2
@@ -37,7 +37,7 @@
|
|||||||
"Typecheck passes (npm run typecheck)"
|
"Typecheck passes (npm run typecheck)"
|
||||||
],
|
],
|
||||||
"priority": 2,
|
"priority": 2,
|
||||||
"passes": false,
|
"passes": true,
|
||||||
"notes": "Education entries use type 'role' — the constellation treats them identically to work roles for layout. They have deliberately few skill connections (2 for UEA, 1 for Highworth) to keep the lower timeline clean. The yScale computes domain from min/max startYear of role nodes, so adding 2009 entries automatically extends the range. Follow exact same data patterns as US-001. Education consultations may use simpler codedEntries and adapted examination content (results rather than workplace achievements). The consultations array should be ordered reverse-chronologically (newest first) — add education entries at the end. Use the d3-viz skill."
|
"notes": "Education entries use type 'role' — the constellation treats them identically to work roles for layout. They have deliberately few skill connections (2 for UEA, 1 for Highworth) to keep the lower timeline clean. The yScale computes domain from min/max startYear of role nodes, so adding 2009 entries automatically extends the range. Follow exact same data patterns as US-001. Education consultations may use simpler codedEntries and adapted examination content (results rather than workplace achievements). The consultations array should be ordered reverse-chronologically (newest first) — add education entries at the end. Use the d3-viz skill."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"Verify in browser: skills recognisable at a glance without hovering; work experience column visibly wider"
|
"Verify in browser: skills recognisable at a glance without hovering; work experience column visibly wider"
|
||||||
],
|
],
|
||||||
"priority": 3,
|
"priority": 3,
|
||||||
"passes": false,
|
"passes": true,
|
||||||
"notes": "Two independent changes in one story. Skill visibility: applyGraphHighlight in CareerConstellation.tsx has two branches — the 'no activeNodeId' resting state and the activeNodeId highlighted state. In the resting branch, change skill fill-opacity from 0.2 to 0.35, skill label opacity from 0 to 0.5, link stroke-opacity from 0.08 to 0.15. In the highlighted branch, change active skill fill-opacity from 0.85 to 0.9, dimmed node opacity from 0.06 to 0.15. Column width: in index.css @media (min-width: 1024px) for .pathway-columns, change grid-template-columns. The containerHeight/ResizeObserver system adapts the graph SVG automatically. Column order: first child is .chronology-stream (work experience), second is .pathway-graph-sticky (constellation). Use the d3-viz skill."
|
"notes": "Two independent changes in one story. Skill visibility: applyGraphHighlight in CareerConstellation.tsx has two branches — the 'no activeNodeId' resting state and the activeNodeId highlighted state. In the resting branch, change skill fill-opacity from 0.2 to 0.35, skill label opacity from 0 to 0.5, link stroke-opacity from 0.08 to 0.15. In the highlighted branch, change active skill fill-opacity from 0.85 to 0.9, dimmed node opacity from 0.06 to 0.15. Column width: in index.css @media (min-width: 1024px) for .pathway-columns, change grid-template-columns. The containerHeight/ResizeObserver system adapts the graph SVG automatically. Column order: first child is .chronology-stream (work experience), second is .pathway-graph-sticky (constellation). Use the d3-viz skill."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+35
-1
@@ -9,7 +9,7 @@
|
|||||||
- DashboardLayout manages constellation state: highlightedNodeId, pinnedNodeId via callbacks
|
- DashboardLayout manages constellation state: highlightedNodeId, pinnedNodeId via callbacks
|
||||||
- Work experience data in src/data/consultations.ts, constellation-specific data in src/data/constellation.ts
|
- Work experience data in src/data/consultations.ts, constellation-specific data in src/data/constellation.ts
|
||||||
- CSS layout: .pathway-columns grid — first column is .chronology-stream (work experience), second is .pathway-graph-sticky (constellation graph)
|
- CSS layout: .pathway-columns grid — first column is .chronology-stream (work experience), second is .pathway-graph-sticky (constellation graph)
|
||||||
- Current grid: minmax(0, 1.15fr) minmax(0, 1.5fr) at desktop — work experience 43%, graph 57%
|
- Current grid: minmax(0, 1.85fr) minmax(0, 1fr) at desktop — work experience ~65%, graph ~35%
|
||||||
- containerHeight prop drives graph height on desktop; on mobile (viewport < 1024px) uses MOBILE_FALLBACK_HEIGHT (360px)
|
- 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
|
- 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.
|
- Design tokens in index.css :root — use var(--accent), var(--border-light), var(--text-tertiary), etc.
|
||||||
@@ -22,6 +22,8 @@
|
|||||||
- Bidirectional highlighting: highlightedNodeId (timeline→graph) and highlightedRoleId (graph→timeline)
|
- Bidirectional highlighting: highlightedNodeId (timeline→graph) and highlightedRoleId (graph→timeline)
|
||||||
- Force simulation: role forceY ~0.98, charge -120/-55, link distance 72, collision ~52-65px roles
|
- Force simulation: role forceY ~0.98, charge -120/-55, link distance 72, collision ~52-65px roles
|
||||||
- applyGraphHighlight is the single source of truth for all visual states (resting, highlighted, dimmed)
|
- applyGraphHighlight is the single source of truth for all visual states (resting, highlighted, dimmed)
|
||||||
|
- Resting state values (US-003): skill fill-opacity 0.35, skill label opacity 0.5, link stroke-opacity 0.15, dimmed node opacity 0.15, active skill fill-opacity 0.9
|
||||||
|
- Initial D3 rendering values MUST match applyGraphHighlight resting values — initial stroke-opacity, fill-opacity, label opacity are set during node/link creation AND in the highlight function
|
||||||
- Use the d3-viz skill for all D3 rendering stories
|
- Use the d3-viz skill for all D3 rendering stories
|
||||||
- Consultation entries ordered reverse-chronologically (newest first) — new entries go at the end of the array
|
- Consultation entries ordered reverse-chronologically (newest first) — new entries go at the end of the array
|
||||||
- Constellation role nodes, skill mappings, and links are in constellation.ts — adding nodes there automatically extends yScale domain and screen reader description
|
- Constellation role nodes, skill mappings, and links are in constellation.ts — adding nodes there automatically extends yScale domain and screen reader description
|
||||||
@@ -39,3 +41,35 @@
|
|||||||
- The #00897B teal colour in index.css (:root --teal) is a generic design token, NOT the Tesco-specific colour — don't change it
|
- The #00897B teal colour in index.css (:root --teal) is a generic design token, NOT the Tesco-specific colour — don't change it
|
||||||
- Consultation.orgColor must match the constellation node orgColor for visual consistency between graph and cards
|
- Consultation.orgColor must match the constellation node orgColor for visual consistency between graph and cards
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-02-16 - US-002
|
||||||
|
- Added UEA MPharm (2011-2015, University of East Anglia, orgColor #7B2D8E) education node to constellation.ts
|
||||||
|
- Added Highworth A-Levels (2009-2011, Highworth Grammar School, orgColor #9C27B0) education node to constellation.ts
|
||||||
|
- Added roleSkillMappings: UEA → medicines-optimisation + data-analysis; Highworth → data-analysis
|
||||||
|
- Added constellationLinks with strength values (0.5, 0.3 for UEA; 0.2 for Highworth)
|
||||||
|
- Added consultation entries for both education entries to consultations.ts (at end of array, maintaining reverse-chronological order)
|
||||||
|
- Education nodes use type 'role' — treated identically by the constellation layout engine
|
||||||
|
- Updated role count comment to "6 roles + Education nodes (2)"
|
||||||
|
- Files changed: src/data/constellation.ts, src/data/consultations.ts
|
||||||
|
- **Learnings for future iterations:**
|
||||||
|
- Education entries use type 'role' and follow exact same data shape — no special handling needed
|
||||||
|
- yScale domain auto-extends from min/max startYear of role-type nodes, so adding 2009 entries extends the timeline automatically
|
||||||
|
- Education entries have deliberately few skill connections (2 for UEA, 1 for Highworth) per design to keep lower timeline clean
|
||||||
|
- Consultation entries go at end of array (reverse-chronological: newest first → oldest last)
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-02-16 - US-003
|
||||||
|
- Increased default skill node fill-opacity from 0.2 to 0.35 (initial render + applyGraphHighlight resting state)
|
||||||
|
- Increased default skill label opacity from 0 to 0.5 (labels now partially visible at rest)
|
||||||
|
- Increased default link stroke-opacity from 0.08 to 0.15
|
||||||
|
- Increased active/highlighted skill fill-opacity from 0.85 to 0.9
|
||||||
|
- Changed unconnected node dimming from 0.06 to 0.15 opacity
|
||||||
|
- Updated non-active link stroke-opacity in highlighted branch from 0.08 to 0.15
|
||||||
|
- Changed .pathway-columns desktop grid from 'minmax(0, 1.15fr) minmax(0, 1.5fr)' to 'minmax(0, 1.85fr) minmax(0, 1fr)' — work experience column now ~65%, constellation ~35%
|
||||||
|
- Files changed: src/components/CareerConstellation.tsx, src/index.css
|
||||||
|
- Browser verified: skills recognisable at a glance without hovering; work experience column visibly wider; constellation adapts to narrower container without clipping
|
||||||
|
- **Learnings for future iterations:**
|
||||||
|
- Initial D3 rendering attributes (set during node/link creation) must stay in sync with applyGraphHighlight resting values — there are TWO places to update for each visual property
|
||||||
|
- The highlighted branch also has a fallback opacity for non-active links/labels — remember to update those too (3 places total: initial render, resting branch, highlighted branch fallback)
|
||||||
|
- The constellation ResizeObserver + containerHeight system handles narrower columns automatically — no explicit graph resize code needed
|
||||||
|
---
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('stroke', 'var(--border-light)')
|
.attr('stroke', 'var(--border-light)')
|
||||||
.attr('stroke-width', 1)
|
.attr('stroke-width', 1)
|
||||||
.attr('stroke-opacity', 0.08)
|
.attr('stroke-opacity', 0.15)
|
||||||
.style('transition', prefersReducedMotion
|
.style('transition', prefersReducedMotion
|
||||||
? 'none'
|
? 'none'
|
||||||
: 'stroke 150ms ease, stroke-opacity 150ms ease, stroke-width 150ms ease'
|
: 'stroke 150ms ease, stroke-opacity 150ms ease, stroke-width 150ms ease'
|
||||||
@@ -410,7 +410,7 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
.attr('r', srDefault)
|
.attr('r', srDefault)
|
||||||
.attr('fill', d => domainColorMap[d.domain ?? 'technical'] ?? '#0D6E6E')
|
.attr('fill', d => domainColorMap[d.domain ?? 'technical'] ?? '#0D6E6E')
|
||||||
.attr('stroke', 'none')
|
.attr('stroke', 'none')
|
||||||
.attr('fill-opacity', 0.2)
|
.attr('fill-opacity', 0.35)
|
||||||
|
|
||||||
nodeSelection.filter(d => d.type === 'skill')
|
nodeSelection.filter(d => d.type === 'skill')
|
||||||
.append('text')
|
.append('text')
|
||||||
@@ -421,7 +421,7 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
.attr('font-size', isMobile ? '9' : '10')
|
.attr('font-size', isMobile ? '9' : '10')
|
||||||
.attr('font-family', 'var(--font-geist-mono)')
|
.attr('font-family', 'var(--font-geist-mono)')
|
||||||
.attr('pointer-events', 'none')
|
.attr('pointer-events', 'none')
|
||||||
.attr('opacity', 0)
|
.attr('opacity', 0.5)
|
||||||
.text(d => {
|
.text(d => {
|
||||||
const label = d.shortLabel ?? d.label
|
const label = d.shortLabel ?? d.label
|
||||||
const maxLen = isMobile ? 12 : 16
|
const maxLen = isMobile ? 12 : 16
|
||||||
@@ -460,22 +460,22 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
skillNodes.select('.node-circle')
|
skillNodes.select('.node-circle')
|
||||||
.transition().duration(dur)
|
.transition().duration(dur)
|
||||||
.attr('r', srDefault)
|
.attr('r', srDefault)
|
||||||
.attr('fill-opacity', 0.2)
|
.attr('fill-opacity', 0.35)
|
||||||
skillNodes.select('.node-label')
|
skillNodes.select('.node-label')
|
||||||
.transition().duration(dur)
|
.transition().duration(dur)
|
||||||
.attr('opacity', 0)
|
.attr('opacity', 0.5)
|
||||||
} else {
|
} else {
|
||||||
skillNodes.select('.node-circle')
|
skillNodes.select('.node-circle')
|
||||||
.attr('r', srDefault)
|
.attr('r', srDefault)
|
||||||
.attr('fill-opacity', 0.2)
|
.attr('fill-opacity', 0.35)
|
||||||
skillNodes.select('.node-label')
|
skillNodes.select('.node-label')
|
||||||
.attr('opacity', 0)
|
.attr('opacity', 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
linkSelection
|
linkSelection
|
||||||
.attr('stroke', 'var(--border-light)')
|
.attr('stroke', 'var(--border-light)')
|
||||||
.attr('stroke-width', 1)
|
.attr('stroke-width', 1)
|
||||||
.attr('stroke-opacity', 0.08)
|
.attr('stroke-opacity', 0.15)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -483,7 +483,7 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
const connected = connectedMap.get(activeNodeId) ?? new Set()
|
const connected = connectedMap.get(activeNodeId) ?? new Set()
|
||||||
const isInGroup = (id: string) => id === activeNodeId || connected.has(id)
|
const isInGroup = (id: string) => id === activeNodeId || connected.has(id)
|
||||||
|
|
||||||
nodeSelection.style('opacity', d => isInGroup(d.id) ? '1' : '0.06')
|
nodeSelection.style('opacity', d => isInGroup(d.id) ? '1' : '0.15')
|
||||||
|
|
||||||
nodeSelection.filter(d => d.type === 'role')
|
nodeSelection.filter(d => d.type === 'role')
|
||||||
.attr('filter', d => {
|
.attr('filter', d => {
|
||||||
@@ -504,16 +504,16 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
skillNodes.select('.node-circle')
|
skillNodes.select('.node-circle')
|
||||||
.transition().duration(dur)
|
.transition().duration(dur)
|
||||||
.attr('r', d => isInGroup(d.id) ? srActive : srDefault)
|
.attr('r', d => isInGroup(d.id) ? srActive : srDefault)
|
||||||
.attr('fill-opacity', d => isInGroup(d.id) ? 0.85 : 0.2)
|
.attr('fill-opacity', d => isInGroup(d.id) ? 0.9 : 0.35)
|
||||||
skillNodes.select('.node-label')
|
skillNodes.select('.node-label')
|
||||||
.transition().duration(dur)
|
.transition().duration(dur)
|
||||||
.attr('opacity', d => isInGroup(d.id) ? 1 : 0)
|
.attr('opacity', d => isInGroup(d.id) ? 1 : 0.5)
|
||||||
} else {
|
} else {
|
||||||
skillNodes.select('.node-circle')
|
skillNodes.select('.node-circle')
|
||||||
.attr('r', d => isInGroup(d.id) ? srActive : srDefault)
|
.attr('r', d => isInGroup(d.id) ? srActive : srDefault)
|
||||||
.attr('fill-opacity', d => isInGroup(d.id) ? 0.85 : 0.2)
|
.attr('fill-opacity', d => isInGroup(d.id) ? 0.9 : 0.35)
|
||||||
skillNodes.select('.node-label')
|
skillNodes.select('.node-label')
|
||||||
.attr('opacity', d => isInGroup(d.id) ? 1 : 0)
|
.attr('opacity', d => isInGroup(d.id) ? 1 : 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
linkSelection
|
linkSelection
|
||||||
@@ -533,7 +533,7 @@ const CareerConstellation: React.FC<CareerConstellationProps> = ({
|
|||||||
if (src === activeNodeId || tgt === activeNodeId) {
|
if (src === activeNodeId || tgt === activeNodeId) {
|
||||||
return Math.max(0.35, Math.min(0.65, l.strength * 0.55 + 0.2))
|
return Math.max(0.35, Math.min(0.65, l.strength * 0.55 + 0.2))
|
||||||
}
|
}
|
||||||
return 0.08
|
return 0.15
|
||||||
})
|
})
|
||||||
.attr('stroke-width', l => {
|
.attr('stroke-width', l => {
|
||||||
const src = typeof l.source === 'string' ? l.source : (l.source as SimNode).id
|
const src = typeof l.source === 'string' ? l.source : (l.source as SimNode).id
|
||||||
|
|||||||
+1
-1
@@ -407,7 +407,7 @@ html {
|
|||||||
/* Desktop: 2 columns */
|
/* Desktop: 2 columns */
|
||||||
@media (min-width: 1024px) {
|
@media (min-width: 1024px) {
|
||||||
.pathway-columns {
|
.pathway-columns {
|
||||||
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.5fr);
|
grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr);
|
||||||
align-items: start;
|
align-items: start;
|
||||||
gap: 22px;
|
gap: 22px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user