feat(client): unify switch, radio and slider styling via theme overrides

- Switch: 36x20 contained pill with 6px right padding so labels keep their distance even inside Stacks
- Radio: theme-level override, 20px ring with a fixed-size inner dot — checked state no longer animates a scale change that pushed labels around
- Slider: white thumb with primary-colour ring, 4px track, soft hover/active glow
- Drop the custom RadioInput icon overrides that bypassed the theme and caused the size jiggle
This commit is contained in:
gorkem-bwl authored and Alex Holliday committed 2026-04-27 10:52:23 -07:00
1 parent 8e004c42af
commit 2ae19d7e1b
2 files changed
+108 -41

No files matched your search

+1 -41
View File
@@ -1,52 +1,13 @@
import Radio from "@mui/material/Radio";
import type { RadioProps } from "@mui/material/Radio";
import { useTheme } from "@mui/material/styles";
import { Circle, CircleDot } from "lucide-react";
import FormControlLabel from "@mui/material/FormControlLabel";
import Typography from "@mui/material/Typography";
interface RadioInputProps extends RadioProps {}
export const RadioInput = ({ ...props }: RadioInputProps) => {
const theme = useTheme();
return (
<Radio
{...props}
icon={
<Circle
size={16}
strokeWidth={1.5}
/>
}
checkedIcon={
<CircleDot
size={14}
strokeWidth={1.5}
/>
}
sx={{
padding: 0,
mt: theme.spacing(0.5),
color: theme.palette.text.secondary,
"&.Mui-checked": {
color: theme.palette.primary.main,
"& svg circle": {
fill: theme.palette.primary.main,
},
},
"& .MuiSvgIcon-root": {
fontSize: 16,
},
"& svg": {
stroke: "currentColor",
},
"& svg path, & svg line, & svg polyline, & svg rect, & svg circle": {
stroke: "currentColor",
fill: "none",
},
}}
/>
);
return <Radio {...props} />;
};
export const RadioWithDescription = ({
@@ -79,7 +40,6 @@ export const RadioWithDescription = ({
backgroundColor: theme.palette.background.paper,
},
"& .MuiButtonBase-root": {
p: 0,
mr: theme.spacing(6),
},
}}
+107
View File
@@ -151,6 +151,113 @@ export const theme = (mode: string, palette: any) =>
},
},
},
MuiSwitch: {
defaultProps: { disableRipple: true },
styleOverrides: {
root: {
width: 36,
height: 20,
padding: "0 6px 0 0",
boxSizing: "content-box",
overflow: "visible",
},
switchBase: ({ theme }) => ({
padding: 2,
"&.Mui-checked": {
transform: "translateX(16px)",
color: "#fff",
"& + .MuiSwitch-track": {
backgroundColor: theme.palette.primary.main,
opacity: 1,
border: 0,
},
},
}),
thumb: {
width: 16,
height: 16,
boxShadow: "0 1px 2px rgba(16, 24, 40, 0.1)",
},
track: ({ theme }) => ({
borderRadius: 999,
backgroundColor:
theme.palette.mode === "dark"
? "rgba(255,255,255,0.16)"
: theme.palette.action.disabledBackground,
opacity: 1,
}),
},
},
MuiRadio: {
defaultProps: { disableRipple: true },
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(2),
color: theme.palette.divider,
"& .MuiSvgIcon-root": {
fontSize: 20,
transition: "none",
},
"&.Mui-checked": {
color: theme.palette.primary.main,
},
"&.Mui-checked .MuiSvgIcon-root:last-of-type": {
transform: "scale(0.6)",
},
}),
},
},
MuiSlider: {
styleOverrides: {
root: ({ theme }) => ({
height: 4,
padding: "13px 0",
color: theme.palette.primary.main,
}),
rail: ({ theme }) => ({
height: 4,
borderRadius: 999,
opacity: 1,
backgroundColor:
theme.palette.mode === "dark"
? "rgba(255,255,255,0.16)"
: theme.palette.action.disabledBackground,
}),
track: {
height: 4,
borderRadius: 999,
border: "none",
},
thumb: ({ theme }) => ({
width: 18,
height: 18,
backgroundColor: theme.palette.background.paper,
border: `2px solid ${theme.palette.primary.main}`,
boxShadow: "0 1px 2px rgba(16, 24, 40, 0.1)",
"&:hover, &.Mui-focusVisible": {
boxShadow: `0 0 0 6px ${theme.palette.primary.main}1f`,
},
"&.Mui-active": {
boxShadow: `0 0 0 8px ${theme.palette.primary.main}29`,
},
"&::before": { display: "none" },
}),
mark: ({ theme }) => ({
width: 2,
height: 2,
borderRadius: 999,
backgroundColor: theme.palette.text.disabled,
opacity: 0.5,
}),
markActive: ({ theme }) => ({
backgroundColor: theme.palette.primary.main,
opacity: 0.5,
}),
},
},
},
shape: {
borderRadius: 2,