diff --git a/.loki/reference/chrome_laptop_Visualizations_shared_RowChart_Default.png b/.loki/reference/chrome_laptop_Visualizations_shared_RowChart_Default.png
new file mode 100644
index 0000000000000000000000000000000000000000..e72364925bb137208fc5cf84660e055e1fdff75c
Binary files /dev/null and b/.loki/reference/chrome_laptop_Visualizations_shared_RowChart_Default.png differ
diff --git a/.loki/reference/chrome_laptop_Visualizations_shared_RowChart_Huge_Font.png b/.loki/reference/chrome_laptop_Visualizations_shared_RowChart_Huge_Font.png
new file mode 100644
index 0000000000000000000000000000000000000000..77d1f501970fdd7fcd7c4d1fb4f606e6666de397
Binary files /dev/null and b/.loki/reference/chrome_laptop_Visualizations_shared_RowChart_Huge_Font.png differ
diff --git a/.loki/reference/chrome_laptop_viz_BarChart_Default.png b/.loki/reference/chrome_laptop_viz_BarChart_Default.png
new file mode 100644
index 0000000000000000000000000000000000000000..2310a1d2c8647c13b8caf3fb8080b50f0307b128
Binary files /dev/null and b/.loki/reference/chrome_laptop_viz_BarChart_Default.png differ
diff --git a/.loki/reference/chrome_laptop_viz_BarChart_Embedding_Huge_Font.png b/.loki/reference/chrome_laptop_viz_BarChart_Embedding_Huge_Font.png
new file mode 100644
index 0000000000000000000000000000000000000000..dbb039e45a4617176ebf5d12f204ff9e02ec35db
Binary files /dev/null and b/.loki/reference/chrome_laptop_viz_BarChart_Embedding_Huge_Font.png differ
diff --git a/.loki/reference/chrome_laptop_viz_LineChart_Default.png b/.loki/reference/chrome_laptop_viz_LineChart_Default.png
new file mode 100644
index 0000000000000000000000000000000000000000..4d3030b04f80cd3f1fdd96d57c7287896f1cea1b
Binary files /dev/null and b/.loki/reference/chrome_laptop_viz_LineChart_Default.png differ
diff --git a/.loki/reference/chrome_laptop_viz_LineChart_Embedding_Huge_Font.png b/.loki/reference/chrome_laptop_viz_LineChart_Embedding_Huge_Font.png
new file mode 100644
index 0000000000000000000000000000000000000000..14b197ab2b87b64bd1a0aaa417f41c5bd90814f0
Binary files /dev/null and b/.loki/reference/chrome_laptop_viz_LineChart_Embedding_Huge_Font.png differ
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/SdkContentWrapper.tsx b/enterprise/frontend/src/embedding-sdk/components/private/SdkContentWrapper.tsx
index 809c574a869af9220ac18e79262e93989862e8e6..26b84241360081b3624d3cd4474c3144febd7b9b 100644
--- a/enterprise/frontend/src/embedding-sdk/components/private/SdkContentWrapper.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/private/SdkContentWrapper.tsx
@@ -52,7 +52,7 @@ const SdkContentWrapperInner = styled.div<
   --mb-color-text-medium: ${({ theme }) => theme.fn.themeColor("text-medium")};
   --mb-color-text-light: ${({ theme }) => theme.fn.themeColor("text-light")};
 
-  font-size: ${({ theme }) => theme.other.fontSize ?? "0.875em"};
+  font-size: ${({ theme }) => theme.other.fontSize};
 
   ${aceEditorStyles}
   ${saveDomImageStyles}
diff --git a/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts b/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts
index 722effc2efd35d329e0f41fa2a0a007cb80c774d..09a6b9f5d262af678d6bdb727eb3ec45288aa376 100644
--- a/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts
+++ b/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts
@@ -1,8 +1,23 @@
-import type { MantineThemeOverride } from "@mantine/core";
 import { merge } from "icepick";
 
 import type { MetabaseComponentTheme } from "embedding-sdk";
 import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
+import type { MantineThemeOverride } from "metabase/ui";
+
+export const DEFAULT_SDK_FONT_SIZE = 14;
+
+// Use em units to scale font sizes relative to the base font size.
+// The em unit is used by default in the embedding SDK.
+const units = (px: number) => ({
+  px: `${px}px`,
+  em: `${px / DEFAULT_SDK_FONT_SIZE}em`,
+});
+
+export const FONT_SIZES = {
+  tableCell: units(12.5),
+  label: units(12),
+  goalLabel: units(14),
+};
 
 /**
  * Default theme options for Metabase components.
@@ -14,12 +29,13 @@ import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
  */
 export const DEFAULT_METABASE_COMPONENT_THEME: MetabaseComponentTheme = {
   table: {
-    idColumn: {
-      textColor: "brand",
-    },
     cell: {
+      fontSize: FONT_SIZES.tableCell.px,
       textColor: "text-dark",
     },
+    idColumn: {
+      textColor: "brand",
+    },
   },
   pivotTable: {
     rowToggle: {
@@ -27,6 +43,12 @@ export const DEFAULT_METABASE_COMPONENT_THEME: MetabaseComponentTheme = {
       backgroundColor: "text-light",
     },
   },
+  cartesian: {
+    label: { fontSize: FONT_SIZES.label.px },
+    goalLine: {
+      label: { fontSize: FONT_SIZES.goalLabel.px },
+    },
+  },
 };
 
 /**
@@ -38,9 +60,16 @@ export const DEFAULT_EMBEDDED_COMPONENT_THEME: MetabaseComponentTheme = merge(
   {
     table: {
       cell: {
+        fontSize: FONT_SIZES.tableCell.em,
         backgroundColor: "bg-white",
       },
     },
+    cartesian: {
+      label: { fontSize: FONT_SIZES.label.em },
+      goalLine: {
+        label: { fontSize: FONT_SIZES.goalLabel.em },
+      },
+    },
   },
 );
 
diff --git a/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts b/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts
index d17890ba7745ef164414811220e29de88cd4241f..03ddde8bce5fc678e42a4a2b7f7080c739e086b4 100644
--- a/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts
+++ b/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts
@@ -12,6 +12,7 @@ import type {
 import { colorTuple } from "./color-tuple";
 import {
   DEFAULT_EMBEDDED_COMPONENT_THEME,
+  DEFAULT_SDK_FONT_SIZE,
   EMBEDDING_SDK_COMPONENTS_OVERRIDES,
 } from "./default-component-theme";
 import type { MappableSdkColor } from "./embedding-color-palette";
@@ -20,6 +21,8 @@ import { SDK_TO_MAIN_APP_COLORS_MAPPING } from "./embedding-color-palette";
 const getFontFamily = (theme: MetabaseTheme) =>
   theme.fontFamily ?? DEFAULT_FONT;
 
+const SDK_BASE_FONT_SIZE = `${DEFAULT_SDK_FONT_SIZE / 16}em`;
+
 /**
  * Transforms a public-facing Metabase theme configuration
  * into a Mantine theme override for internal use.
@@ -39,7 +42,7 @@ export function getEmbeddingThemeOverride(
 
     other: {
       ...components,
-      ...(theme.fontSize && { fontSize: theme.fontSize }),
+      fontSize: theme.fontSize ?? SDK_BASE_FONT_SIZE,
     },
 
     components: EMBEDDING_SDK_COMPONENTS_OVERRIDES,
diff --git a/enterprise/frontend/src/embedding-sdk/types/theme/index.ts b/enterprise/frontend/src/embedding-sdk/types/theme/index.ts
index 90247245632578e4e5aba034de45bf821db5cb1c..1a8531316c1787eb945d76a63cae6ce8f1563b78 100644
--- a/enterprise/frontend/src/embedding-sdk/types/theme/index.ts
+++ b/enterprise/frontend/src/embedding-sdk/types/theme/index.ts
@@ -5,7 +5,11 @@ import type { DeepPartial } from "../utils";
  * Theme configuration for embedded Metabase components.
  */
 export interface MetabaseTheme {
-  /** Base font size */
+  /**
+   * Base font size.
+   * Supported units are px, em and rem.
+   * Defaults to ~14px (0.875em)
+   **/
   fontSize?: string;
 
   /**
@@ -83,6 +87,9 @@ export interface MetabaseComponentTheme {
 
       /** Default background color of cells, defaults to `background` */
       backgroundColor?: string;
+
+      /** Font size of cell values, defaults to ~12.5px */
+      fontSize: string;
     };
 
     idColumn?: {
@@ -111,6 +118,21 @@ export interface MetabaseComponentTheme {
       lineHeight?: string;
     };
   };
+
+  /** Cartesian charts */
+  cartesian: {
+    label: {
+      /** Labels used in cartesian charts, such as axis ticks and series. */
+      fontSize: string;
+    };
+
+    goalLine: {
+      label: {
+        /** Font size of goal line labels */
+        fontSize: string;
+      };
+    };
+  };
 }
 
 export type ChartColor =
diff --git a/enterprise/frontend/src/embedding-sdk/types/theme/private.ts b/enterprise/frontend/src/embedding-sdk/types/theme/private.ts
index 06d2a077d5e2fba87511fa957a98b60bbc1219f9..a2cd0867f92d7e6f68561771642f0dadf1413f4c 100644
--- a/enterprise/frontend/src/embedding-sdk/types/theme/private.ts
+++ b/enterprise/frontend/src/embedding-sdk/types/theme/private.ts
@@ -1,9 +1,7 @@
-import type { MetabaseComponentTheme } from ".";
+import type { MetabaseComponentTheme, MetabaseTheme } from ".";
 
 /**
  * Mantine theme options specific to React embedding.
  */
-export type EmbeddingThemeOptions = MetabaseComponentTheme & {
-  /** Base font size */
-  fontSize?: string;
-};
+export type EmbeddingThemeOptions = MetabaseComponentTheme &
+  Pick<MetabaseTheme, "fontSize">;
diff --git a/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx b/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx
index 001c734fb384ebdd2e54f58a3a8c60898e243b93..0c95be526ef2114d39a17e0ae3091240e3fc5adf 100644
--- a/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx
@@ -3,6 +3,7 @@ import type { ComponentStory } from "@storybook/react";
 import { color } from "metabase/lib/colors";
 import { formatStaticValue } from "metabase/static-viz/lib/format";
 import { measureTextWidth } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type { RenderingContext } from "metabase/visualizations/types";
 
 import { ComboChart } from "./ComboChart";
@@ -27,6 +28,7 @@ const renderingContext: RenderingContext = {
   measureText: (text, style) =>
     measureTextWidth(text, Number(style.size), Number(style.weight)),
   fontFamily: "Lato",
+  theme: DEFAULT_VISUALIZATION_THEME,
 };
 
 export const LineLinearXScale = Template.bind({});
diff --git a/frontend/src/metabase/static-viz/components/FunnelBarChart/FunnelBarChart.stories.tsx b/frontend/src/metabase/static-viz/components/FunnelBarChart/FunnelBarChart.stories.tsx
index e1edf76e700258ec985e259dc3f0f6af6d8303a3..c2857a3669fdc60ee9edf84c82f750d6b0b4fe44 100644
--- a/frontend/src/metabase/static-viz/components/FunnelBarChart/FunnelBarChart.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/FunnelBarChart/FunnelBarChart.stories.tsx
@@ -3,6 +3,7 @@ import type { ComponentStory } from "@storybook/react";
 import { color } from "metabase/lib/colors";
 import { formatStaticValue } from "metabase/static-viz/lib/format";
 import { measureTextWidth } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type { RenderingContext } from "metabase/visualizations/types";
 
 import { FunnelBarChart } from "./FunnelBarChart";
@@ -27,6 +28,7 @@ const renderingContext: RenderingContext = {
   measureText: (text, style) =>
     measureTextWidth(text, Number(style.size), Number(style.weight)),
   fontFamily: "Lato",
+  theme: DEFAULT_VISUALIZATION_THEME,
 };
 
 export const Default = Template.bind({});
diff --git a/frontend/src/metabase/static-viz/components/ScalarChart/ScalarChart.stories.tsx b/frontend/src/metabase/static-viz/components/ScalarChart/ScalarChart.stories.tsx
index bb522f1c80aeb8ed48aab56db8c1dfdacf2f5066..59a9576f65e8d20b05487bb14bc6762d598b7b39 100644
--- a/frontend/src/metabase/static-viz/components/ScalarChart/ScalarChart.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/ScalarChart/ScalarChart.stories.tsx
@@ -3,6 +3,7 @@ import type { ComponentStory } from "@storybook/react";
 import { color } from "metabase/lib/colors";
 import { formatStaticValue } from "metabase/static-viz/lib/format";
 import { measureTextWidth } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type { RenderingContext } from "metabase/visualizations/types";
 
 import { ScalarChart } from "./ScalarChart";
@@ -27,6 +28,7 @@ const renderingContext: RenderingContext = {
   measureText: (text, style) =>
     measureTextWidth(text, Number(style.size), Number(style.weight)),
   fontFamily: "Lato",
+  theme: DEFAULT_VISUALIZATION_THEME,
 };
 
 export const Default = Template.bind({});
diff --git a/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.stories.tsx b/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.stories.tsx
index 81e22799b1aa4cb4dc821bb7b81fb0ca76abd231..979acb3783c91edad4be8940162c306e69e4025b 100644
--- a/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.stories.tsx
@@ -3,6 +3,7 @@ import type { ComponentStory } from "@storybook/react";
 import { color } from "metabase/lib/colors";
 import { formatStaticValue } from "metabase/static-viz/lib/format";
 import { measureTextWidth } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type { RenderingContext } from "metabase/visualizations/types";
 
 import { ScatterPlot } from "./ScatterPlot";
@@ -27,6 +28,7 @@ const renderingContext: RenderingContext = {
   measureText: (text, style) =>
     measureTextWidth(text, Number(style.size), Number(style.weight)),
   fontFamily: "Lato",
+  theme: DEFAULT_VISUALIZATION_THEME,
 };
 
 export const Default = Template.bind({});
diff --git a/frontend/src/metabase/static-viz/components/SmartScalar/SmartScalar.stories.tsx b/frontend/src/metabase/static-viz/components/SmartScalar/SmartScalar.stories.tsx
index be53b2d24c685fcd465f557514d47b0da899df82..10d6edb6cb2078785d94ebb543fb62e1bf5eae96 100644
--- a/frontend/src/metabase/static-viz/components/SmartScalar/SmartScalar.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/SmartScalar/SmartScalar.stories.tsx
@@ -2,6 +2,7 @@ import { colors } from "metabase/lib/colors";
 import { createColorGetter } from "metabase/static-viz/lib/colors";
 import { formatStaticValue } from "metabase/static-viz/lib/format";
 import { measureTextWidth } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type { RowValues, VisualizationSettings } from "metabase-types/api";
 import {
   createMockColumn,
@@ -136,6 +137,7 @@ const createTemplate = ({ rows, vizSettings }: SmartScalarSeriesOpts) =>
           getColor: createColorGetter(colors),
           measureText: (text, style) =>
             measureTextWidth(text, Number(style.size), Number(style.weight)),
+          theme: DEFAULT_VISUALIZATION_THEME,
         }}
       />
     );
diff --git a/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.stories.tsx b/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.stories.tsx
index daf26ee33d94afa380dc7f4bcbb8278a04a743d8..e7e5201340f2181644d2794daf50dca17554280d 100644
--- a/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.stories.tsx
@@ -4,6 +4,7 @@ import { color } from "metabase/lib/colors";
 import { data } from "metabase/static-viz/components/WaterfallChart/stories-data";
 import { formatStaticValue } from "metabase/static-viz/lib/format";
 import { measureTextWidth } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type { RenderingContext } from "metabase/visualizations/types";
 
 import { WaterfallChart } from "./WaterfallChart";
@@ -27,6 +28,7 @@ const renderingContext: RenderingContext = {
   measureText: (text, style) =>
     measureTextWidth(text, Number(style.size), Number(style.weight)),
   fontFamily: "Lato",
+  theme: DEFAULT_VISUALIZATION_THEME,
 };
 
 export const YAxisCompactWithoutDataLabels = Template.bind({});
diff --git a/frontend/src/metabase/static-viz/index.js b/frontend/src/metabase/static-viz/index.js
index 6987169ab82fde3daf02cf2ce02b6be26f793183..9cfc4a8c2d688baeeea21d9886a596ee0ea6eeaf 100644
--- a/frontend/src/metabase/static-viz/index.js
+++ b/frontend/src/metabase/static-viz/index.js
@@ -11,6 +11,7 @@ import {
   measureTextWidth,
   measureTextEChartsAdapter,
 } from "metabase/static-viz/lib/text";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 
 import { LegacyStaticChart } from "./containers/LegacyStaticChart";
 
@@ -38,6 +39,7 @@ export function RenderChart(rawSeries, dashcardSettings, colors) {
     measureText: (text, style) =>
       measureTextWidth(text, style.size, style.weight),
     fontFamily: "Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif",
+    theme: DEFAULT_VISUALIZATION_THEME,
   };
 
   const props = {
diff --git a/frontend/src/metabase/ui/index.ts b/frontend/src/metabase/ui/index.ts
index b926912c52eab0afc736161fe4c737361414bf21..fd469e35ff887aafac0dc97b8262a4a5128d2f82 100644
--- a/frontend/src/metabase/ui/index.ts
+++ b/frontend/src/metabase/ui/index.ts
@@ -3,6 +3,7 @@ export type {
   TabsValue,
   MantineTheme,
   MantineThemeOverride,
+  MantineThemeOther,
 } from "@mantine/core";
 export { useHover } from "@mantine/hooks";
 export * from "./components";
diff --git a/frontend/src/metabase/visualizations/components/FunnelNormal.styled.tsx b/frontend/src/metabase/visualizations/components/FunnelNormal.styled.tsx
index 24cc6227f0341a82bd290a2fd0ca35a14e07d9e7..7b5bcdc5985ea1b9362a21efbd8fd20614404b20 100644
--- a/frontend/src/metabase/visualizations/components/FunnelNormal.styled.tsx
+++ b/frontend/src/metabase/visualizations/components/FunnelNormal.styled.tsx
@@ -2,7 +2,6 @@ import { css } from "@emotion/react";
 import styled from "@emotion/styled";
 
 import { isDesktopSafari } from "metabase/lib/browser";
-import { color } from "metabase/lib/colors";
 
 interface SharedProps {
   isNarrow: boolean;
@@ -17,7 +16,7 @@ interface FunnelStepProps {
 export const FunnelStep = styled.div<FunnelStepProps>`
   width: 100%;
   min-width: 20px;
-  border-right: 1px solid ${color("border")};
+  border-right: 1px solid var(--mb-color-border);
   display: flex;
   flex-direction: column;
 
@@ -95,7 +94,7 @@ interface FunnelNormalRootProps {
 export const FunnelNormalRoot = styled.div<FunnelNormalRootProps>`
   display: flex;
   padding: ${props => (props.isSmall ? "0.5rem" : "1rem")};
-  color: ${color("text-medium")};
+  color: var(--mb-color-text-medium);
 
   ${isDesktopSafari()
     ? css`
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
index cae0d890549671f366d796618d714a0aa861b2b6..25e75885c858243fac8a52b685989f21ea347aea 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
+++ b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
@@ -121,9 +121,19 @@ class TableInteractive extends Component {
   static defaultProps = {
     isPivoted: false,
     hasMetadataPopovers: true,
-    renderTableHeaderWrapper: children => {
+
+    // NOTE: the column and index arguments are used in the DatasetEditor,
+    //       which renders table header differently.
+    renderTableHeaderWrapper: (children, _column, _index, theme) => {
+      const cellTheme = theme.other?.table?.cell;
+
       return (
-        <Box className={TableS.cellData} data-testid="cell-data" c="brand">
+        <Box
+          className={TableS.cellData}
+          data-testid="cell-data"
+          c="brand"
+          fz={cellTheme?.fontSize}
+        >
           {children}
         </Box>
       );
@@ -145,6 +155,7 @@ class TableInteractive extends Component {
         c={cellTheme.color}
         bg={cellTheme.background}
         style={{ border: cellTheme.border }}
+        fz={cellTheme.fontSize}
       >
         {children}
       </Box>
@@ -771,6 +782,7 @@ class TableInteractive extends Component {
       renderTableHeaderWrapper,
       question,
       mode,
+      theme,
     } = this.props;
 
     const { dragColIndex, showDetailShortcut } = this.state;
@@ -921,6 +933,7 @@ class TableInteractive extends Component {
               </Ellipsified>,
               column,
               columnIndex,
+              theme,
             )}
           </QueryColumnInfoPopover>
           <TableDraggable
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.module.css b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.module.css
index cfd1d2e9be57f23e504e8fde99692f5b0ee655d6..1553aa7a4b16107dcf2b403f2fd843d4058d523f 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.module.css
+++ b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.module.css
@@ -5,7 +5,6 @@
 
 .TableInteractiveHeaderCellData .cellData {
   font-weight: 900;
-  font-size: 10px;
   border: 1px solid transparent;
   padding: 0.25em 0.65em;
   border-radius: 6px;
@@ -56,7 +55,6 @@
   text-overflow: ellipsis;
   overflow-x: hidden;
   font-weight: 700;
-  font-size: 12.5px;
 }
 
 /* pivot */
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive/table-theme-utils.ts b/frontend/src/metabase/visualizations/components/TableInteractive/table-theme-utils.ts
index b7184566dd8785e5d4a947458e4313bf7f244575..019005c847d7d03e2c11f01ca80c9e056dc3b415 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive/table-theme-utils.ts
+++ b/frontend/src/metabase/visualizations/components/TableInteractive/table-theme-utils.ts
@@ -16,9 +16,12 @@ export function getCellDataTheme({
   const cellTheme = theme.other?.table?.cell;
   const idTheme = theme.other?.table?.idColumn;
 
+  const fontSize = cellTheme?.fontSize;
+
   if (isIDColumn) {
     return {
       color: idTheme?.textColor,
+      fontSize,
       background:
         idTheme?.backgroundColor || alpha(theme.fn.themeColor("brand"), 0.08),
       border: `1px solid ${alpha(
@@ -28,7 +31,7 @@ export function getCellDataTheme({
     };
   }
 
-  return { color: cellTheme?.textColor };
+  return { color: cellTheme?.textColor, fontSize };
 }
 
 export const getCellHoverBackground = ({
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/chart-measurements/index.ts b/frontend/src/metabase/visualizations/echarts/cartesian/chart-measurements/index.ts
index 0405feeaf910618325616b4ed1914948b4fc7305..016e1e7c8291c48b010bece91e4474721efcefd7 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/chart-measurements/index.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/chart-measurements/index.ts
@@ -49,7 +49,7 @@ const getYAxisTicksWidth = (
   axisModel: YAxisModel,
   yAxisScaleTransforms: NumericAxisScaleTransforms,
   settings: ComputedVisualizationSettings,
-  { measureText, fontFamily }: RenderingContext,
+  { measureText, fontFamily, theme }: RenderingContext,
 ): number => {
   if (!settings["graph.y_axis.axis_enabled"]) {
     return 0;
@@ -58,7 +58,9 @@ const getYAxisTicksWidth = (
   const fontStyle = {
     ...CHART_STYLE.axisTicks,
     family: fontFamily,
+    size: theme.cartesian.label.fontSize,
   };
+
   // extents need to be untransformed to get the value of the tick label
   const [min, max] = axisModel.extent.map(extent =>
     yAxisScaleTransforms.fromEChartsAxisValue(extent),
@@ -117,20 +119,23 @@ const getXAxisTicksWidth = (
   dataset: ChartDataset,
   axisEnabledSetting: ComputedVisualizationSettings["graph.x_axis.axis_enabled"],
   axisModel: XAxisModel,
-  { measureText, fontFamily }: RenderingContext,
+  { theme, measureText, fontFamily }: RenderingContext,
 ) => {
+  const { fontSize } = theme.cartesian.label;
+
   if (!axisEnabledSetting) {
     return { firstXTickWidth: 0, lastXTickWidth: 0 };
   }
   if (axisEnabledSetting === "rotate-90") {
     return {
-      firstXTickWidth: CHART_STYLE.axisTicks.size,
-      lastXTickWidth: CHART_STYLE.axisTicks.size,
+      firstXTickWidth: fontSize,
+      lastXTickWidth: fontSize,
     };
   }
 
   const fontStyle = {
     ...CHART_STYLE.axisTicks,
+    size: fontSize,
     family: fontFamily,
   };
 
@@ -161,13 +166,16 @@ const getXAxisTicksWidth = (
 const getXAxisTicksHeight = (
   maxXTickWidth: number,
   axisEnabledSetting: ComputedVisualizationSettings["graph.x_axis.axis_enabled"],
+  { theme }: RenderingContext,
 ) => {
+  const { fontSize } = theme.cartesian.label;
+
   if (!axisEnabledSetting) {
     return 0;
   }
 
   if (axisEnabledSetting === true || axisEnabledSetting === "compact") {
-    return CHART_STYLE.axisTicks.size;
+    return fontSize;
   }
 
   if (axisEnabledSetting === "rotate-90") {
@@ -182,7 +190,7 @@ const getXAxisTicksHeight = (
     `Unexpected "graph.x_axis.axis_enabled" value ${axisEnabledSetting}`,
   );
 
-  return CHART_STYLE.axisTicks.size + CHART_STYLE.axisNameMargin;
+  return fontSize + CHART_STYLE.axisNameMargin;
 };
 
 const X_LABEL_HEIGHT_RATIO_THRESHOLD = 0.7; // x-axis labels cannot be taller than 70% of chart height
@@ -211,6 +219,8 @@ const getAutoAxisEnabledSetting = (
   outerHeight: number,
   renderingContext: RenderingContext,
 ): ComputedVisualizationSettings["graph.x_axis.axis_enabled"] => {
+  const { fontSize } = renderingContext.theme.cartesian.label;
+
   const shouldAutoSelectSetting =
     settings["graph.x_axis.axis_enabled"] === true &&
     (settings["graph.x_axis.scale"] === "ordinal" ||
@@ -232,19 +242,13 @@ const getAutoAxisEnabledSetting = (
     return true;
   }
 
-  if (
-    dimensionWidth >=
-    CHART_STYLE.axisTicks.size * X_LABEL_ROTATE_45_THRESHOLD_FACTOR
-  ) {
+  if (dimensionWidth >= fontSize * X_LABEL_ROTATE_45_THRESHOLD_FACTOR) {
     return checkHeight(maxXTickWidth, outerHeight, "rotate-45")
       ? "rotate-45"
       : false;
   }
 
-  if (
-    dimensionWidth >=
-    CHART_STYLE.axisTicks.size * X_LABEL_ROTATE_90_THRESHOLD_FACTOR
-  ) {
+  if (dimensionWidth >= fontSize * X_LABEL_ROTATE_90_THRESHOLD_FACTOR) {
     return checkHeight(maxXTickWidth, outerHeight, "rotate-90")
       ? "rotate-90"
       : false;
@@ -261,6 +265,8 @@ const getTicksDimensions = (
   hasTimelineEvents: boolean,
   renderingContext: RenderingContext,
 ) => {
+  const { fontSize } = renderingContext.theme.cartesian.label;
+
   const ticksDimensions: TicksDimensions = {
     yTicksWidthLeft: 0,
     yTicksWidthRight: 0,
@@ -302,6 +308,7 @@ const getTicksDimensions = (
   if (hasBottomAxis) {
     const fontStyle = {
       ...CHART_STYLE.axisTicks,
+      size: fontSize,
       family: renderingContext.fontFamily,
     };
 
@@ -333,7 +340,7 @@ const getTicksDimensions = (
     ticksDimensions.lastXTickWidth = lastXTickWidth;
 
     ticksDimensions.xTicksHeight =
-      getXAxisTicksHeight(maxXTickWidth, axisEnabledSetting) +
+      getXAxisTicksHeight(maxXTickWidth, axisEnabledSetting, renderingContext) +
       CHART_STYLE.axisTicksMarginX +
       (isTimeSeries && hasTimelineEvents
         ? CHART_STYLE.timelineEvents.height
@@ -357,7 +364,13 @@ export const getChartPadding = (
   ticksDimensions: TicksDimensions,
   axisEnabledSetting: ComputedVisualizationSettings["graph.x_axis.axis_enabled"],
   chartWidth: number,
+  { theme }: RenderingContext,
 ): Padding => {
+  const { fontSize } = theme.cartesian.label;
+
+  const axisNameFontSize = fontSize;
+  const seriesLabelFontSize = fontSize;
+
   const padding: Padding = {
     top: CHART_STYLE.padding.y,
     left: CHART_STYLE.padding.x,
@@ -372,8 +385,7 @@ export const getChartPadding = (
     settings["graph.show_values"] ||
     (settings["graph.show_goal"] && settings["graph.goal_label"])
   ) {
-    padding.top +=
-      CHART_STYLE.seriesLabels.size + CHART_STYLE.seriesLabels.offset;
+    padding.top += seriesLabelFontSize + CHART_STYLE.seriesLabels.offset;
   }
 
   // 2. Bottom Padding
@@ -382,14 +394,12 @@ export const getChartPadding = (
 
   const hasXAxisName = settings["graph.x_axis.labels_enabled"];
   if (hasXAxisName) {
-    padding.bottom +=
-      CHART_STYLE.axisName.size / 2 + CHART_STYLE.axisNameMargin;
+    padding.bottom += axisNameFontSize / 2 + CHART_STYLE.axisNameMargin;
   }
 
   // 3. Side (Left and Right) Padding
 
-  const yAxisNameTotalWidth =
-    CHART_STYLE.axisName.size + CHART_STYLE.axisNameMargin;
+  const yAxisNameTotalWidth = axisNameFontSize + CHART_STYLE.axisNameMargin;
 
   padding.left += ticksDimensions.yTicksWidthLeft;
   if (chartModel.leftAxisModel?.label) {
@@ -500,10 +510,13 @@ const areHorizontalXAxisTicksOverlapping = (
   dataset: ChartDataset,
   dimensionWidth: number,
   formatter: AxisFormatter,
-  { measureText, fontFamily }: RenderingContext,
+  { theme, measureText, fontFamily }: RenderingContext,
 ) => {
+  const { fontSize } = theme.cartesian.label;
+
   const fontStyle = {
     ...CHART_STYLE.axisTicks,
+    size: fontSize,
     family: fontFamily,
   };
 
@@ -648,6 +661,7 @@ export const getChartMeasurements = (
     ticksDimensions,
     axisEnabledSetting,
     width,
+    renderingContext,
   );
   const bounds = getChartBounds(width, height, padding, ticksDimensions);
 
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/constants/style.ts b/frontend/src/metabase/visualizations/echarts/cartesian/constants/style.ts
index 9e5483e38040c193e58d71bd19f9aa79b80e510c..5db7f467590e5f17dd8cb0db4085a2a9e58832ae 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/constants/style.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/constants/style.ts
@@ -24,7 +24,6 @@ export const CHART_STYLE = {
   axisTicksMarginX: 5,
   axisTicksMarginY: 10,
   axisTicks: {
-    size: 12,
     weight: 700,
   },
   seriesLabels: {
@@ -34,7 +33,6 @@ export const CHART_STYLE = {
     stackedPadding: 2,
   },
   axisName: {
-    size: 12,
     weight: 700,
   },
   axisNameMargin: 12,
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/model/series.unit.spec.ts b/frontend/src/metabase/visualizations/echarts/cartesian/model/series.unit.spec.ts
index 8395c77e705c8b654069e42fc22b11176ef7e4f0..d2bc91e206021fc813e5368b2188b890ba2f355f 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/model/series.unit.spec.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/model/series.unit.spec.ts
@@ -2,6 +2,7 @@ import type {
   BreakoutChartColumns,
   CartesianChartColumns,
 } from "metabase/visualizations/lib/graph/columns";
+import { DEFAULT_VISUALIZATION_THEME } from "metabase/visualizations/shared/utils/theme";
 import type {
   ComputedVisualizationSettings,
   RenderingContext,
@@ -30,6 +31,7 @@ const renderingContextMock: RenderingContext = {
   getColor: colorName => colorName,
   measureText: () => 0,
   fontFamily: "Lato",
+  theme: DEFAULT_VISUALIZATION_THEME,
 };
 
 describe("series", () => {
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/axis.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/axis.ts
index 6af66adec5ee0243de063a9a22d4ae899a4b284f..e9f6eceb6f55b13de34acb2f31c2630d1365a010 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/option/axis.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/axis.ts
@@ -65,7 +65,7 @@ export const getYAxisRange = (
 };
 
 export const getAxisNameDefaultOption = (
-  { getColor, fontFamily }: RenderingContext,
+  { getColor, fontFamily, theme }: RenderingContext,
   nameGap: number,
   name: string | undefined,
   rotate?: number,
@@ -76,20 +76,21 @@ export const getAxisNameDefaultOption = (
   nameRotate: rotate,
   nameTextStyle: {
     color: getColor("text-dark"),
-    fontSize: CHART_STYLE.axisName.size,
+    fontSize: theme.cartesian.label.fontSize,
     fontWeight: CHART_STYLE.axisName.weight,
     fontFamily,
   },
 });
 
 export const getTicksDefaultOption = ({
+  theme,
   getColor,
   fontFamily,
 }: RenderingContext) => {
   return {
     hideOverlap: true,
     color: getColor("text-dark"),
-    fontSize: CHART_STYLE.axisTicks.size,
+    fontSize: theme.cartesian.label.fontSize,
     fontWeight: CHART_STYLE.axisTicks.weight,
     fontFamily,
   };
@@ -110,7 +111,10 @@ const getHistogramTicksOptions = (
   chartModel: BaseCartesianChartModel,
   settings: ComputedVisualizationSettings,
   chartMeasurements: ChartMeasurements,
+  { theme }: RenderingContext,
 ) => {
+  const { fontSize } = theme.cartesian.label;
+
   if (settings["graph.x_axis.scale"] !== "histogram") {
     return {};
   }
@@ -120,16 +124,14 @@ const getHistogramTicksOptions = (
   const options = { showMinLabel: false, showMaxLabel: true };
 
   if (settings["graph.x_axis.axis_enabled"] === "rotate-45") {
-    const topOffset =
-      (histogramDimensionWidth + CHART_STYLE.axisTicks.size / 2) * Math.SQRT1_2;
+    const topOffset = (histogramDimensionWidth + fontSize / 2) * Math.SQRT1_2;
     return {
       ...options,
       padding: [0, topOffset, 0, 0],
       margin: -histogramDimensionWidth / 2 + CHART_STYLE.axisTicksMarginX,
     };
   } else if (settings["graph.x_axis.axis_enabled"] === "rotate-90") {
-    const rightOffset =
-      histogramDimensionWidth / 2 - CHART_STYLE.axisTicks.size / 2;
+    const rightOffset = histogramDimensionWidth / 2 - fontSize / 2;
     return {
       ...options,
       verticalAlign: "bottom",
@@ -334,7 +336,12 @@ export const buildCategoricalDimensionAxis = (
     axisLabel: {
       margin: CHART_STYLE.axisTicksMarginX,
       ...getDimensionTicksDefaultOption(settings, renderingContext),
-      ...getHistogramTicksOptions(chartModel, settings, chartMeasurements),
+      ...getHistogramTicksOptions(
+        chartModel,
+        settings,
+        chartMeasurements,
+        renderingContext,
+      ),
       interval: () => true,
       formatter: (value: string) => {
         const numberValue = parseNumberValue(value);
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts
index bd3321a507c7cd9d4479daee2b4bd32a0113a9bd..0cfbfbefc8bb0a5b244a78d6a1ae718702f882b7 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts
@@ -36,6 +36,7 @@ export function getGoalLineSeriesOption(
   }
 
   const goalValue = settings["graph.goal_value"];
+  const { fontSize } = renderingContext.theme.cartesian.goalLine.label;
 
   return {
     id: GOAL_LINE_SERIES_ID,
@@ -78,8 +79,7 @@ export function getGoalLineSeriesOption(
       const hasRightYAxis = chartModel.rightAxisModel == null;
       const align = hasRightYAxis ? ("right" as const) : ("left" as const);
       const labelX = hasRightYAxis ? xEnd : xStart;
-      const labelY =
-        y - CHART_STYLE.goalLine.label.size - CHART_STYLE.goalLine.label.margin;
+      const labelY = y - fontSize - CHART_STYLE.goalLine.label.margin;
 
       const label = {
         type: "text" as const,
@@ -94,7 +94,7 @@ export function getGoalLineSeriesOption(
           align,
           text: settings["graph.goal_label"] ?? "",
           fontFamily: renderingContext.fontFamily,
-          fontSize: CHART_STYLE.goalLine.label.size,
+          fontSize,
           fontWeight: CHART_STYLE.goalLine.label.weight,
           fill: renderingContext.getColor("text-medium"),
         },
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts
index 65729e223c88a122dd74e082bd99b79c292b7628..9abbad6896f39ae98c808ed59b9ec73e32b2fb76 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts
@@ -266,6 +266,8 @@ export const buildEChartsLabelOptions = (
   chartDataDensity?: ChartDataDensity,
   position?: "top" | "bottom" | "inside",
 ): SeriesLabelOption => {
+  const { fontSize } = renderingContext.theme.cartesian.label;
+
   return {
     show: !!formatter,
     silent: true,
@@ -273,7 +275,7 @@ export const buildEChartsLabelOptions = (
     opacity: 1,
     fontFamily: renderingContext.fontFamily,
     fontWeight: CHART_STYLE.seriesLabels.weight,
-    fontSize: CHART_STYLE.seriesLabels.size,
+    fontSize,
     color: renderingContext.getColor("text-dark"),
     textBorderColor: renderingContext.getColor("white"),
     textBorderWidth: 3,
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/model.ts b/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/model.ts
index 80e9276fef9a8ed4016986db2bc625bb4a722687..ff0d59e6e8ea6676df94dd90a405a43db2af03c0 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/model.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/model.ts
@@ -62,6 +62,7 @@ const getMinDistanceFromTimelineEventGroup = (
 
   const countLabelWidth = renderingContext.measureText(eventsCount.toString(), {
     ...CHART_STYLE.axisTicks,
+    size: renderingContext.theme.cartesian.label.fontSize,
     family: renderingContext.fontFamily,
   });
 
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts b/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts
index 7249d3d46d22487ae70405b3565326e5ab3321de..5f021317fee5ee11c79f485b7735933269e30c94 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts
@@ -40,8 +40,10 @@ function svgToImageUri(svgString: string) {
 export const getTimelineEventsSeries = (
   timelineEventsModel: TimelineEventsModel,
   selectedEventsIds: TimelineEventId[],
-  { fontFamily, getColor }: RenderingContext,
+  { fontFamily, getColor, theme }: RenderingContext,
 ): LineSeriesOption | null => {
+  const { fontSize } = theme?.cartesian?.label ?? {};
+
   if (timelineEventsModel.length === 0) {
     return null;
   }
@@ -74,7 +76,7 @@ export const getTimelineEventsSeries = (
           padding: [0, 0, 0, 24],
           hideOverlap: true,
           color,
-          fontSize: CHART_STYLE.axisTicks.size,
+          fontSize,
           fontWeight: CHART_STYLE.axisTicks.weight,
           fontFamily,
         },
diff --git a/frontend/src/metabase/visualizations/hooks/use-browser-rendering-context.ts b/frontend/src/metabase/visualizations/hooks/use-browser-rendering-context.ts
index 4f8d6110695c66f69f9f97ba1dbcae2dcde22c0d..6de4af88068f136a91f10b871ee8f3dd1a4ee552 100644
--- a/frontend/src/metabase/visualizations/hooks/use-browser-rendering-context.ts
+++ b/frontend/src/metabase/visualizations/hooks/use-browser-rendering-context.ts
@@ -4,20 +4,31 @@ import { usePalette } from "metabase/hooks/use-palette";
 import { color } from "metabase/lib/colors";
 import { formatValue } from "metabase/lib/formatting/value";
 import { measureTextWidth } from "metabase/lib/measure-text";
+import { useMantineTheme } from "metabase/ui";
+import { getVisualizationTheme } from "metabase/visualizations/shared/utils/theme";
 import type { RenderingContext } from "metabase/visualizations/types";
 
+interface RenderingOptions {
+  fontFamily: string;
+}
+
 export const useBrowserRenderingContext = (
-  fontFamily: string,
+  options: RenderingOptions,
 ): RenderingContext => {
+  const { fontFamily } = options;
+
   const palette = usePalette();
+  const theme = useMantineTheme();
+
+  return useMemo(() => {
+    const style = getVisualizationTheme(theme.other);
 
-  return useMemo(
-    () => ({
+    return {
       getColor: name => color(name, palette),
       formatValue: (value, options) => String(formatValue(value, options)),
       measureText: measureTextWidth,
       fontFamily: `${fontFamily}, Arial, sans-serif`,
-    }),
-    [fontFamily, palette],
-  );
+      theme: style,
+    };
+  }, [fontFamily, palette, theme]);
 };
diff --git a/frontend/src/metabase/visualizations/shared/components/RowChart/RowChart.stories.tsx b/frontend/src/metabase/visualizations/shared/components/RowChart/RowChart.stories.tsx
index 1d1837a7451426fabf1977e3336fdc5b4a4a642f..674d553bd4f25f56a41a17db25f2056f0909c207 100644
--- a/frontend/src/metabase/visualizations/shared/components/RowChart/RowChart.stories.tsx
+++ b/frontend/src/metabase/visualizations/shared/components/RowChart/RowChart.stories.tsx
@@ -1,8 +1,11 @@
 import type { ComponentStory } from "@storybook/react";
 
+import { SdkVisualizationWrapper } from "__support__/storybook";
 import { color } from "metabase/lib/colors";
 import { measureTextWidth } from "metabase/lib/measure-text";
 import { getStaticChartTheme } from "metabase/static-viz/components/RowChart/theme";
+import { Box } from "metabase/ui";
+import { useRowChartTheme } from "metabase/visualizations/visualizations/RowChart/utils/theme";
 
 import { RowChart } from "./RowChart";
 
@@ -13,14 +16,13 @@ export default {
 
 const Template: ComponentStory<typeof RowChart> = args => {
   return (
-    <div style={{ padding: 8, height: 600, backgroundColor: "white" }}>
+    <Box h={600} bg="white" p="8px">
       <RowChart {...args} />
-    </div>
+    </Box>
   );
 };
 
-export const Default = Template.bind({});
-Default.args = {
+const DEFAULT_ROW_CHART_ARGS = {
   width: 800,
   height: 400,
   data: [
@@ -78,3 +80,22 @@ Default.args = {
 
   style: { fontFamily: "Lato" },
 };
+
+export const Default = Template.bind({});
+Default.args = DEFAULT_ROW_CHART_ARGS;
+
+const ThemedRowChart = () => {
+  const theme = useRowChartTheme({ fontFamily: "Lato" });
+
+  return (
+    <Box h={600} bg="white" p="8px">
+      <RowChart {...DEFAULT_ROW_CHART_ARGS} theme={theme} stackOffset={null} />
+    </Box>
+  );
+};
+
+export const HugeFont = () => (
+  <SdkVisualizationWrapper theme={{ fontSize: "20px" }}>
+    <ThemedRowChart />
+  </SdkVisualizationWrapper>
+);
diff --git a/frontend/src/metabase/visualizations/shared/utils/size-in-px.ts b/frontend/src/metabase/visualizations/shared/utils/size-in-px.ts
new file mode 100644
index 0000000000000000000000000000000000000000..47b4be1ae379a368545e764b6f83d3a9abf0ed9a
--- /dev/null
+++ b/frontend/src/metabase/visualizations/shared/utils/size-in-px.ts
@@ -0,0 +1,33 @@
+/**
+ * Convert string font sizes (e.g. 12px, 25em) to number in pixels.
+ *
+ * This is useful for visualizations that require font size in pixels,
+ * mainly for calculating padding and offsets.
+ **/
+export function getSizeInPx(
+  value?: string | number,
+  parentFontSize: number = 16,
+): number | undefined {
+  if (typeof value !== "string") {
+    return value;
+  }
+
+  if (value.endsWith("px")) {
+    return stripNaN(parseFloat(value));
+  }
+
+  if (value.endsWith("em")) {
+    const em = stripNaN(parseFloat(value.replace(/em|rem/, "")));
+
+    if (em === undefined) {
+      return undefined;
+    }
+
+    const emValue = em * parentFontSize;
+
+    return Math.round(emValue * 100) / 100;
+  }
+}
+
+const stripNaN = (value: number): number | undefined =>
+  isNaN(value) ? undefined : value;
diff --git a/frontend/src/metabase/visualizations/shared/utils/size-in-px.unit.spec.ts b/frontend/src/metabase/visualizations/shared/utils/size-in-px.unit.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c6c1e48a1b53234930aad6de525d99937cab444c
--- /dev/null
+++ b/frontend/src/metabase/visualizations/shared/utils/size-in-px.unit.spec.ts
@@ -0,0 +1,24 @@
+import { getSizeInPx } from "./size-in-px";
+
+describe("getSizeInPx", () => {
+  it("returns the number value if it's not a string", () => {
+    expect(getSizeInPx(12)).toBe(12);
+  });
+
+  it("returns the px units as a number", () => {
+    expect(getSizeInPx("14px")).toBe(14);
+    expect(getSizeInPx("8.256px")).toBe(8.256);
+  });
+
+  it("converts em/rem units based on parent font size", () => {
+    expect(getSizeInPx("0.75em")).toBe(12);
+    expect(getSizeInPx("0.75rem", 14)).toBe(10.5);
+    expect(getSizeInPx("1.56em", 18)).toBe(28.08);
+  });
+
+  it("returns undefined if the value cannot be parsed", () => {
+    expect(getSizeInPx("15%")).toBe(undefined);
+    expect(getSizeInPx("22")).toBe(undefined);
+    expect(getSizeInPx("foobar")).toBe(undefined);
+  });
+});
diff --git a/frontend/src/metabase/visualizations/shared/utils/theme.ts b/frontend/src/metabase/visualizations/shared/utils/theme.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c0d3193f89521ec8e8563e2529206ff01bf8c888
--- /dev/null
+++ b/frontend/src/metabase/visualizations/shared/utils/theme.ts
@@ -0,0 +1,34 @@
+import { DEFAULT_METABASE_COMPONENT_THEME } from "embedding-sdk/lib/theme";
+import type { MantineThemeOther } from "metabase/ui";
+import { getSizeInPx } from "metabase/visualizations/shared/utils/size-in-px";
+import type { VisualizationTheme } from "metabase/visualizations/types";
+
+/**
+ * Computes the visualization style from the Mantine theme.
+ */
+export function getVisualizationTheme(
+  options: MantineThemeOther,
+): VisualizationTheme {
+  const { cartesian } = options;
+
+  // This allows sdk users to set the base font size,
+  // which scales the visualization's font sizes.
+  const baseFontSize = getSizeInPx(options.fontSize);
+
+  // ECharts requires font sizes in px for offset calculations.
+  const px = (value: string) =>
+    getSizeInPx(value, baseFontSize) ?? baseFontSize ?? 14;
+
+  return {
+    cartesian: {
+      label: { fontSize: px(cartesian.label.fontSize) },
+      goalLine: {
+        label: { fontSize: px(cartesian.goalLine.label.fontSize) },
+      },
+    },
+  };
+}
+
+export const DEFAULT_VISUALIZATION_THEME = getVisualizationTheme(
+  DEFAULT_METABASE_COMPONENT_THEME,
+);
diff --git a/frontend/src/metabase/visualizations/types/visualization.ts b/frontend/src/metabase/visualizations/types/visualization.ts
index dd0ec98f2ddd9afdf0ec6c2fdc5064c9152add59..5fa19f2293695135cab8b74dab8174c075b4dbb7 100644
--- a/frontend/src/metabase/visualizations/types/visualization.ts
+++ b/frontend/src/metabase/visualizations/types/visualization.ts
@@ -31,6 +31,25 @@ export interface RenderingContext {
 
   measureText: TextWidthMeasurer;
   fontFamily: string;
+
+  theme: VisualizationTheme;
+}
+
+/**
+ * Visualization theming overrides.
+ * Refer to DEFAULT_METABASE_COMPONENT_THEME for the default values.
+ **/
+export interface VisualizationTheme {
+  cartesian: {
+    label: {
+      fontSize: number;
+    };
+    goalLine: {
+      label: {
+        fontSize: number;
+      };
+    };
+  };
 }
 
 export type OnChangeCardAndRunOpts = {
diff --git a/frontend/src/metabase/visualizations/visualizations/BarChart/BarChart.stories.tsx b/frontend/src/metabase/visualizations/visualizations/BarChart/BarChart.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..53755d658a972810ed9a8e10fd57cbf46c4d73dc
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/BarChart/BarChart.stories.tsx
@@ -0,0 +1,59 @@
+import type { Story } from "@storybook/react";
+
+import {
+  SdkVisualizationWrapper,
+  VisualizationWrapper,
+} from "__support__/storybook";
+import { NumberColumn, StringColumn } from "__support__/visualizations";
+import type { MetabaseTheme } from "embedding-sdk";
+import { Box } from "metabase/ui";
+import { registerVisualization } from "metabase/visualizations";
+import Visualization from "metabase/visualizations/components/Visualization";
+import { createMockCard } from "metabase-types/api/mocks";
+
+import { BarChart } from "./BarChart";
+
+export default {
+  title: "viz/BarChart",
+  component: BarChart,
+};
+
+// @ts-expect-error: incompatible prop types with registerVisualization
+registerVisualization(BarChart);
+
+const MOCK_SERIES = [
+  {
+    card: createMockCard({ name: "Card", display: "bar" }),
+    data: {
+      cols: [
+        StringColumn({ name: "Dimension" }),
+        NumberColumn({ name: "Count" }),
+      ],
+      rows: [
+        ["foo", 1],
+        ["bar", 2],
+      ],
+    },
+  },
+];
+
+export const Default: Story = () => (
+  <VisualizationWrapper>
+    <Box h={500}>
+      <Visualization rawSeries={MOCK_SERIES} width={500} />
+    </Box>
+  </VisualizationWrapper>
+);
+
+// Example of how themes can be applied in the SDK.
+export const EmbeddingHugeFont: Story = () => {
+  const theme: MetabaseTheme = { fontSize: "20px" };
+
+  return (
+    <SdkVisualizationWrapper theme={theme}>
+      <Box h={500}>
+        <Visualization rawSeries={MOCK_SERIES} width={500} />
+      </Box>
+    </SdkVisualizationWrapper>
+  );
+};
diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-models-and-option.ts b/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-models-and-option.ts
index 3320b4a2abca8e234f27171268329562a5591669..a83514dc49d53822ee81c494579b2e24c7f784a3 100644
--- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-models-and-option.ts
+++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-models-and-option.ts
@@ -33,6 +33,8 @@ export function useModelsAndOption({
   onRender,
   hovered,
 }: VisualizationProps) {
+  const renderingContext = useBrowserRenderingContext({ fontFamily });
+
   const rawSeriesWithRemappings = useMemo(
     () => extractRemappings(rawSeries),
     [rawSeries],
@@ -48,8 +50,6 @@ export function useModelsAndOption({
     [onRender],
   );
 
-  const renderingContext = useBrowserRenderingContext(fontFamily);
-
   const hasTimelineEvents = timelineEvents
     ? timelineEvents.length !== 0
     : false;
diff --git a/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.tsx b/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.tsx
index 17455f87e88c28ad843a8d7311355c880b8f94a6..63504b5113b5f0554d4b50c8fe50c3939a9da10d 100644
--- a/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.tsx
+++ b/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.tsx
@@ -204,7 +204,7 @@ export function Funnel(props: VisualizationProps) {
   } = props;
   const hasTitle = showTitle && settings["card.title"];
 
-  const renderingContext = useBrowserRenderingContext(fontFamily);
+  const renderingContext = useBrowserRenderingContext({ fontFamily });
 
   if (settings["funnel.type"] === "bar") {
     return (
diff --git a/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.unit.spec.jsx b/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.unit.spec.jsx
index ed32f6bb970d809a92055a9853cdbdbd4e7b0081..a8b6a397a2df788338026796deeb9f712d0f152e 100644
--- a/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.unit.spec.jsx
+++ b/frontend/src/metabase/visualizations/visualizations/Funnel/Funnel.unit.spec.jsx
@@ -1,4 +1,5 @@
 import { render, screen } from "__support__/ui";
+import { ThemeProvider } from "metabase/ui";
 import registerVisualizations from "metabase/visualizations/register";
 import {
   createMockCard,
@@ -45,14 +46,17 @@ const setup = (funnelProps, visualizationSettings = {}) => {
   });
 
   render(
-    <Funnel
-      series={[series]}
-      rawSeries={[series]}
-      settings={settings}
-      visualizationIsClickable={jest.fn()}
-      card={card}
-      {...funnelProps}
-    />,
+    <ThemeProvider>
+      <Funnel
+        series={[series]}
+        rawSeries={[series]}
+        settings={settings}
+        visualizationIsClickable={jest.fn()}
+        card={card}
+        {...funnelProps}
+      />
+      ,
+    </ThemeProvider>,
   );
 };
 
diff --git a/frontend/src/metabase/visualizations/visualizations/LineChart/LineChart.stories.tsx b/frontend/src/metabase/visualizations/visualizations/LineChart/LineChart.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a2988784d011a87a0966518002ce5eeb76a0032c
--- /dev/null
+++ b/frontend/src/metabase/visualizations/visualizations/LineChart/LineChart.stories.tsx
@@ -0,0 +1,66 @@
+import type { Story } from "@storybook/react";
+
+import {
+  SdkVisualizationWrapper,
+  VisualizationWrapper,
+} from "__support__/storybook";
+import { NumberColumn, StringColumn } from "__support__/visualizations";
+import type { MetabaseTheme } from "embedding-sdk";
+import { Box } from "metabase/ui";
+import { registerVisualization } from "metabase/visualizations";
+import Visualization from "metabase/visualizations/components/Visualization";
+import {
+  createMockCard,
+  createMockStructuredDatasetQuery,
+} from "metabase-types/api/mocks";
+
+import { LineChart } from "./LineChart";
+
+export default {
+  title: "viz/LineChart",
+  component: LineChart,
+};
+
+// @ts-expect-error: incompatible prop types with registerVisualization
+registerVisualization(LineChart);
+
+const dataset_query = createMockStructuredDatasetQuery({
+  query: { "source-table": 1 },
+});
+
+const MOCK_SERIES = [
+  {
+    card: createMockCard({ id: 1, display: "line", dataset_query }),
+    data: {
+      cols: [
+        StringColumn({ name: "Dimension" }),
+        NumberColumn({ name: "Count" }),
+      ],
+      rows: [
+        ["foo", 4],
+        ["bar", 20],
+        ["baz", 12],
+      ],
+    },
+  },
+];
+
+export const Default: Story = () => (
+  <VisualizationWrapper>
+    <Box h={500}>
+      <Visualization rawSeries={MOCK_SERIES} width={500} />
+    </Box>
+  </VisualizationWrapper>
+);
+
+export const EmbeddingHugeFont: Story = () => {
+  const theme: MetabaseTheme = { fontSize: "20px" };
+
+  return (
+    <SdkVisualizationWrapper theme={theme}>
+      <Box h={500}>
+        <Visualization rawSeries={MOCK_SERIES} width={500} />
+      </Box>
+    </SdkVisualizationWrapper>
+  );
+};
diff --git a/frontend/src/metabase/visualizations/visualizations/RowChart/RowChart.tsx b/frontend/src/metabase/visualizations/visualizations/RowChart/RowChart.tsx
index 3322728ff57e223df3527349b4410db6d9f3a63f..ba1666de8850ec059f2f162e6686df120ed7f9ac 100644
--- a/frontend/src/metabase/visualizations/visualizations/RowChart/RowChart.tsx
+++ b/frontend/src/metabase/visualizations/visualizations/RowChart/RowChart.tsx
@@ -48,7 +48,7 @@ import {
   getHoverData,
   getLegendClickData,
 } from "metabase/visualizations/visualizations/RowChart/utils/events";
-import { getChartTheme } from "metabase/visualizations/visualizations/RowChart/utils/theme";
+import { useRowChartTheme } from "metabase/visualizations/visualizations/RowChart/utils/theme";
 import { isDimension, isMetric } from "metabase-lib/v1/types/utils/isa";
 import type { DatasetData, VisualizationSettings } from "metabase-types/api";
 
@@ -134,8 +134,8 @@ const RowChartVisualization = ({
     [chartColumns, data, formatColumnValue],
   );
   const goal = useMemo(() => getChartGoal(settings), [settings]);
-  const theme = useMemo(getChartTheme, []);
   const stackOffset = getStackOffset(settings);
+  const theme = useRowChartTheme({ fontFamily });
 
   const chartWarnings = useMemo(
     () => getChartWarnings(chartColumns, data.rows),
diff --git a/frontend/src/metabase/visualizations/visualizations/RowChart/utils/theme.ts b/frontend/src/metabase/visualizations/visualizations/RowChart/utils/theme.ts
index 835caa2b1cd49d03750c02350ed3381287e7a6e3..36073fde259b666e1c7061d38266ac00b4aaa208 100644
--- a/frontend/src/metabase/visualizations/visualizations/RowChart/utils/theme.ts
+++ b/frontend/src/metabase/visualizations/visualizations/RowChart/utils/theme.ts
@@ -1,40 +1,57 @@
+import { useMemo } from "react";
+
 import { color } from "metabase/lib/colors";
+import { useMantineTheme } from "metabase/ui";
 import type { RowChartTheme } from "metabase/visualizations/shared/components/RowChart/types";
+import { getVisualizationTheme } from "metabase/visualizations/shared/utils/theme";
 
-export const getChartTheme = (fontFamily: string = "Lato"): RowChartTheme => {
-  return {
-    axis: {
-      color: color("border"),
-      ticks: {
-        size: 12,
-        weight: 700,
-        color: color("text-medium"),
-        family: fontFamily,
+interface RowChartThemeOptions {
+  fontFamily?: string;
+}
+
+export const useRowChartTheme = (
+  options: RowChartThemeOptions,
+): RowChartTheme => {
+  const theme = useMantineTheme();
+
+  return useMemo(() => {
+    const { fontFamily = "Lato" } = options;
+    const { cartesian } = getVisualizationTheme(theme.other);
+
+    return {
+      axis: {
+        color: color("border"),
+        ticks: {
+          size: cartesian.label.fontSize,
+          weight: 700,
+          color: color("text-medium"),
+          family: fontFamily,
+        },
+        label: {
+          size: cartesian.label.fontSize,
+          weight: 700,
+          color: color("text-dark"),
+          family: fontFamily,
+        },
+      },
+      goal: {
+        lineStroke: color("text-medium"),
+        label: {
+          size: cartesian.goalLine.label.fontSize,
+          weight: 700,
+          color: color("text-medium"),
+          family: fontFamily,
+        },
       },
-      label: {
-        size: 12,
+      dataLabels: {
         weight: 700,
         color: color("text-dark"),
+        size: cartesian.label.fontSize,
         family: fontFamily,
       },
-    },
-    goal: {
-      lineStroke: color("text-medium"),
-      label: {
-        size: 14,
-        weight: 700,
-        color: color("text-medium"),
-        family: fontFamily,
+      grid: {
+        color: color("border"),
       },
-    },
-    dataLabels: {
-      weight: 700,
-      color: color("text-dark"),
-      size: 12,
-      family: fontFamily,
-    },
-    grid: {
-      color: color("border"),
-    },
-  };
+    };
+  }, [options, theme]);
 };
diff --git a/frontend/src/metabase/visualizations/visualizations/SmartScalar/SmartScalar.stories.tsx b/frontend/src/metabase/visualizations/visualizations/SmartScalar/SmartScalar.stories.tsx
index 28ee83b47c4fbf05f351acd387a09ad70d66c00d..8b8e80c5ef091d88a029c5379f73f2e6ff858b90 100644
--- a/frontend/src/metabase/visualizations/visualizations/SmartScalar/SmartScalar.stories.tsx
+++ b/frontend/src/metabase/visualizations/visualizations/SmartScalar/SmartScalar.stories.tsx
@@ -4,6 +4,7 @@ import {
   SdkVisualizationWrapper,
   VisualizationWrapper,
 } from "__support__/storybook";
+import type { MetabaseTheme } from "embedding-sdk";
 import { registerVisualization } from "metabase/visualizations";
 import Visualization from "metabase/visualizations/components/Visualization";
 
@@ -36,7 +37,7 @@ export const Default: Story = () => (
 
 // Example of how themes can be applied in the SDK.
 export const EmbeddingTemplate: Story = () => {
-  const theme = {
+  const theme: MetabaseTheme = {
     colors: {
       positive: "#4834d4",
       negative: "#e84118",
diff --git a/loki.config.js b/loki.config.js
index e684dd31599a13d4436d91f291b14f02656a5874..35fd56a7256192c82c2424496676f708d0b7a39e 100644
--- a/loki.config.js
+++ b/loki.config.js
@@ -1,6 +1,6 @@
 module.exports = {
   diffingEngine: "looks-same",
-  storiesFilter: "static-viz|viz",
+  storiesFilter: "static-viz|viz|visualizations/shared",
   configurations: {
     "chrome.laptop": {
       target: "chrome.docker",
diff --git a/webpack.static-viz.config.js b/webpack.static-viz.config.js
index c69db2e5dc7613b26e4dfd05b68b8b33b2369a12..8fac3ca092d844ceb40b839520e692070b887542 100644
--- a/webpack.static-viz.config.js
+++ b/webpack.static-viz.config.js
@@ -9,6 +9,7 @@ const CLJS_SRC_PATH = __dirname + "/target/cljs_release";
 const CLJS_SRC_PATH_DEV = __dirname + "/target/cljs_dev";
 const LIB_SRC_PATH = __dirname + "/frontend/src/metabase-lib";
 const TYPES_SRC_PATH = __dirname + "/frontend/src/metabase-types";
+const SDK_SRC_PATH = __dirname + "/enterprise/frontend/src/embedding-sdk";
 
 const BABEL_CONFIG = {
   cacheDirectory: process.env.BABEL_DISABLE_CACHE ? null : ".babel_cache",
@@ -89,6 +90,7 @@ module.exports = env => {
         cljs: devMode ? CLJS_SRC_PATH_DEV : CLJS_SRC_PATH,
         "metabase-lib": LIB_SRC_PATH,
         "metabase-types": TYPES_SRC_PATH,
+        "embedding-sdk": SDK_SRC_PATH,
       },
     },
     optimization: {