`;
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.