Files
portfolio/.ralph/plan.md
T
admin 8f4ddc454a refactor: centralise color maps, org color fallback, and motion-safe transitions
Create src/lib/theme-colors.ts with DOT_COLORS, KPI_COLORS,
PROJECT_STATUS_COLORS, and DEFAULT_ORG_COLOR constants. Add
motionSafeTransition() utility to src/lib/utils.ts.

Removes 6 duplicate color map definitions across Card, DetailPanel,
PatientSummaryTile, KPIDetail, ProjectsTile, and ProjectDetail.
Replaces 9 hardcoded '#0D6E6E' fallbacks and 7 inline motion ternaries.
Fixes project status color inconsistency between ProjectsTile and
ProjectDetail (Ongoing was teal in tile, amber in detail).
2026-02-17 01:58:10 +00:00

6.9 KiB

Refactoring Plan — Comprehensive Codebase Refactor & Simplification

Baseline

  • Total src lines: 13,242
  • Recorded: 2026-02-17

Current Iteration: Phase 2.2

Audit complete. Three consolidation targets identified at 3+ occurrences. One data inconsistency to fix.


Phase 2.2 — Audit and consolidate repeated patterns

Change 1: Create src/lib/theme-colors.ts — centralise color maps

Why: Four files define identical/overlapping color maps. Two project status maps are inconsistent (bug).

New file: src/lib/theme-colors.ts

/** Semantic dot/accent colors used across Card, DetailPanel, KPIs */
export const DOT_COLORS = {
  teal: '#0D6E6E',
  amber: '#D97706',
  green: '#059669',
  alert: '#DC2626',
  purple: '#7C3AED',
} as const

export type DotColorName = keyof typeof DOT_COLORS

/** KPI color variants (subset of DOT_COLORS) */
export const KPI_COLORS: Record<'green' | 'amber' | 'teal', string> = {
  green: DOT_COLORS.green,
  amber: DOT_COLORS.amber,
  teal: DOT_COLORS.teal,
}

/** Project/investigation status colors */
export const PROJECT_STATUS_COLORS: Record<'Complete' | 'Ongoing' | 'Live', string> = {
  Complete: '#059669',
  Ongoing: '#D97706',
  Live: '#0D6E6E',
}

/** Default org color fallback when consultation.orgColor is undefined */
export const DEFAULT_ORG_COLOR = '#0D6E6E'

Note on project status inconsistency: ProjectsTile has Ongoing: '#0D6E6E' (teal) and Live: '#059669' (green), while ProjectDetail has Ongoing: '#D97706' (amber) and Live: '#0D6E6E' (teal). The ProjectDetail version is more semantically correct (Ongoing=amber=warning, Live=teal=active, Complete=green=success). Use the ProjectDetail mapping as canonical.

Files to update:

  1. src/components/Card.tsx (line 46-52)

    • Remove const dotColorMap (6 lines)
    • Import DOT_COLORS from @/lib/theme-colors
    • Use DOT_COLORS[dotColor] instead of dotColorMap[dotColor]
  2. src/components/DetailPanel.tsx (line 64-70)

    • Remove const dotColorValueMap (7 lines)
    • Import DOT_COLORS from @/lib/theme-colors
    • Use DOT_COLORS[dotColor] instead of dotColorValueMap[dotColor]
  3. src/components/tiles/PatientSummaryTile.tsx (line 10-14)

    • Remove const colorMap (5 lines)
    • Import KPI_COLORS from @/lib/theme-colors
    • Use KPI_COLORS[kpi.colorVariant] instead of colorMap[kpi.colorVariant]
  4. src/components/detail/KPIDetail.tsx (line 8-12)

    • Remove local colorMap (5 lines)
    • Import KPI_COLORS from @/lib/theme-colors
    • Use KPI_COLORS[kpi.colorVariant]
  5. src/components/tiles/ProjectsTile.tsx (line 7-11)

    • Remove const statusColorMap (5 lines)
    • Import PROJECT_STATUS_COLORS from @/lib/theme-colors
    • Use PROJECT_STATUS_COLORS[project.status] (fixes color values to match ProjectDetail)
  6. src/components/detail/ProjectDetail.tsx (line 8-12)

    • Remove const statusColorMap (5 lines)
    • Import PROJECT_STATUS_COLORS from @/lib/theme-colors
    • Use PROJECT_STATUS_COLORS[investigation.status]

Change 2: Add DEFAULT_ORG_COLOR to src/lib/theme-colors.ts

Why: consultation.orgColor ?? '#0D6E6E' appears 9 times across 2 files.

Files to update:

  1. src/components/WorkExperienceSubsection.tsx (lines 36, 38, 63, 81, 213, 215)

    • Import DEFAULT_ORG_COLOR from @/lib/theme-colors
    • Replace all 6 instances of consultation.orgColor ?? '#0D6E6E'consultation.orgColor ?? DEFAULT_ORG_COLOR
  2. src/components/DashboardLayout.tsx (lines 105, 106, 133)

    • Import DEFAULT_ORG_COLOR from @/lib/theme-colors
    • Replace all 3 instances of consultation.orgColor ?? '#0D6E6E'consultation.orgColor ?? DEFAULT_ORG_COLOR

Change 3: Add motionSafeTransition() to src/lib/utils.ts

Why: The pattern prefersReducedMotion ? { duration: 0 } : { duration, ease, delay } appears 7 times.

Add to src/lib/utils.ts:

/** Returns a framer-motion transition that respects prefers-reduced-motion */
export function motionSafeTransition(
  duration: number,
  ease: string = 'easeOut',
  delay: number = 0
): { duration: number; ease?: string; delay?: number } {
  if (prefersReducedMotion) return { duration: 0 }
  return { duration, ease, ...(delay ? { delay } : {}) }
}

Files to update:

  1. src/components/DashboardLayout.tsx (lines 27-29, 37-39)

    • Import motionSafeTransition from @/lib/utils
    • Replace 2 inline ternaries
  2. src/components/WorkExperienceSubsection.tsx (lines 141-143)

    • Import motionSafeTransition from @/lib/utils
    • Replace 1 inline ternary
  3. src/components/ChatWidget.tsx (lines 32-34, 45-47)

    • Import motionSafeTransition from @/lib/utils
    • Replace 2 inline ternaries
  4. src/components/TimelineInterventionsSubsection.tsx (lines 174-176)

    • Import motionSafeTransition from @/lib/utils
    • Replace 1 inline ternary
  5. src/components/constellation/MobileAccordion.tsx (line 26)

    • Import motionSafeTransition from @/lib/utils
    • Replace 1 inline ternary

What was audited and NOT extracted (with reasons)

  • Shadow rgba(26,43,42,...): 15+ occurrences but already partially covered by CSS vars; remaining inline usages are in varied contexts (D3 attributes, dynamic JS). Low ROI.
  • Breakpoint window.innerWidth < 640: Only 3 occurrences, trivially clear inline, in different execution contexts (hook, D3, component).
  • Date formatting: Only 2 occurrences.
  • Section heading styles: Slightly varied across components (letterSpacing differs, marginBottom differs).

Verification

  • npm run lint — no unused imports, no duplicate definitions
  • npm run typecheck — all imports resolve, types match
  • npm run build — clean build
  • grep -r "dotColorMap\|dotColorValueMap" src/ — zero matches (removed)
  • grep -r "statusColorMap" src/ — zero matches (removed)
  • grep -r "orgColor ?? '#0D6E6E'" src/ — zero matches (replaced with constant)
  • grep "prefersReducedMotion ? { duration: 0 }" src/ — zero matches (replaced with utility)

Overall Checklist Status

Phase 0: Dev Shortcut

  • 0.1 — Disable boot/ECG/login sequence

Phase 1: Data Consolidation

  • 1.1 — Migrate medications.ts history into skills.ts
  • 1.2 — Consolidate timeline narrative into timeline.ts
  • 1.3 — Split profile-content.ts into focused concerns
  • 1.4 — Evaluate thin re-export layers

Phase 2: Utility Extraction

  • 2.1 — Extract duplicated utility functions into lib/utils.ts
  • 2.2 — Audit and consolidate other repeated patterns

Phase 3: Component Simplification

  • 3.1 — Extract shared ExpandableCard component
  • 3.2 — Simplify detail panel components
  • 3.3 — Review large components for extraction opportunities

Phase 4: Final Cleanup

  • 4.1 — Remove dead code and unused exports
  • 4.2 — Final validation and baseline comparison
  • 4.3 — Re-enable boot/ECG/login sequence