Simplify theming

- Remove dependency on listening to mutations on pf-theme-provider. In
  an embedded world the UI might not have theme provider so there would
  be nothing to listen to.
- Make charts more reactive to to when CSS themes change by reading them
  out every frame. This can cause a reflow but we trigger reflows in
  plenty of other places in the UI so it's hardly an issue.
- Don't rely on custom themes, just set the theme colors using
  setOption().
diff --git a/ui/src/components/widgets/charts/bar_chart.ts b/ui/src/components/widgets/charts/bar_chart.ts
index a41a20f..97bfcad 100644
--- a/ui/src/components/widgets/charts/bar_chart.ts
+++ b/ui/src/components/widgets/charts/bar_chart.ts
@@ -15,11 +15,7 @@
 import m from 'mithril';
 import type {EChartsCoreOption} from 'echarts/core';
 import {AggregationType, extractBrushRange, formatNumber} from './chart_utils';
-import {
-  EChartView,
-  EChartEventHandler,
-  getPerfettoThemeColors,
-} from './echart_view';
+import {EChartView, EChartEventHandler} from './echart_view';
 import {
   buildAxisOption,
   buildGridOption,
@@ -154,13 +150,6 @@
     orientation = 'vertical',
   } = attrs;
   const fmtMeasure = formatMeasure ?? formatNumber;
-
-  // Only get theme for custom color overrides
-  const theme =
-    barColor === undefined || barHoverColor === undefined
-      ? getPerfettoThemeColors()
-      : undefined;
-
   const horizontal = orientation === 'horizontal';
   const labels = data.items.map((item) => String(item.label));
 
@@ -213,9 +202,7 @@
         emphasis:
           barHoverColor !== undefined
             ? {itemStyle: {color: barHoverColor}}
-            : theme !== undefined
-              ? {itemStyle: {color: theme.accentColor}}
-              : undefined,
+            : undefined,
       },
     ],
   };
@@ -230,7 +217,7 @@
     option.toolbox = {show: false};
   }
 
-  return option as EChartsCoreOption;
+  return option;
 }
 
 function buildBarEventHandlers(
diff --git a/ui/src/components/widgets/charts/chart_option_builder.ts b/ui/src/components/widgets/charts/chart_option_builder.ts
index 176d67d..b8ae7fd 100644
--- a/ui/src/components/widgets/charts/chart_option_builder.ts
+++ b/ui/src/components/widgets/charts/chart_option_builder.ts
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 import type {EChartsCoreOption} from 'echarts/core';
-import {getChartThemeColors} from './chart_theme';
 
 /**
  * Configuration for an axis in a chart.
@@ -48,35 +47,31 @@
 
 /**
  * Build an axis option from config.
- * Explicitly includes theme colors because ECharts doesn't deep merge
- * option objects with theme objects - setting axisLabel overrides the theme's
- * axisLabel entirely, so we must include colors here.
+ * Theme colors are applied by EChartView via the registered ECharts theme.
  */
 export function buildAxisOption(
   config: AxisConfig,
   isXAxis: boolean,
 ): Record<string, unknown> {
-  const theme = getChartThemeColors();
   const axis: Record<string, unknown> = {
     type: config.type,
     name: config.name,
     nameLocation: isXAxis ? ('middle' as const) : ('end' as const),
     nameGap: config.nameGap ?? (isXAxis ? 25 : 10),
-    nameTextStyle: {fontSize: 11, color: theme.textColor},
+    nameTextStyle: {fontSize: 11},
     axisLabel: {
       fontSize: 10,
-      color: theme.textColor,
       ...(config.formatter !== undefined && {formatter: config.formatter}),
       ...(config.labelOverflow !== undefined && {
         overflow: config.labelOverflow,
       }),
       ...(config.labelWidth !== undefined && {width: config.labelWidth}),
     },
-    axisTick: {lineStyle: {color: theme.borderColor}},
-    axisLine: {lineStyle: {color: theme.borderColor}},
+    axisTick: {lineStyle: {}},
+    axisLine: {lineStyle: {}},
     splitLine: {
       show: config.showSplitLine ?? false,
-      lineStyle: {color: theme.borderColor},
+      lineStyle: {},
     },
   };
 
@@ -100,7 +95,7 @@
 }
 
 /**
- * Build a themed grid option.
+ * Build a grid option.
  */
 export function buildGridOption(opts?: {
   top?: number;
@@ -120,10 +115,9 @@
 
 /**
  * Build a brush configuration.
- * Uses accent color from theme (ECharts doesn't theme brush colors).
+ * Theme colors are applied by EChartView via the registered ECharts theme.
  */
 export function buildBrushOption(config: BrushConfig): Record<string, unknown> {
-  const theme = getChartThemeColors();
   return {
     ...(config.xAxisIndex !== undefined && {xAxisIndex: config.xAxisIndex}),
     ...(config.yAxisIndex !== undefined && {yAxisIndex: config.yAxisIndex}),
@@ -132,7 +126,6 @@
     brushStyle: {
       borderWidth: 1,
       color: 'rgba(0, 0, 0, 0.1)',
-      borderColor: theme.accentColor,
     },
     throttleType: 'debounce' as const,
     throttleDelay: 100,
@@ -141,13 +134,11 @@
 
 /**
  * Build a legend option.
- * Explicitly includes theme colors because ECharts doesn't deep merge
- * option objects with theme objects.
+ * Theme colors are applied by EChartView via the registered ECharts theme.
  */
 export function buildLegendOption(
   position: 'top' | 'right' = 'top',
 ): Record<string, unknown> {
-  const theme = getChartThemeColors();
   if (position === 'right') {
     return {
       show: true,
@@ -161,7 +152,6 @@
         width: 120,
         overflow: 'truncate',
         ellipsis: '\u2026',
-        color: theme.textColor,
       },
       tooltip: {show: true},
       pageButtonPosition: 'end',
@@ -170,14 +160,14 @@
   return {
     show: true,
     top: 0,
-    textStyle: {fontSize: 10, color: theme.textColor},
+    textStyle: {fontSize: 10},
   };
 }
 
 /**
  * Build a complete base chart option with grid, axes, and optional
  * tooltip/brush/legend. Charts add their own `series` on top.
- * Theme colors are applied by ECharts theme system.
+ * Theme colors are applied by EChartView via the registered ECharts theme.
  */
 export function buildChartOption(config: {
   readonly grid?: Parameters<typeof buildGridOption>[0];
diff --git a/ui/src/components/widgets/charts/chart_theme.ts b/ui/src/components/widgets/charts/chart_theme.ts
index c76612b..065ca21 100644
--- a/ui/src/components/widgets/charts/chart_theme.ts
+++ b/ui/src/components/widgets/charts/chart_theme.ts
@@ -13,25 +13,7 @@
 // limitations under the License.
 
 /**
- * Chart theme utilities for reading Perfetto theme colors.
- *
- * DESIGN NOTES:
- *
- * 1. Colors are read from CSS variables defined in theme_provider.scss, NOT
- *    hardcoded. This ensures charts automatically adapt when theme colors
- *    are updated in SCSS.
- *
- * 2. We read from the `.pf-theme-provider` element (not document.documentElement)
- *    because that's where theme classes (.pf-theme-provider--light/--dark) are
- *    applied and where CSS variables are scoped.
- *
- * 3. ECharts doesn't automatically pick up CSS variable changes, so chart
- *    components must call getChartThemeColors() when building options and
- *    rebuild when the theme changes (see EChartView.onThemeChange).
- *
- * 4. Chart options that set sub-objects (like axisLabel: {fontSize: 10})
- *    override theme values entirely - ECharts doesn't deep merge. Therefore,
- *    chart_option_builder.ts explicitly includes theme colors in axis options.
+ * Chart theme utilities for reading Perfetto theme colors from CSS variables.
  */
 
 /**
@@ -46,23 +28,10 @@
 }
 
 /**
- * Returns true if dark theme is currently active.
+ * Returns the current theme colors by reading CSS variables from the given element.
  */
-export function isDarkTheme(): boolean {
-  const themeProvider = document.querySelector('.pf-theme-provider');
-  return themeProvider?.classList.contains('pf-theme-provider--dark') ?? false;
-}
-
-/**
- * Returns the current theme colors by reading CSS variables from the
- * theme provider element. Colors are defined in theme_provider.scss.
- */
-export function getChartThemeColors(): ChartThemeColors {
-  const themeProvider = document.querySelector('.pf-theme-provider');
-  if (themeProvider === null) {
-    throw new Error('Theme provider element not found');
-  }
-  const style = getComputedStyle(themeProvider);
+export function getChartThemeColors(element: Element): ChartThemeColors {
+  const style = getComputedStyle(element);
 
   const chartColors: string[] = [];
   for (let i = 1; i <= 8; i++) {
diff --git a/ui/src/components/widgets/charts/echart_view.ts b/ui/src/components/widgets/charts/echart_view.ts
index dda7e17..09f3ce9 100644
--- a/ui/src/components/widgets/charts/echart_view.ts
+++ b/ui/src/components/widgets/charts/echart_view.ts
@@ -15,17 +15,11 @@
 /**
  * ECharts integration for Perfetto UI.
  *
- * THEME HANDLING:
- *
- * ECharts themes are registered at initialization time by reading CSS variables
- * from the theme provider (see chart_theme.ts). When the user switches themes:
- *
- * 1. A MutationObserver detects the class change on .pf-theme-provider
- * 2. onThemeChange() re-registers ECharts themes with fresh CSS variable values
- * 3. The chart is disposed and re-initialized with the new theme
- * 4. m.redraw() triggers parent components to rebuild options with new colors
- *
- * This approach ensures charts respond to theme changes without page reload.
+ * Theme colors are read from CSS variables and used to register an ECharts
+ * theme at init time. When the theme changes (e.g., light/dark mode toggle),
+ * the chart is disposed and re-initialized with the new theme. This approach
+ * avoids mutating options (which would drop formatter functions) and ensures
+ * charts automatically respond to theme changes.
  */
 
 import m from 'mithril';
@@ -50,15 +44,9 @@
 import {classNames} from '../../../base/classnames';
 import {SimpleResizeObserver} from '../../../base/resize_observer';
 import {Spinner} from '../../../widgets/spinner';
-import {
-  isDarkTheme,
-  getChartThemeColors,
-  type ChartThemeColors,
-} from './chart_theme';
+import {getChartThemeColors, type ChartThemeColors} from './chart_theme';
 
-// Re-export for backward compatibility
-export {getChartThemeColors as getPerfettoThemeColors};
-export type {ChartThemeColors as ThemeColors};
+const PERFETTO_THEME_NAME = 'perfetto';
 
 let echartsInitialized = false;
 
@@ -79,162 +67,6 @@
     ToolboxComponent,
     CanvasRenderer,
   ]);
-  registerPerfettoThemes();
-}
-
-/**
- * Returns the ECharts theme name based on current theme.
- */
-function getCurrentThemeName(): 'perfetto-light' | 'perfetto-dark' {
-  return isDarkTheme() ? 'perfetto-dark' : 'perfetto-light';
-}
-
-/**
- * Builds an ECharts theme object by reading CSS variables.
- */
-function buildEChartsTheme(): Record<string, unknown> {
-  const theme = getChartThemeColors();
-
-  return {
-    color: theme.chartColors,
-    backgroundColor: 'transparent',
-    textStyle: {
-      color: theme.textColor,
-      fontFamily: 'inherit',
-    },
-    title: {
-      textStyle: {
-        color: theme.textColor,
-      },
-    },
-    legend: {
-      textStyle: {
-        color: theme.textColor,
-      },
-    },
-    tooltip: {
-      backgroundColor: theme.backgroundColor,
-      borderColor: theme.borderColor,
-      textStyle: {
-        color: theme.textColor,
-      },
-    },
-    axisPointer: {
-      lineStyle: {
-        color: theme.borderColor,
-      },
-      crossStyle: {
-        color: theme.borderColor,
-      },
-    },
-    xAxis: {
-      axisLine: {
-        lineStyle: {
-          color: theme.borderColor,
-        },
-      },
-      axisTick: {
-        lineStyle: {
-          color: theme.borderColor,
-        },
-      },
-      axisLabel: {
-        color: theme.textColor,
-      },
-      splitLine: {
-        lineStyle: {
-          color: theme.borderColor,
-        },
-      },
-      nameTextStyle: {
-        color: theme.textColor,
-      },
-    },
-    yAxis: {
-      axisLine: {
-        lineStyle: {
-          color: theme.borderColor,
-        },
-      },
-      axisTick: {
-        lineStyle: {
-          color: theme.borderColor,
-        },
-      },
-      axisLabel: {
-        color: theme.textColor,
-      },
-      splitLine: {
-        lineStyle: {
-          color: theme.borderColor,
-        },
-      },
-      nameTextStyle: {
-        color: theme.textColor,
-      },
-    },
-  };
-}
-
-/**
- * Registers both light and dark Perfetto themes with ECharts.
- * Called once during ECharts initialization.
- */
-function registerPerfettoThemes(): void {
-  // Register themes with current CSS variable values.
-  // Note: Theme registration happens once at init time. For dynamic theme
-  // switching, we re-initialize the chart instance with the new theme name.
-  const theme = buildEChartsTheme();
-  echarts.registerTheme('perfetto-light', theme);
-  echarts.registerTheme('perfetto-dark', theme);
-}
-
-// Global set to track all mounted EChartView instances
-const mountedCharts = new Set<EChartView>();
-
-// Single MutationObserver for all charts
-let themeObserver: MutationObserver | undefined;
-
-/**
- * Starts observing theme provider class changes to detect theme switches.
- * Only creates the observer when the first chart mounts.
- */
-function startThemeObserver(): void {
-  if (themeObserver !== undefined) return;
-
-  const themeProvider = document.querySelector('.pf-theme-provider');
-  if (themeProvider === null) return;
-
-  themeObserver = new MutationObserver((mutations) => {
-    for (const mutation of mutations) {
-      if (
-        mutation.type === 'attributes' &&
-        mutation.attributeName === 'class'
-      ) {
-        const newTheme = getCurrentThemeName();
-        // Notify all mounted charts
-        for (const chart of mountedCharts) {
-          chart.onThemeChange(newTheme);
-        }
-        break;
-      }
-    }
-  });
-
-  themeObserver.observe(themeProvider, {
-    attributes: true,
-    attributeFilter: ['class'],
-  });
-}
-
-/**
- * Stops the theme observer when no charts are mounted.
- */
-function stopThemeObserver(): void {
-  if (themeObserver !== undefined && mountedCharts.size === 0) {
-    themeObserver.disconnect();
-    themeObserver = undefined;
-  }
 }
 
 /**
@@ -311,17 +143,64 @@
 
 const DEFAULT_HEIGHT = 200;
 
+/**
+ * Build an ECharts theme object from CSS variable colors.
+ * This sets default styling for axes, text, legends, tooltips, etc.
+ */
+function buildEChartsTheme(colors: ChartThemeColors): Record<string, unknown> {
+  const {textColor, borderColor, backgroundColor, chartColors} = colors;
+  return {
+    color: chartColors,
+    backgroundColor: 'transparent',
+    textStyle: {
+      color: textColor,
+      fontFamily: 'inherit',
+    },
+    title: {textStyle: {color: textColor}},
+    legend: {textStyle: {color: textColor}},
+    tooltip: {
+      backgroundColor,
+      borderColor,
+      textStyle: {color: textColor},
+    },
+    axisPointer: {
+      lineStyle: {color: borderColor},
+      crossStyle: {color: borderColor},
+    },
+    categoryAxis: {
+      axisLabel: {color: textColor},
+      nameTextStyle: {color: textColor},
+      axisLine: {lineStyle: {color: borderColor}},
+      axisTick: {lineStyle: {color: borderColor}},
+      splitLine: {lineStyle: {color: borderColor}},
+    },
+    valueAxis: {
+      axisLabel: {color: textColor},
+      nameTextStyle: {color: textColor},
+      axisLine: {lineStyle: {color: borderColor}},
+      axisTick: {lineStyle: {color: borderColor}},
+      splitLine: {lineStyle: {color: borderColor}},
+    },
+    logAxis: {
+      axisLabel: {color: textColor},
+      nameTextStyle: {color: textColor},
+      axisLine: {lineStyle: {color: borderColor}},
+      axisTick: {lineStyle: {color: borderColor}},
+      splitLine: {lineStyle: {color: borderColor}},
+    },
+  };
+}
+
 export class EChartView implements m.ClassComponent<EChartViewAttrs> {
   private chart?: EChartsType;
   private container?: HTMLElement;
   private resizeObs?: Disposable;
   private prevHandlers: ReadonlyArray<EChartEventHandler> = [];
   private prevOptionJson?: string;
-  private currentTheme: 'perfetto-light' | 'perfetto-dark' = 'perfetto-light';
+  private prevThemeJson?: string;
 
   oncreate({dom, attrs}: m.CVnodeDOM<EChartViewAttrs>) {
     ensureEChartsSetup();
-    this.currentTheme = getCurrentThemeName();
 
     const container = dom.querySelector(
       '.pf-echart-view__canvas',
@@ -332,7 +211,7 @@
     // Only init ECharts when we have an option to render (the canvas
     // is display:none during loading, so init would get 0×0 dimensions).
     if (attrs.option !== undefined) {
-      this.initChart(attrs);
+      this.initChart(attrs, dom);
     }
 
     // Defer resize to the next frame so that a layout change caused by
@@ -340,21 +219,28 @@
     this.resizeObs = new SimpleResizeObserver(dom, () => {
       requestAnimationFrame(() => this.chart?.resize());
     });
-
-    // Register for theme changes
-    mountedCharts.add(this);
-    startThemeObserver();
   }
 
-  onupdate({attrs}: m.CVnodeDOM<EChartViewAttrs>) {
+  onupdate({attrs, dom}: m.CVnodeDOM<EChartViewAttrs>) {
     if (attrs.option === undefined) return;
 
-    // Lazy init: first option arrived after a loading state.
+    // Read theme colors from DOM
+    const themeColors = getChartThemeColors(dom);
+    const themeJson = JSON.stringify(themeColors);
+
+    // If theme changed, we need to re-init the chart with the new theme
+    if (this.chart !== undefined && themeJson !== this.prevThemeJson) {
+      this.chart.dispose();
+      this.chart = undefined;
+    }
+
+    // Lazy init: first option arrived after a loading state, or theme changed.
     if (this.chart === undefined) {
-      this.initChart(attrs);
+      this.initChart(attrs, dom, themeColors);
       return;
     }
 
+    // Update option (just stringify option, not themed version since theme is in ECharts)
     const optionJson = JSON.stringify(attrs.option);
     if (optionJson !== this.prevOptionJson) {
       this.prevOptionJson = optionJson;
@@ -367,11 +253,24 @@
     this.syncHandlers(attrs.eventHandlers ?? []);
   }
 
-  private initChart(attrs: EChartViewAttrs): void {
+  private initChart(
+    attrs: EChartViewAttrs,
+    dom: Element,
+    themeColors?: ChartThemeColors,
+  ): void {
     if (this.container === undefined || attrs.option === undefined) return;
-    this.chart = echarts.init(this.container, this.currentTheme);
+
+    // Get theme colors if not provided
+    const colors = themeColors ?? getChartThemeColors(dom);
+    const themeJson = JSON.stringify(colors);
+
+    // Register theme with ECharts (re-registering is safe, it just overwrites)
+    echarts.registerTheme(PERFETTO_THEME_NAME, buildEChartsTheme(colors));
+
+    this.chart = echarts.init(this.container, PERFETTO_THEME_NAME);
     this.chart.setOption(attrs.option);
     this.prevOptionJson = JSON.stringify(attrs.option);
+    this.prevThemeJson = themeJson;
     this.syncHandlers(attrs.eventHandlers ?? []);
     this.activateBrush(attrs.activeBrushType);
   }
@@ -389,9 +288,6 @@
   }
 
   onremove() {
-    mountedCharts.delete(this);
-    stopThemeObserver();
-
     if (this.resizeObs) {
       this.resizeObs[Symbol.dispose]();
       this.resizeObs = undefined;
@@ -403,31 +299,6 @@
     }
   }
 
-  /**
-   * Called when the document theme changes.
-   * Re-registers ECharts themes with new CSS values and reinitializes charts.
-   */
-  onThemeChange(newTheme: 'perfetto-light' | 'perfetto-dark'): void {
-    if (this.currentTheme === newTheme) return;
-    this.currentTheme = newTheme;
-
-    // Re-register themes with updated CSS variable values
-    const theme = buildEChartsTheme();
-    echarts.registerTheme('perfetto-light', theme);
-    echarts.registerTheme('perfetto-dark', theme);
-
-    // Re-initialize chart with new theme
-    if (this.chart !== undefined && this.container !== undefined) {
-      const currentOption = this.chart.getOption();
-      this.chart.dispose();
-      this.chart = echarts.init(this.container, newTheme);
-      this.chart.setOption(currentOption, {notMerge: true});
-      this.syncHandlers(this.prevHandlers);
-      this.chart.resize();
-      m.redraw();
-    }
-  }
-
   view({attrs}: m.Vnode<EChartViewAttrs>) {
     const height = attrs.height ?? DEFAULT_HEIGHT;
     const isLoading = attrs.option === undefined && !attrs.empty;
diff --git a/ui/src/components/widgets/charts/histogram.ts b/ui/src/components/widgets/charts/histogram.ts
index 9b98f01..ddbda62 100644
--- a/ui/src/components/widgets/charts/histogram.ts
+++ b/ui/src/components/widgets/charts/histogram.ts
@@ -21,11 +21,7 @@
   HistogramConfig,
   computeHistogram,
 } from './histogram_loader';
-import {
-  EChartView,
-  EChartEventHandler,
-  getPerfettoThemeColors,
-} from './echart_view';
+import {EChartView, EChartEventHandler} from './echart_view';
 import {buildChartOption} from './chart_option_builder';
 
 // Re-export data types for convenience
@@ -138,12 +134,6 @@
     logScale = false,
   } = attrs;
   const fmtY = formatYValue ?? formatNumber;
-
-  // Only get theme for custom color overrides
-  const theme =
-    barColor === undefined || barHoverColor === undefined
-      ? getPerfettoThemeColors()
-      : undefined;
   const categories = data.buckets.map((b) => formatXValue(b.start));
 
   const option = buildChartOption({
@@ -197,9 +187,7 @@
       emphasis:
         barHoverColor !== undefined
           ? {itemStyle: {color: barHoverColor}}
-          : theme !== undefined
-            ? {itemStyle: {color: theme.accentColor}}
-            : undefined,
+          : undefined,
     },
   ];
 
diff --git a/ui/src/components/widgets/charts/pie_chart.ts b/ui/src/components/widgets/charts/pie_chart.ts
index 1f3bcd8..ca123bb 100644
--- a/ui/src/components/widgets/charts/pie_chart.ts
+++ b/ui/src/components/widgets/charts/pie_chart.ts
@@ -15,12 +15,7 @@
 import m from 'mithril';
 import type {EChartsCoreOption} from 'echarts/core';
 import {formatNumber} from './chart_utils';
-import {
-  EChartView,
-  EChartEventHandler,
-  EChartClickParams,
-  getPerfettoThemeColors,
-} from './echart_view';
+import {EChartView, EChartEventHandler, EChartClickParams} from './echart_view';
 import {buildLegendOption} from './chart_option_builder';
 
 /**
@@ -123,9 +118,6 @@
     innerRadiusRatio = 0,
   } = attrs;
 
-  // Only get theme for border color (not themed by ECharts)
-  const theme = getPerfettoThemeColors();
-
   const pieData = slices.map((s) => ({
     name: s.label,
     value: s.value,
@@ -166,10 +158,6 @@
         emphasis: {
           scaleSize: 5,
         },
-        itemStyle: {
-          borderColor: theme.backgroundColor,
-          borderWidth: 2,
-        },
       },
     ],
   };
diff --git a/ui/src/components/widgets/charts/scatter_chart.ts b/ui/src/components/widgets/charts/scatter_chart.ts
index d1ad227..c47de82 100644
--- a/ui/src/components/widgets/charts/scatter_chart.ts
+++ b/ui/src/components/widgets/charts/scatter_chart.ts
@@ -15,11 +15,7 @@
 import m from 'mithril';
 import type {EChartsCoreOption} from 'echarts/core';
 import {extractBrushRange, formatNumber} from './chart_utils';
-import {
-  EChartView,
-  EChartEventHandler,
-  getPerfettoThemeColors,
-} from './echart_view';
+import {EChartView, EChartEventHandler} from './echart_view';
 import {buildChartOption, buildLegendOption} from './chart_option_builder';
 
 /**
@@ -182,9 +178,6 @@
   } = attrs;
   const fmtX = formatXValue ?? formatNumber;
   const fmtY = formatYValue ?? formatNumber;
-
-  // Only get theme for emphasis border color (not themed by ECharts)
-  const theme = getPerfettoThemeColors();
   const displayLegend = showLegend ?? data.series.length > 1;
 
   // Compute size range for normalization if any points have sizes
@@ -238,7 +231,7 @@
         : symbolSize,
       itemStyle: s.color !== undefined ? {color: s.color} : undefined,
       emphasis: {
-        itemStyle: {borderWidth: 2, borderColor: theme.backgroundColor},
+        itemStyle: {borderWidth: 2},
       },
     };
   });
diff --git a/ui/src/components/widgets/charts/treemap_chart.ts b/ui/src/components/widgets/charts/treemap_chart.ts
index fdfd68b..5274bfb 100644
--- a/ui/src/components/widgets/charts/treemap_chart.ts
+++ b/ui/src/components/widgets/charts/treemap_chart.ts
@@ -15,12 +15,7 @@
 import m from 'mithril';
 import type {EChartsCoreOption} from 'echarts/core';
 import {formatNumber} from './chart_utils';
-import {
-  EChartView,
-  EChartEventHandler,
-  EChartClickParams,
-  getPerfettoThemeColors,
-} from './echart_view';
+import {EChartView, EChartEventHandler, EChartClickParams} from './echart_view';
 
 /**
  * A node in the treemap hierarchy.
@@ -126,12 +121,6 @@
     enableDrillDown = false,
   } = attrs;
 
-  const theme = getPerfettoThemeColors();
-
-  // Build category-to-color mapping
-  const categoryColors = new Map<string, string>();
-  assignColors(data.nodes, categoryColors, theme.chartColors);
-
   const total = data.nodes.reduce((sum, n) => sum + computeTotal(n), 0);
 
   return {
@@ -148,7 +137,7 @@
     series: [
       {
         type: 'treemap',
-        data: convertNodes(data.nodes, categoryColors),
+        data: convertNodes(data.nodes),
         roam: enableDrillDown ? 'move' : false,
         nodeClick: enableDrillDown ? 'zoomToNode' : false,
         visibleMin,
@@ -156,33 +145,22 @@
           show: showLabels,
           formatter: '{b}',
           fontSize: 11,
-          color: theme.textColor,
         },
         itemStyle: {
-          borderColor: theme.backgroundColor,
           borderWidth: 2,
           gapWidth: 2,
         },
-        breadcrumb: enableDrillDown
-          ? {
-              show: true,
-              itemStyle: {
-                textStyle: {color: theme.textColor},
-              },
-            }
-          : {show: false},
+        breadcrumb: enableDrillDown ? {show: true} : {show: false},
         levels: [
           {
             // Level 0: parent groups
             itemStyle: {
-              borderColor: theme.borderColor,
               borderWidth: 3,
               gapWidth: 3,
             },
             upperLabel: {
               show: true,
               height: 20,
-              color: theme.textColor,
               fontSize: 12,
               fontWeight: 'bold' as const,
             },
@@ -191,7 +169,6 @@
             // Level 1: children
             colorSaturation: [0.35, 0.65],
             itemStyle: {
-              borderColor: theme.backgroundColor,
               borderWidth: 1,
               gapWidth: 1,
             },
@@ -200,7 +177,6 @@
             // Level 2+: deeper children (if any)
             colorSaturation: [0.25, 0.55],
             itemStyle: {
-              borderColor: theme.backgroundColor,
               borderWidth: 1,
               gapWidth: 1,
             },
@@ -212,37 +188,12 @@
 }
 
 /**
- * Recursively assign colors to categories found in nodes.
- */
-function assignColors(
-  nodes: readonly TreemapNode[],
-  categoryColors: Map<string, string>,
-  chartColors: readonly string[],
-): void {
-  for (const node of nodes) {
-    const category = node.category ?? node.name;
-    if (!categoryColors.has(category)) {
-      categoryColors.set(
-        category,
-        chartColors[categoryColors.size % chartColors.length],
-      );
-    }
-    if (node.children !== undefined) {
-      assignColors(node.children, categoryColors, chartColors);
-    }
-  }
-}
-
-/**
  * Convert TreemapNode tree to ECharts data format.
  */
 function convertNodes(
   nodes: readonly TreemapNode[],
-  categoryColors: Map<string, string>,
 ): Array<Record<string, unknown>> {
   return nodes.map((node) => {
-    const category = node.category ?? node.name;
-    const color = categoryColors.get(category);
     const children = node.children;
     const hasChildren = children !== undefined && children.length > 0;
     // For nodes with children, use computed value from children if value is 0
@@ -251,10 +202,9 @@
     const result: Record<string, unknown> = {
       name: node.name,
       value,
-      itemStyle: {color},
     };
     if (hasChildren) {
-      result.children = convertNodes(children, categoryColors);
+      result.children = convertNodes(children);
     }
     return result;
   });