diff --git a/client/src/Components/design-elements/PageHeader.tsx b/client/src/Components/design-elements/PageHeader.tsx index eccf69b46..28b447bc7 100644 --- a/client/src/Components/design-elements/PageHeader.tsx +++ b/client/src/Components/design-elements/PageHeader.tsx @@ -6,6 +6,7 @@ import { Link as RouterLink } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { useTheme } from "@mui/material/styles"; import { ChevronLeft, HelpCircle, FileText, Code } from "lucide-react"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; import type { ReactNode } from "react"; @@ -145,7 +146,7 @@ export const PageHeader = ({ alignItems: "center", justifyContent: "center", width: 36, - height: 32, + height: CONTROL_HEIGHT, color: theme.palette.text.secondary, borderRight: idx < linkItems.length - 1 diff --git a/client/src/Components/design-elements/SkeletonCard.tsx b/client/src/Components/design-elements/SkeletonCard.tsx index 05f0c462a..66bb0b560 100644 --- a/client/src/Components/design-elements/SkeletonCard.tsx +++ b/client/src/Components/design-elements/SkeletonCard.tsx @@ -1,4 +1,5 @@ import { Box, Stack, useTheme } from "@mui/material"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; interface SkeletonCardProps { width?: number | string | { xs?: number | string; md?: number | string }; @@ -44,7 +45,7 @@ export const SkeletonCard = ({ const blockStyle = { width: 55, - height: 34, + height: CONTROL_HEIGHT, borderRadius: theme.shape.borderRadius, background: theme.palette.secondary.main, border: `1px solid ${theme.palette.divider}`, diff --git a/client/src/Components/design-elements/Tabs.tsx b/client/src/Components/design-elements/Tabs.tsx index 4084b6510..13ccaae77 100644 --- a/client/src/Components/design-elements/Tabs.tsx +++ b/client/src/Components/design-elements/Tabs.tsx @@ -2,6 +2,7 @@ import MuiTabs from "@mui/material/Tabs"; import type { TabsProps } from "@mui/material/Tabs"; import { useTheme } from "@mui/material/styles"; import { typographyLevels } from "@/Utils/Theme/Palette"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; interface CustomTabsProps extends TabsProps {} export const Tabs = (props: CustomTabsProps) => { @@ -9,7 +10,7 @@ export const Tabs = (props: CustomTabsProps) => { return ( { textTransform: "none", fontSize: typographyLevels.m, fontWeight: 500, - minHeight: 34, + minHeight: CONTROL_HEIGHT, padding: theme.spacing(1, 0), paddingBottom: 0, minWidth: "auto", diff --git a/client/src/Components/inputs/Button.tsx b/client/src/Components/inputs/Button.tsx index 4eae654b2..65540b6c4 100644 --- a/client/src/Components/inputs/Button.tsx +++ b/client/src/Components/inputs/Button.tsx @@ -1,7 +1,7 @@ import Button from "@mui/material/Button"; import type { ButtonProps } from "@mui/material/Button"; import { useTheme, darken } from "@mui/material/styles"; -import { HOVER } from "@/Utils/Theme/constants"; +import { CONTROL_HEIGHT, HOVER } from "@/Utils/Theme/constants"; const PALETTE_COLORS = [ "primary", @@ -43,7 +43,11 @@ export const ButtonInput = ({ sx, ...props }: ButtonProps) => { {...props} sx={{ textTransform: "none", - height: 34, + height: CONTROL_HEIGHT, + // MUI's button line-height is 1.75 (22.75px at 13px), which with the + // 6px padding needs 34.75px - already over its own box at 34, and + // clipping at 32. 1.2 leaves room for tall scripts too. + lineHeight: 1.2, fontWeight: 400, borderRadius: 2, whiteSpace: "nowrap", diff --git a/client/src/Components/inputs/ColorPicker.tsx b/client/src/Components/inputs/ColorPicker.tsx index de736c85d..79c4d63fc 100644 --- a/client/src/Components/inputs/ColorPicker.tsx +++ b/client/src/Components/inputs/ColorPicker.tsx @@ -4,6 +4,7 @@ import { typographyLevels } from "@/Utils/Theme/Palette"; import { useTheme } from "@mui/material"; import Stack from "@mui/material/Stack"; import { FieldLabel } from "@/Components/inputs/FieldLabel"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; interface ColorPickerProps extends MuiColorInputProps { fieldLabel?: string; @@ -18,7 +19,7 @@ export const ColorInput = ({ fieldLabel, required, ...props }: ColorPickerProps) sx={{ "& .MuiOutlinedInput-root": { borderRadius: theme.shape.borderRadius, - height: 34, + height: CONTROL_HEIGHT, fontSize: typographyLevels.base, }, "& .MuiOutlinedInput-notchedOutline": { diff --git a/client/src/Components/inputs/DatePicker.tsx b/client/src/Components/inputs/DatePicker.tsx index 74dec5adf..1cb9f7738 100644 --- a/client/src/Components/inputs/DatePicker.tsx +++ b/client/src/Components/inputs/DatePicker.tsx @@ -8,6 +8,7 @@ import { FieldLabel } from "./FieldLabel"; import { Calendar } from "lucide-react"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; interface DatePickerComponentProps extends Omit, "label"> { fieldLabel?: string; @@ -55,7 +56,7 @@ export const DatePickerComponent = ({ flexDirection: "row-reverse", }, "& input": { - minHeight: 34, + minHeight: CONTROL_HEIGHT, p: 0, pl: theme.spacing(3), pr: theme.spacing(5), diff --git a/client/src/Components/inputs/Select.tsx b/client/src/Components/inputs/Select.tsx index f5670887d..8481ad4f4 100644 --- a/client/src/Components/inputs/Select.tsx +++ b/client/src/Components/inputs/Select.tsx @@ -6,6 +6,7 @@ import Stack from "@mui/material/Stack"; import { FieldLabel } from "./FieldLabel"; import { ChevronDown } from "lucide-react"; import Typography from "@mui/material/Typography"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; interface SelectInputProps extends Omit, "label"> { fieldLabel?: string; @@ -71,7 +72,7 @@ const SelectInputInner = ( /> )} sx={{ - height: "34px", + height: `${CONTROL_HEIGHT}px`, "& .MuiSelect-select": { display: "flex", alignItems: "center", diff --git a/client/src/Components/inputs/TextInput.tsx b/client/src/Components/inputs/TextInput.tsx index 8810136ac..392bfccd9 100644 --- a/client/src/Components/inputs/TextInput.tsx +++ b/client/src/Components/inputs/TextInput.tsx @@ -5,7 +5,7 @@ import { typographyLevels } from "@/Utils/Theme/Palette"; import { useTheme } from "@mui/material/styles"; import Stack from "@mui/material/Stack"; import { FieldLabel } from "./FieldLabel"; -import { LAYOUT } from "@/Utils/Theme/constants"; +import { CONTROL_HEIGHT, LAYOUT } from "@/Utils/Theme/constants"; interface TextInputProps extends Omit { fieldLabel?: string; @@ -22,7 +22,7 @@ export const TextInput = forwardRef(function T width: "100%", "& .MuiOutlinedInput-root": { borderRadius: theme.shape.borderRadius, - height: props.multiline ? "auto" : 34, + height: props.multiline ? "auto" : CONTROL_HEIGHT, fontSize: typographyLevels.base, overflow: "hidden", }, diff --git a/client/src/Components/inputs/TimePicker.tsx b/client/src/Components/inputs/TimePicker.tsx index b82df1f15..dfefc2f3c 100644 --- a/client/src/Components/inputs/TimePicker.tsx +++ b/client/src/Components/inputs/TimePicker.tsx @@ -7,6 +7,7 @@ import Typography from "@mui/material/Typography"; import { FieldLabel } from "./FieldLabel"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { LocalizationProvider } from "@mui/x-date-pickers"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; interface TimePickerComponentProps extends Omit, "label"> { fieldLabel?: string; @@ -33,7 +34,7 @@ export const TimePickerComponent = ({ sx: { width: "fit-content", "& input": { - minHeight: 34, + minHeight: CONTROL_HEIGHT, p: 0, px: theme.spacing(5), }, diff --git a/client/src/Components/inputs/ToggleButton.tsx b/client/src/Components/inputs/ToggleButton.tsx index 467c232b6..0319578ae 100644 --- a/client/src/Components/inputs/ToggleButton.tsx +++ b/client/src/Components/inputs/ToggleButton.tsx @@ -4,6 +4,7 @@ import type { ToggleButtonProps } from "@mui/material/ToggleButton"; import type { ToggleButtonGroupProps } from "@mui/material/ToggleButtonGroup"; import { useTheme } from "@mui/material/styles"; import { alpha } from "@mui/material/styles"; +import { CONTROL_HEIGHT } from "@/Utils/Theme/constants"; export const ToggleButtonInput = ({ sx, ...props }: ToggleButtonProps) => { const theme = useTheme(); @@ -20,7 +21,7 @@ export const ToggleButtonInput = ({ sx, ...props }: ToggleButtonProps) => { sx={{ px: 8, textTransform: "none", - height: 34, + height: CONTROL_HEIGHT, fontWeight: 400, whiteSpace: "nowrap", textOverflow: "ellipsis", diff --git a/client/src/Utils/Theme/Theme.ts b/client/src/Utils/Theme/Theme.ts index bda2c3806..a480686a7 100644 --- a/client/src/Utils/Theme/Theme.ts +++ b/client/src/Utils/Theme/Theme.ts @@ -202,7 +202,24 @@ export const theme = (mode: string, palette: any) => // jumping to near-black. Focus: the brand, at 2px. MuiOutlinedInput: { styleOverrides: { + // MUI pads the inner input 8.5px top and bottom, which with an + // 18.7px line-height needs 35.7px - taller than the control + // itself, so the text was being clipped by overflow:hidden. + input: { + paddingTop: 0, + paddingBottom: 0, + height: "100%", + }, root: ({ theme }) => ({ + // The Select renders a div rather than an input, so it needs + // the same treatment to sit inside the control height. + "& .MuiSelect-select": { + paddingTop: 0, + paddingBottom: 0, + display: "flex", + alignItems: "center", + minHeight: "unset", + }, "& .MuiOutlinedInput-notchedOutline": { borderColor: theme.palette.divider, transition: "border-color 0.15s ease", diff --git a/client/src/Utils/Theme/constants.ts b/client/src/Utils/Theme/constants.ts index 70ddcd407..1d5ed4991 100644 --- a/client/src/Utils/Theme/constants.ts +++ b/client/src/Utils/Theme/constants.ts @@ -18,6 +18,13 @@ export const LAYOUT = { XXL: 16, } as const; +/** + * Shared height for anything that can sit in a row with something else - + * buttons, inputs, selects, pickers, toggles, tabs, icon buttons. One number so + * they share a baseline instead of drifting 2px apart. + */ +export const CONTROL_HEIGHT = 32; + export const HOVER = { DARKEN: 0.06, // This is a coefficient for darkening function ROW: 0.025, // Overlay alpha for hoverable rows and cards