feat(ui): D.6b UnavailableMeasurementCard → EmptyState - #77
Conversation
PR 10 (sub-step b) of 16. Second of three small PRs against MeasurementDashboard.tsx — folds the local UnavailableMeasurementCard into the EmptyState primitive while preserving its (title, description, detail) call-site API. Changes: - UnavailableMeasurementCard's body now delegates to <EmptyState tone="neutral" padding="md" ...> with title + description passed through. The optional `detail` line renders as a children slot (EmptyState's first-class props stop at description; children is the documented escape hatch). - The dashed-border + bg-bg-surface-dark visual treatment carries over via className override on EmptyState. - The two call sites at :1805 and :1858 are untouched — they continue to call <UnavailableMeasurementCard title=... description=... detail=...> exactly as before. Deviation from plan §D.6b: the plan flagged `bg-blue-500` at :438 as drift to fix → bg-accent. Inspecting the context, that class is one of three colors (bg-red-500 kick / bg-yellow-500 mid / bg-blue-500 high) on a "Beat Dominance" 3-band content visualization stack — the same data-coded vocabulary the plan explicitly protects for spectral content colors (:99-134) and LUFS_METER_GRADIENT. Changing only the blue band to accent would also break the visual coherence of the 3-band stack (warm/neutral/cool). Leaving all three Beat Dominance colors as data-coding to match the protection spirit. The plan's note was likely from a stale grep against a moved/removed call site. Preserved verbatim: - UnavailableMeasurementCard's external API (title, description, detail) so call sites don't change. - The Beat Dominance band-color visualization at :428/:433/:438 (red/yellow/blue) — treated as data, not chrome. - All spectral content colors at :99-134. - LUFS_METER_GRADIENT. - All data-testid attributes inside the dashboard. Verified: lint clean, 628/628 unit tests passing, build clean. Next: D.6c — migrate the local SectionHeader at :288-303 to the SectionHeader primitive. https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
slittycode
left a comment
There was a problem hiding this comment.
Verdict: COMMENT
Summary
Pure UI refactor — UnavailableMeasurementCard now delegates its rendering to the EmptyState primitive while keeping its (title, description, detail) call-site API intact. No Phase 1/2/3 logic is touched. Tests pass (645/649, 4 pre-existing skips). The deviation decision on Beat Dominance colors is sound. One issue worth flagging before merge.
Findings
Should fix: EmptyState's base classes introduce silent visual changes the PR description doesn't account for
The PR says "the dashed-border + bg-bg-surface-dark visual treatment carries over via className override." The background and border-color do carry over — cn() + tailwind-merge handles those correctly. But EmptyState's CVA base string is flex flex-col items-center justify-center gap-2 rounded-sm border text-center, which introduces changes the className override does not and cannot suppress:
- Text alignment:
text-centerforced by the base. The original rendered left-aligned text. For dashboard "unavailable" cards this is a visible layout shift. - Layout model:
flex flex-col items-center justify-centervs the originalblock div. Content is now vertically centered in the card, not top-anchored. - Title: EmptyState hardcodes
text-[11px] text-text-primary(MeasurementDashboard.tsx:47). Original wastext-[10px] text-text-secondary— 1px larger and a color step up toward primary. - Description: EmptyState drops the
tracking-[0.14em]and opacity modifier (text-text-secondary/80→text-text-secondary) and addsleading-snug max-w-prose. - Spacing:
space-y-3(12px) →gap-2(8px).
If these changes are intentional "leveling up" to the canonical primitive — fine, that's a reasonable restyle decision. But the PR description presents this as a "preserved verbatim" visual treatment and doesn't mention any of it, which will make the next person reading git blame confused about why the card looks different.
Either: (a) explicitly note the changes in the PR description as intentional visual alignment, or (b) add className overrides to suppress the EmptyState defaults that don't match the original (particularly text-center and the title's text-text-primary).
Test results
645 passed / 4 skipped (pre-existing). No failures.
Phase boundary check
Clean. No Phase 1, 2, or 3 code touched.
Generated by Claude Code
PR 14 of 16 in the UI consistency overhaul. Final cleanup pass. Removes three CSS utility classes from index.css: - .ableton-panel - .ableton-device - .ableton-header All three were retired by D.* migrations — every former consumer now uses the canonical primitives from components/ui/ (Panel, DeviceRack, SectionHeader). `grep -rn` against apps/ui/src/ confirms zero remaining usages. .ableton-shell and .ableton-toolbar STAY because App.tsx:952,956 still consumes them for the outer app chrome (the load-bearing section-grid shell). A focused follow-up could migrate those two to DeviceRack / a future AppShell primitive; out of scope here. Deferred (preserved as-is): - components/MeasurementPrimitives.tsx cannot be fully deleted yet — 5 consumers still depend on timeline/lane primitives (LaneContainer, LaneRow, StatsBar, TimeRuler) that the plan explicitly preserved. The shipped primitives (StatusBadge, DeltaBadge, MetricBar, MetricBarRow, OutlinePillButton, TokenBadgeList, StyledDataTable, AccentMetricCard) also still have consumers in MeasurementDashboard and AnalysisResults sections that weren't migrated (StructureLanes, HarmonyLanes, AnalysisResults remaining sections). Keep the file intact. - components/Tooltip.tsx stays as a re-export shim; the file isn't causing any drift and external imports may still target it. Verified: lint clean, 628/628 unit tests passing, build clean. D.* migrations summary (all merged or open at time of this PR): - D.0 Foundation (#64) ✓ - D.1 Primitives complete (#65) ✓ - D.2 SamplePlayback (#66) ✓ - D.3 SignalChain marquee (#67) ✓ - D.4 FileUpload + Input Source (#69) ✓ - D.5a Results header + metric strip (#71) ✓ - D.5b Mix Chain (#73) ✓ - D.5c Patches (#74) ✓ - D.5d Section headers (#75) ✓ - D.6a Dashboard structure (#76) ✓ - D.6b Dashboard sections (#77) ✓ - D.6c Dashboard headers (#78) ✓ - D.7 SessionMusicianPanel + MixDoctorPanel (#79) — open - D.8 Signal Monitor + SpectrogramViewer drift (#80) — open - D.9 DenseDawConcept drift (#81) — open - D.10 Cleanup (this PR) ✓ https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
…82) PR 14 of 16 in the UI consistency overhaul. Final cleanup pass. Removes three CSS utility classes from index.css: - .ableton-panel - .ableton-device - .ableton-header All three were retired by D.* migrations — every former consumer now uses the canonical primitives from components/ui/ (Panel, DeviceRack, SectionHeader). `grep -rn` against apps/ui/src/ confirms zero remaining usages. .ableton-shell and .ableton-toolbar STAY because App.tsx:952,956 still consumes them for the outer app chrome (the load-bearing section-grid shell). A focused follow-up could migrate those two to DeviceRack / a future AppShell primitive; out of scope here. Deferred (preserved as-is): - components/MeasurementPrimitives.tsx cannot be fully deleted yet — 5 consumers still depend on timeline/lane primitives (LaneContainer, LaneRow, StatsBar, TimeRuler) that the plan explicitly preserved. The shipped primitives (StatusBadge, DeltaBadge, MetricBar, MetricBarRow, OutlinePillButton, TokenBadgeList, StyledDataTable, AccentMetricCard) also still have consumers in MeasurementDashboard and AnalysisResults sections that weren't migrated (StructureLanes, HarmonyLanes, AnalysisResults remaining sections). Keep the file intact. - components/Tooltip.tsx stays as a re-export shim; the file isn't causing any drift and external imports may still target it. Verified: lint clean, 628/628 unit tests passing, build clean. D.* migrations summary (all merged or open at time of this PR): - D.0 Foundation (#64) ✓ - D.1 Primitives complete (#65) ✓ - D.2 SamplePlayback (#66) ✓ - D.3 SignalChain marquee (#67) ✓ - D.4 FileUpload + Input Source (#69) ✓ - D.5a Results header + metric strip (#71) ✓ - D.5b Mix Chain (#73) ✓ - D.5c Patches (#74) ✓ - D.5d Section headers (#75) ✓ - D.6a Dashboard structure (#76) ✓ - D.6b Dashboard sections (#77) ✓ - D.6c Dashboard headers (#78) ✓ - D.7 SessionMusicianPanel + MixDoctorPanel (#79) — open - D.8 Signal Monitor + SpectrogramViewer drift (#80) — open - D.9 DenseDawConcept drift (#81) — open - D.10 Cleanup (this PR) ✓ https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe Co-authored-by: Claude <noreply@anthropic.com>
Summary
PR 10 (sub-step b) of 16. Second of three small PRs against
MeasurementDashboard.tsx— folds the localUnavailableMeasurementCardinto theEmptyStateprimitive while preserving its(title, description, detail)call-site API.Changes
UnavailableMeasurementCard's body now delegates to<EmptyState tone="neutral" padding="md" ...>withtitle+descriptionpassed through. The optionaldetailline renders as a children slot (EmptyState's first-class props stop atdescription; children is the documented escape hatch).bg-bg-surface-darkvisual treatment carries over viaclassNameoverride on EmptyState.:1805and:1858are untouched — they continue to call<UnavailableMeasurementCard title=... description=... detail=...>exactly as before.Deviation from plan §D.6b
The plan flagged
bg-blue-500at:438as drift to fix →bg-accent. Inspecting the context, that class is one of three colors (bg-red-500kick /bg-yellow-500mid /bg-blue-500high) on a "Beat Dominance" 3-band content visualization stack — the same data-coded vocabulary the plan explicitly protects for spectral content colors (:99-134) andLUFS_METER_GRADIENT. Changing only the blue band to accent would also break the visual coherence of the 3-band stack (warm/neutral/cool). Leaving all three Beat Dominance colors as data-coding to match the protection spirit. The plan's note was likely from a stale grep against a moved/removed call site.Preserved verbatim
UnavailableMeasurementCard's external API (title,description,detail) so call sites don't change.:428/:433/:438(red/yellow/blue) — treated as data, not chrome.:99-134.LUFS_METER_GRADIENT.data-testidattributes inside the dashboard.Test plan
npm run lint— cleannpm run test:unit— 628/628 passingnpm run build— cleannpm run test:smoke— sandbox blocks Playwright browser; call-site API + data-testids preservedUp next
D.6c — migrate the local
SectionHeaderat:288-303to theSectionHeaderprimitive.https://claude.ai/code/session_01WNtYcWXwn4mVnnjxqT7uAe
Generated by Claude Code