From f223db9330dc26d82cecef0315d4d9e45db15e26 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 26 May 2026 12:30:13 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20fix(charts):=20improve=20dark=20?= =?UTF-8?q?mode=20chart=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure VChart labels and grid lines use theme-aware colors, and remove oversized rounded corners from ranking bar charts. --- .../components/model-details-charts.tsx | 51 ++++++++++++++----- .../components/market-share-section.tsx | 29 ++++++----- .../rankings/components/models-section.tsx | 29 ++++++----- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/web/default/src/features/pricing/components/model-details-charts.tsx b/web/default/src/features/pricing/components/model-details-charts.tsx index 0065069e..1f9923f8 100644 --- a/web/default/src/features/pricing/components/model-details-charts.tsx +++ b/web/default/src/features/pricing/components/model-details-charts.tsx @@ -47,6 +47,19 @@ function formatDayLabel(date: string): string { }) } +function getChartThemeTokens(resolvedTheme: string) { + return { + textColor: + resolvedTheme === 'dark' + ? 'rgba(255, 255, 255, 0.68)' + : 'rgba(15, 23, 42, 0.58)', + gridColor: + resolvedTheme === 'dark' + ? 'rgba(255, 255, 255, 0.12)' + : 'rgba(15, 23, 42, 0.12)', + } +} + // --------------------------------------------------------------------------- // Latency trend chart (24h, multi-group point-line chart) // --------------------------------------------------------------------------- @@ -57,6 +70,7 @@ export function LatencyTrendChart(props: { }) { const { t } = useTranslation() const { resolvedTheme, themeReady } = useChartTheme() + const { textColor, gridColor } = getChartThemeTokens(resolvedTheme) const spec = useMemo(() => { if (props.series.length === 0) return null @@ -95,7 +109,7 @@ export function LatencyTrendChart(props: { { orient: 'bottom', label: { - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: textColor, fontSize: 10 }, }, tick: { visible: false }, }, @@ -103,13 +117,16 @@ export function LatencyTrendChart(props: { orient: 'left', label: { formatMethod: (val: number | string) => `${val} ms`, - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: textColor, fontSize: 10 }, + }, + grid: { + visible: true, + style: { lineDash: [3, 3], stroke: gridColor }, }, - grid: { visible: true, style: { lineDash: [3, 3] } }, }, ], } - }, [props.series, t]) + }, [gridColor, props.series, t, textColor]) if (props.series.length === 0) { return ( @@ -151,6 +168,7 @@ export function UptimeTrendChart(props: { }) { const { t } = useTranslation() const { resolvedTheme, themeReady } = useChartTheme() + const { textColor, gridColor } = getChartThemeTokens(resolvedTheme) const spec = useMemo(() => { if (props.series.length === 0) return null @@ -207,7 +225,7 @@ export function UptimeTrendChart(props: { { orient: 'bottom', label: { - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: textColor, fontSize: 10 }, autoLimit: true, }, tick: { visible: false }, @@ -218,13 +236,16 @@ export function UptimeTrendChart(props: { max: 100, label: { formatMethod: (val: number | string) => `${val}%`, - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: textColor, fontSize: 10 }, + }, + grid: { + visible: true, + style: { lineDash: [3, 3], stroke: gridColor }, }, - grid: { visible: true, style: { lineDash: [3, 3] } }, }, ], } - }, [props.series, t]) + }, [gridColor, props.series, t, textColor]) if (props.series.length === 0) { return ( @@ -266,6 +287,7 @@ export function ThroughputBarChart(props: { }) { const { t } = useTranslation() const { resolvedTheme, themeReady } = useChartTheme() + const { textColor, gridColor } = getChartThemeTokens(resolvedTheme) const { customization } = useThemeCustomization() const barRadius = useThemeRadiusPx( '--radius-sm', @@ -294,19 +316,22 @@ export function ThroughputBarChart(props: { label: { visible: true, position: 'right', - style: { fontSize: 11, fill: 'currentColor' }, + style: { fontSize: 11, fill: textColor }, formatMethod: (text: string) => `${text} t/s`, }, axes: [ { orient: 'left', - label: { style: { fill: 'currentColor', fontSize: 10 } }, + label: { style: { fill: textColor, fontSize: 10 } }, tick: { visible: false }, }, { orient: 'bottom', - label: { style: { fill: 'currentColor', fontSize: 10 } }, - grid: { visible: true, style: { lineDash: [3, 3] } }, + label: { style: { fill: textColor, fontSize: 10 } }, + grid: { + visible: true, + style: { lineDash: [3, 3], stroke: gridColor }, + }, }, ], tooltip: { @@ -322,7 +347,7 @@ export function ThroughputBarChart(props: { }, }, } - }, [barRadius, filtered, t]) + }, [barRadius, filtered, gridColor, t, textColor]) if (filtered.length === 0) { return null diff --git a/web/default/src/features/rankings/components/market-share-section.tsx b/web/default/src/features/rankings/components/market-share-section.tsx index ba90d6de..51bcbc11 100644 --- a/web/default/src/features/rankings/components/market-share-section.tsx +++ b/web/default/src/features/rankings/components/market-share-section.tsx @@ -20,10 +20,8 @@ import { useMemo } from 'react' import { VChart } from '@visactor/react-vchart' import { PieChart } from 'lucide-react' import { useTranslation } from 'react-i18next' -import { useThemeRadiusPx } from '@/lib/theme-radius' import { useChartTheme } from '@/lib/use-chart-theme' import { VCHART_OPTION } from '@/lib/vchart' -import { useThemeCustomization } from '@/context/theme-customization-provider' import { formatShare, formatTokens } from '../lib/format' import type { RankingPeriod, VendorRanking, VendorShareSeries } from '../types' import { VendorLink } from './entity-links' @@ -104,11 +102,14 @@ type MarketShareSectionProps = { export function MarketShareSection(props: MarketShareSectionProps) { const { t } = useTranslation() const { resolvedTheme, themeReady } = useChartTheme() - const { customization } = useThemeCustomization() - const barRadius = useThemeRadiusPx( - '--radius-sm', - `${customization.preset}:${customization.radius}` - ) + const chartTextColor = + resolvedTheme === 'dark' + ? 'rgba(255, 255, 255, 0.68)' + : 'rgba(15, 23, 42, 0.58)' + const chartGridColor = + resolvedTheme === 'dark' + ? 'rgba(255, 255, 255, 0.12)' + : 'rgba(15, 23, 42, 0.12)' const colourMap = useMemo( () => buildVendorColourMap(props.history.vendors.map((v) => v.name)), @@ -137,15 +138,12 @@ export function MarketShareSection(props: MarketShareSectionProps) { stack: true, paddingInner: 0.12, legends: { visible: false }, - bar: { - style: barRadius == null ? {} : { cornerRadius: barRadius }, - }, color: { specified: colourMap }, axes: [ { orient: 'bottom', label: { - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: chartTextColor, fontSize: 10 }, autoHide: true, autoLimit: true, }, @@ -158,9 +156,12 @@ export function MarketShareSection(props: MarketShareSectionProps) { label: { formatMethod: (val: number | string) => `${Math.round(Number(val) * 100)}%`, - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: chartTextColor, fontSize: 10 }, + }, + grid: { + visible: true, + style: { lineDash: [3, 3], stroke: chartGridColor }, }, - grid: { visible: true, style: { lineDash: [3, 3] } }, }, ], tooltip: { @@ -202,7 +203,7 @@ export function MarketShareSection(props: MarketShareSectionProps) { }, animationAppear: { duration: 500 }, } - }, [barRadius, colourMap, orderedPoints]) + }, [chartGridColor, chartTextColor, colourMap, orderedPoints]) const visible = props.rows.slice(0, MAX_VENDORS_IN_LIST) const half = Math.ceil(visible.length / 2) diff --git a/web/default/src/features/rankings/components/models-section.tsx b/web/default/src/features/rankings/components/models-section.tsx index e9d16d34..f59a0b14 100644 --- a/web/default/src/features/rankings/components/models-section.tsx +++ b/web/default/src/features/rankings/components/models-section.tsx @@ -20,10 +20,8 @@ import { useMemo } from 'react' import { VChart } from '@visactor/react-vchart' import { BarChart3, Trophy } from 'lucide-react' import { useTranslation } from 'react-i18next' -import { useThemeRadiusPx } from '@/lib/theme-radius' import { useChartTheme } from '@/lib/use-chart-theme' import { VCHART_OPTION } from '@/lib/vchart' -import { useThemeCustomization } from '@/context/theme-customization-provider' import { formatTokens } from '../lib/format' import type { ModelHistorySeries, ModelRanking, RankingPeriod } from '../types' import { ModelLeaderboard } from './model-leaderboard' @@ -52,11 +50,14 @@ type ModelsSectionProps = { export function ModelsSection(props: ModelsSectionProps) { const { t } = useTranslation() const { resolvedTheme, themeReady } = useChartTheme() - const { customization } = useThemeCustomization() - const barRadius = useThemeRadiusPx( - '--radius-sm', - `${customization.preset}:${customization.radius}` - ) + const chartTextColor = + resolvedTheme === 'dark' + ? 'rgba(255, 255, 255, 0.68)' + : 'rgba(15, 23, 42, 0.58)' + const chartGridColor = + resolvedTheme === 'dark' + ? 'rgba(255, 255, 255, 0.12)' + : 'rgba(15, 23, 42, 0.12)' // Order points so the largest model appears at the bottom of every stack. const orderedPoints = useMemo(() => { @@ -84,15 +85,12 @@ export function ModelsSection(props: ModelsSectionProps) { yField: 'tokens', seriesField: 'model', stack: true, - bar: { - style: barRadius == null ? {} : { cornerRadius: barRadius }, - }, legends: { visible: false }, axes: [ { orient: 'bottom', label: { - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: chartTextColor, fontSize: 10 }, autoHide: true, autoLimit: true, }, @@ -102,9 +100,12 @@ export function ModelsSection(props: ModelsSectionProps) { orient: 'left', label: { formatMethod: (val: number | string) => formatTokens(Number(val)), - style: { fill: 'currentColor', fontSize: 10 }, + style: { fill: chartTextColor, fontSize: 10 }, + }, + grid: { + visible: true, + style: { lineDash: [3, 3], stroke: chartGridColor }, }, - grid: { visible: true, style: { lineDash: [3, 3] } }, }, ], tooltip: { @@ -159,7 +160,7 @@ export function ModelsSection(props: ModelsSectionProps) { }, animationAppear: { duration: 500 }, } - }, [barRadius, orderedPoints, t]) + }, [chartGridColor, chartTextColor, orderedPoints, t]) return (