From 40e34181e7d2032cce00aaefe62ab33b7467c0d3 Mon Sep 17 00:00:00 2001 From: Bergasha Date: Wed, 26 Aug 2026 16:44:36 +1000 Subject: [PATCH] fix(board-v2): icon and tiny text fix (#6704) --- packages/ui/index.ts | 29 +++++ .../src/audio-stats/audio-stats-content.tsx | 3 +- packages/widgets/src/bazarr/component.tsx | 3 +- .../src/beszel-system-grid/component.tsx | 25 ++-- .../src/beszel-system-stats/component.tsx | 7 +- .../src/beszel-system-table/component.tsx | 25 ++-- .../widgets/src/beszel/_shared/stats-view.tsx | 5 +- .../src/bookmarks/bookmarks-widget.tsx | 7 +- .../widgets/src/calendar/calender-day.tsx | 9 +- packages/widgets/src/clock/advanced-view.tsx | 3 +- packages/widgets/src/clock/component.tsx | 2 +- .../widgets/src/clock/weather-summary.tsx | 44 ++++--- packages/widgets/src/coolify/resource-row.tsx | 7 +- packages/widgets/src/countdown/component.tsx | 9 +- packages/widgets/src/custom-api/component.tsx | 7 +- .../src/dns-hole/summary/component.tsx | 10 +- packages/widgets/src/docker/component.tsx | 12 +- .../widgets/src/errors/base-component.tsx | 4 +- packages/widgets/src/firewall/component.tsx | 3 +- .../cluster/cluster-health.tsx | 5 +- .../cluster/resource-popover.tsx | 12 +- .../src/health-monitoring/rings/cpu-ring.tsx | 4 +- .../health-monitoring/rings/cpu-temp-ring.tsx | 4 +- .../src/health-monitoring/rings/gpu-ring.tsx | 7 +- .../health-monitoring/rings/memory-ring.tsx | 4 +- .../src/health-monitoring/system-health.tsx | 113 ++++++++++++------ .../src/immich/album-carousel/component.tsx | 3 +- .../src/immich/server-stats/component.tsx | 9 +- .../widgets/src/media-missing/component.tsx | 7 +- .../src/minecraft/server-status/component.tsx | 12 +- .../network-status/variants/wifi-variant.tsx | 3 +- .../network-status/variants/wired-variant.tsx | 3 +- .../network-controller/summary/component.tsx | 22 ++-- .../widgets/src/notifications/component.tsx | 7 +- .../widgets/src/paperless-ngx/component.tsx | 3 +- packages/widgets/src/patchmon/component.tsx | 10 +- .../execute-automation/component.tsx | 3 +- .../src/speedtest-tracker/speed-stat-card.tsx | 3 +- .../system-resources/chart/common-chart.tsx | 5 +- packages/widgets/src/timer/component.tsx | 7 +- packages/widgets/src/traefik/component.tsx | 5 +- .../widgets/src/uptime-kuma/component.tsx | 3 +- packages/widgets/src/weather/advanced.tsx | 23 ++-- .../widgets/src/weather/animated-icon.tsx | 7 +- packages/widgets/src/weather/compact.tsx | 9 +- packages/widgets/src/weather/details.tsx | 19 +-- packages/widgets/src/weather/icon.tsx | 11 +- packages/widgets/src/wud/component.tsx | 3 +- 48 files changed, 368 insertions(+), 172 deletions(-) diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 0933ebe7d..dcb7cf300 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -1,3 +1,4 @@ +import type { CSSProperties } from "react"; import type { Icon123, IconProps } from "@tabler/icons-react"; export * from "./src"; @@ -5,3 +6,31 @@ export * from "./src"; export type TablerIcon = typeof Icon123; export type TablerIconProps = IconProps; + +/** + * Icon sizes tied to Mantine's font-size scale instead of raw pixel + * numbers, so icons stay legible under the board's CSS zoom scaling. + * Pass as the `style` prop (not `size`) - tabler icons convert `size` into + * raw SVG width/height attributes, and CSS `var()` isn't guaranteed to + * resolve there (Firefox especially). `style` is real CSS, so it does. + */ +export const iconSizes = { + xs: { width: "var(--mantine-font-size-xs)", height: "var(--mantine-font-size-xs)", flexShrink: 0 }, + sm: { width: "var(--mantine-font-size-sm)", height: "var(--mantine-font-size-sm)", flexShrink: 0 }, + md: { width: "var(--mantine-font-size-md)", height: "var(--mantine-font-size-md)", flexShrink: 0 }, + lg: { width: "var(--mantine-font-size-lg)", height: "var(--mantine-font-size-lg)", flexShrink: 0 }, + xl: { width: "var(--mantine-font-size-xl)", height: "var(--mantine-font-size-xl)", flexShrink: 0 }, +} as const satisfies Record; + +/** + * For icon/element sizes that don't fit one of `iconSizes`' fixed tokens (e.g. a deliberately + * oversized icon). Same reasoning as `iconSizes`: pass as `style`, not a raw `size` number - a + * bare pixel value never re-scales when the board's zoom level changes, so it renders at a + * fraction of its intended size on any board that isn't at 100% zoom (which is most boards with + * more than a couple of widgets). + */ +export const zoomCompensatedSize = (px: number): CSSProperties => ({ + width: `calc(${px}px * var(--board-canvas-ui-scale, 1))`, + height: `calc(${px}px * var(--board-canvas-ui-scale, 1))`, + flexShrink: 0, +}); diff --git a/packages/widgets/src/audio-stats/audio-stats-content.tsx b/packages/widgets/src/audio-stats/audio-stats-content.tsx index 75958584a..f239961f5 100644 --- a/packages/widgets/src/audio-stats/audio-stats-content.tsx +++ b/packages/widgets/src/audio-stats/audio-stats-content.tsx @@ -5,6 +5,7 @@ import { Text } from "@mantine/core"; import type { AudiobookshelfDashboardData, NavidromeDashboardData } from "@homarr/integrations/types"; import { useI18n } from "@homarr/translation/client"; +import { zoomCompensatedSize } from "@homarr/ui"; import classes from "./component.module.css"; import type { AudioStatsBackend, AudioStatsDisplayOptions } from "./shared"; @@ -66,7 +67,7 @@ export function AudioStatsContent({ > {visibleStats.map(({ optionKey, statKey, value, Icon }) => (
- + {value} {t(statKey as never)}
diff --git a/packages/widgets/src/bazarr/component.tsx b/packages/widgets/src/bazarr/component.tsx index 518901f7b..acca27878 100644 --- a/packages/widgets/src/bazarr/component.tsx +++ b/packages/widgets/src/bazarr/component.tsx @@ -7,6 +7,7 @@ import { IconAlertTriangle, IconBell, IconDeviceTv, IconMovie, IconPlugConnected import { clientApi } from "@homarr/api/client"; import { getIntegrationName } from "@homarr/definitions"; import { useI18n } from "@homarr/translation/client"; +import { zoomCompensatedSize } from "@homarr/ui"; import { WidgetEmptyState } from "../common/empty-state"; import type { WidgetComponentProps } from "../definition"; @@ -122,7 +123,7 @@ export default function BazarrWidget({ return (
- + {typeof value === "string" && value.trim() === "" ? "—" : value} diff --git a/packages/widgets/src/beszel-system-grid/component.tsx b/packages/widgets/src/beszel-system-grid/component.tsx index 3cb6ce591..2a46778a4 100644 --- a/packages/widgets/src/beszel-system-grid/component.tsx +++ b/packages/widgets/src/beszel-system-grid/component.tsx @@ -35,6 +35,7 @@ import { formatBytes } from "@homarr/common"; import { invariantTechnicalLabels } from "@homarr/definitions"; import { useModalAction } from "@homarr/modals"; import { useI18n } from "@homarr/translation/client"; +import { zoomCompensatedSize } from "@homarr/ui"; import classes from "./component.module.css"; @@ -207,7 +208,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={invariantTechnicalLabels.cpu} value={formatPercent(s.cpu)} progress={{ value: s.cpu, color: thresholdColor(s.cpu) }} @@ -222,7 +223,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={t("metric.memory")} value={formatPercent(s.memory)} progress={{ value: s.memory, color: thresholdColor(s.memory) }} @@ -236,7 +237,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ key: "showDisk", render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( - + {t("metric.disk")} @@ -251,7 +252,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={invariantTechnicalLabels.gpu} value={formatPercent(s.gpu)} progress={{ value: s.gpu, color: thresholdColor(s.gpu) }} @@ -266,7 +267,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={t("metric.loadAvg")} value={formatLoadAvg(s.loadAvg)} size={sz} @@ -280,7 +281,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={t("metric.net")} value={formatByteRate(s.netBytes)} size={sz} @@ -294,7 +295,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={t("metric.temp")} value={formatTemp(s.temp, false)} size={sz} @@ -311,7 +312,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ return ( } + icon={} label={t("metric.battery")} value={`${s.battery?.[0] ?? 0}%`} size={sz} @@ -326,7 +327,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, _t: SystemCardProps["t"], sz: SizeConfig, tCommon: SystemCardProps["tCommon"]) => ( } + icon={} label={tCommon("services")} value={String(s.services)} size={sz} @@ -340,7 +341,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={t("metric.uptime")} value={formatUptime(s.uptime)} size={sz} @@ -354,7 +355,7 @@ const metricRenderers: BeszelMetricRenderer[] = [ render: (s: BeszelSystemRow, t: SystemCardProps["t"], sz: SizeConfig) => ( } + icon={} label={t("metric.agent")} value={s.agentVersion} size={sz} @@ -487,7 +488,7 @@ export default function BeszelSystemGridWidget({
- + {t("empty.noSystems")} diff --git a/packages/widgets/src/beszel-system-stats/component.tsx b/packages/widgets/src/beszel-system-stats/component.tsx index ca3604d50..0aca4348f 100644 --- a/packages/widgets/src/beszel-system-stats/component.tsx +++ b/packages/widgets/src/beszel-system-stats/component.tsx @@ -11,6 +11,7 @@ import { constructBoardPermissions } from "@homarr/auth/shared"; import { useOptionalBoard } from "@homarr/boards/context"; import { showErrorNotification } from "@homarr/notifications"; import { useI18n } from "@homarr/translation/client"; +import { iconSizes, zoomCompensatedSize } from "@homarr/ui"; import classes from "./component.module.css"; @@ -147,7 +148,7 @@ export default function BeszelSystemStatsWidget({
- + {tBeszel("empty.noSystems")} @@ -161,7 +162,7 @@ export default function BeszelSystemStatsWidget({ return (
- + {t("error.systemNotFound")} @@ -199,7 +200,7 @@ export default function BeszelSystemStatsWidget({