`;
+ a screen component must not render its own - use a ``.
+3. **`whitespace-nowrap` + `shrink-0` on fixed-height pills/badges.** Without
+ them, a longer label silently wraps onto two lines and blows out the
+ pill's height inside its flex row - easy to miss until you test with real
+ (longer) data or a narrower viewport.
+4. **Never fabricate data.** Before adding a metric, column, or button, check
+ whether the field actually exists in the real data model - the backend API
+ response, or for RUM specifically, what `static/otel-rum.js` actually
+ sends (see the header comment in `otel-ui/src/lib/rum-utils.ts` for a
+ worked example of what's real vs. invented). If a design reference shows
+ something with no backing data - the RUM mockup invented rage clicks, geo
+ location, a "frustration score", and session replay, none of which this
+ pipeline captures - drop it or replace it with the closest real,
+ honestly-labeled equivalent. Never wire up a control that looks
+ functional but isn't backed by anything.
+5. **Screenshots of a hidden Browser preview pane are unreliable** for
+ `position: fixed` elements (stale compositor frame, even after a full
+ reload). If a screenshot shows something that contradicts
+ `getComputedStyle`, trust the computed style, front the tab, and re-check
+ before concluding there's a real bug.
+
+## Migration checklist
+
+Converted to `ds-*` already: `App.tsx` (shell), `IconRail.tsx`, `TopBar.tsx`,
+`FilterBar.tsx`, `IssuesVisualizer.tsx` (Issues / triage overview, tab0),
+`RumSessionsVisualizer.tsx` (RUM sessions, tab7).
+
+Everything below still renders with the legacy MD3 dark-only palette
+(`text-on-surface`, `bg-surface-container-*`, `bg-primary-container`,
+gradient badges, the Material Symbols icon font) and needs the same
+treatment. Sizes are current line counts, biggest first:
+
+| File | Lines | Tab / role | Notes |
+|---|---|---|---|
+| `TraceVisualization.tsx` | 1866 | Traces & Spans (tab1) | Waterfall + span detail; closest existing screen to the design spec's `trace-waterfall.html` reference |
+| `MetricsVisualizer.tsx` | 1107 | Metrics & APM (tab2) | Imports `GaugeMeter.tsx` |
+| `StructuredLogTerminal.tsx` | 582 | Live Log Stream (tab3) | Uses Material Symbols icons |
+| `Tabs.tsx` (shell) | 513 | horizontal tab strip rendered above every screen | Gradient active-tab underline, slate colors; decide whether this strip is still needed now that `IconRail` is the primary nav, or whether it should just lose the gradient/slate styling |
+| `SearchAndWatcher.tsx` | 504 | Archive & Trap (tab6) | |
+| `ServiceGraph.tsx` | 495 | used by `TraceVisualization.tsx` | migrate alongside its caller |
+| `WaveformHistogram.tsx` | 385 | shown above every non-Issues/RUM tab's content | Uses Material Symbols icons; see the shared-chrome note below |
+| `GaugeMeter.tsx` | 372 | used by `MetricsVisualizer.tsx` | migrate alongside its caller |
+| `TrendsVisualizer.tsx` | 323 | Historical Trends (tab5, only when ClickHouse stats are configured) | |
+| `CommandAndQueryBar.tsx` | 251 | shown above every non-Issues/RUM tab's content | see the shared-chrome note below |
+| `TelemetryFilters.tsx` | 124 | conditionally shown above `Tabs.tsx` content | service multiselect + attribute exclusions; candidate to rebuild on top of `FilterBar.tsx` instead of restyling in place |
+| `SetupPanel.tsx` | 112 | Setup & Agent API (tab4) | |
+| `PatternsVisualizer.tsx` | 76 | Similarity Patterns (tab8) | |
+| `ViewErrorBoundary.tsx` | 62 | error fallback UI, wraps every tab's content | small, easy pass |
+| `RolloutsVisualizer.tsx` | 48 | Cluster Rollouts (tab9) | |
+| `AuthCallbackView` (inside `App.tsx`) | ~40 | OIDC callback screen | still 100% old tokens even though the rest of `App.tsx` is migrated |
+
+**Shared chrome note:** `CommandAndQueryBar.tsx` (anomaly banner, filter
+status strip, NDJSON export) and `WaveformHistogram.tsx` (volume/error
+waveform with click-to-focus) are currently skipped entirely on the Issues
+tab (`isTriageOverview` branch in `App.tsx`) because their old styling would
+clash with the new `FilterBar`. When migrating the remaining tabs, decide
+per screen whether that real functionality gets folded into the screen's own
+`FilterBar`/content area (as happened for Issues and RUM sessions), or stays
+as shared chrome above the remaining tabs, restyled to `ds-*` tokens.
+
+### Finding old-pattern usages
+
+```sh
+grep -rlE "surface-container|text-on-surface|primary-container|bg-error-container|material-symbols-outlined" otel-ui/src
+grep -rlE "from-[a-z]+-[0-9]+ via-[a-z]+-[0-9]+|getServicePalette" otel-ui/src
+```
+
+A file with zero hits and zero `ds-*` usage either has no meaningful styling
+of its own (a pure hook/context) or hasn't been looked at yet - check the
+checklist above.