diff --git a/src/admin/components/dashboard/DashActivityFeed.tsx b/src/admin/components/dashboard/DashActivityFeed.tsx index b677b9a..01bce67 100644 --- a/src/admin/components/dashboard/DashActivityFeed.tsx +++ b/src/admin/components/dashboard/DashActivityFeed.tsx @@ -2,6 +2,7 @@ import { Link as RouterLink } from "react-router-dom"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import { Card, Button, EmptyState } from "../../ui"; +import { iconBadgeSx, type IconBadgeColor } from "../../theme"; import { ENTITY_TYPE_LABELS, getActivityIconClass, @@ -159,17 +160,20 @@ export default function DashActivityFeed({ }} > {getActivityIcon(act.action)} diff --git a/src/admin/components/dashboard/DashProfile.tsx b/src/admin/components/dashboard/DashProfile.tsx index f0ef4e4..e8df687 100644 --- a/src/admin/components/dashboard/DashProfile.tsx +++ b/src/admin/components/dashboard/DashProfile.tsx @@ -10,6 +10,7 @@ import DialogActions from "@mui/material/DialogActions"; import { useAuth } from "../../context/AuthContext"; import { useAlert } from "../../context/AlertContext"; import apiFetch from "../../utils/api"; +import { iconBadgeSx } from "../../theme"; import { Card, Button, Modal, Field, TextField } from "../../ui"; import useDialogScrollLock from "../../ui/useDialogScrollLock"; @@ -251,17 +252,18 @@ export default function DashProfile({ > .main` fill with a WHITE + * glyph, darkened in dark mode (FILLED_DARK_BG) so white stays legible on the + * otherwise-bright success/warning/info/error fills. `default` = neutral grey. + * Single source of truth for every labelled icon badge — use in an sx array + * next to the tile's size/shape so they can never drift apart: + * sx={[{ width: 40, height: 40, borderRadius: 2, display: "flex", … }, iconBadgeSx(color)]} + */ +export function iconBadgeSx(color: IconBadgeColor) { + const bg = color === "default" ? "grey.600" : `${color}.main`; + const darkBg = (FILLED_DARK_BG as Record)[color]?.bg; + return (theme: Theme) => ({ + bgcolor: bg, + color: "common.white", + ...(darkBg ? theme.applyStyles("dark", { backgroundColor: darkBg }) : {}), + }); +} diff --git a/src/admin/ui/StatCard.tsx b/src/admin/ui/StatCard.tsx index ff5d55a..1b608ef 100644 --- a/src/admin/ui/StatCard.tsx +++ b/src/admin/ui/StatCard.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from "react"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import Card from "./Card"; -import { FILLED_DARK_BG } from "../theme"; +import { iconBadgeSx } from "../theme"; export type StatCardColor = | "default" @@ -31,33 +31,23 @@ export default function StatCard({ color = "default", footer, }: StatCardProps) { - // Solid tile + white glyph: high-contrast in both themes (the old light tint - // left the icon dark/low-visibility, esp. for the default variant). In dark - // mode the bright .main fill is darkened (same rule as filled buttons/chips) - // so the white glyph stays legible on success/warning/info/error tiles. - const iconBg = color === "default" ? "grey.600" : `${color}.main`; - const iconDarkBg = (FILLED_DARK_BG as Record)[color] - ?.bg; - return ( {icon && ( ({ - display: "flex", - alignItems: "center", - justifyContent: "center", - width: 40, - height: 40, - borderRadius: 2, - bgcolor: iconBg, - color: "common.white", - flexShrink: 0, - ...(iconDarkBg - ? theme.applyStyles("dark", { backgroundColor: iconDarkBg }) - : {}), - })} + sx={[ + { + display: "flex", + alignItems: "center", + justifyContent: "center", + width: 40, + height: 40, + borderRadius: 2, + flexShrink: 0, + }, + iconBadgeSx(color), + ]} > {icon}