311 lines
8.5 KiB
TypeScript
311 lines
8.5 KiB
TypeScript
import { alpha, createTheme, darken, lighten } from "@mui/material/styles";
|
|
|
|
type PaletteLike = Record<string, any>;
|
|
|
|
function buildPrimary(main: string) {
|
|
return {
|
|
lighter: lighten(main, 0.82),
|
|
light: lighten(main, 0.56),
|
|
main,
|
|
dark: darken(main, 0.22),
|
|
darker: darken(main, 0.72),
|
|
};
|
|
}
|
|
|
|
function buildLightPalette(accentColor: string): PaletteLike {
|
|
const textPrimary = "#1B1B1F";
|
|
const textSecondary = "#46464F";
|
|
|
|
const secondaryMain = "#5A5C78";
|
|
const divider = "#EFEDF4";
|
|
const background = "#FFFFFF";
|
|
|
|
const disabled = "#777680";
|
|
const disabledBackground = "#E4E1E6";
|
|
|
|
return {
|
|
primary: buildPrimary(accentColor || "#606BDF"),
|
|
secondary: {
|
|
lighter: "#E0E0FF",
|
|
light: "#C3C4E4",
|
|
main: secondaryMain,
|
|
dark: "#43455F",
|
|
darker: "#171A31",
|
|
},
|
|
error: {
|
|
lighter: "#FFEDEA",
|
|
light: "#FFDAD6",
|
|
main: "#DE3730",
|
|
dark: "#BA1A1A",
|
|
darker: "#690005",
|
|
},
|
|
warning: {
|
|
lighter: "#FFEEE1",
|
|
light: "#FFDCBE",
|
|
main: "#AE6600",
|
|
dark: "#8B5000",
|
|
darker: "#4A2800",
|
|
},
|
|
success: {
|
|
lighter: "#C8FFC0",
|
|
light: "#B6F2AF",
|
|
main: "#22892F",
|
|
dark: "#006E1C",
|
|
darker: "#00390A",
|
|
},
|
|
info: {
|
|
lighter: "#D4F7FF",
|
|
light: "#A1EFFF",
|
|
main: "#008394",
|
|
dark: "#006876",
|
|
darker: "#00363E",
|
|
},
|
|
grey: {
|
|
50: "#FBF8FF",
|
|
100: "#F5F2FA",
|
|
200: divider,
|
|
300: "#EAE7EF",
|
|
400: disabledBackground,
|
|
500: "#DBD9E0",
|
|
600: "#C7C5D0",
|
|
700: disabled,
|
|
800: textSecondary,
|
|
900: textPrimary,
|
|
},
|
|
text: {
|
|
primary: textPrimary,
|
|
secondary: textSecondary,
|
|
disabled,
|
|
},
|
|
divider,
|
|
background: { default: background, paper: background },
|
|
action: {
|
|
hover: alpha(secondaryMain, 0.05),
|
|
disabled: alpha(disabled, 0.6),
|
|
disabledBackground: alpha(disabledBackground, 0.9),
|
|
},
|
|
};
|
|
}
|
|
|
|
function buildDarkPalette(accentColor: string): PaletteLike {
|
|
const bg = "#0B0B0E";
|
|
const paper = "#111116";
|
|
const divider = alpha("#FFFFFF", 0.10);
|
|
const textPrimary = "#EAEAF2";
|
|
const textSecondary = alpha(textPrimary, 0.72);
|
|
|
|
const secondaryMain = "#A4A6C8";
|
|
const disabled = alpha(textPrimary, 0.5);
|
|
const disabledBackground = alpha("#FFFFFF", 0.08);
|
|
|
|
return {
|
|
primary: buildPrimary(accentColor || "#606BDF"),
|
|
secondary: {
|
|
lighter: alpha(secondaryMain, 0.22),
|
|
light: alpha(secondaryMain, 0.14),
|
|
main: secondaryMain,
|
|
dark: alpha(secondaryMain, 0.8),
|
|
darker: alpha(secondaryMain, 0.95),
|
|
},
|
|
error: {
|
|
lighter: alpha("#DE3730", 0.18),
|
|
light: alpha("#DE3730", 0.12),
|
|
main: "#FF5A52",
|
|
dark: "#FF3B33",
|
|
darker: "#FFC2BE",
|
|
},
|
|
warning: {
|
|
lighter: alpha("#AE6600", 0.18),
|
|
light: alpha("#AE6600", 0.12),
|
|
main: "#FFB14A",
|
|
dark: "#FF9A1F",
|
|
darker: "#FFE1B8",
|
|
},
|
|
success: {
|
|
lighter: alpha("#22892F", 0.18),
|
|
light: alpha("#22892F", 0.12),
|
|
main: "#4ADE80",
|
|
dark: "#22C55E",
|
|
darker: "#BBF7D0",
|
|
},
|
|
info: {
|
|
lighter: alpha("#008394", 0.18),
|
|
light: alpha("#008394", 0.12),
|
|
main: "#22D3EE",
|
|
dark: "#06B6D4",
|
|
darker: "#BAF3FF",
|
|
},
|
|
grey: {
|
|
50: bg,
|
|
100: "#0F0F13",
|
|
200: "#15151B",
|
|
300: "#1C1C24",
|
|
400: "#23232D",
|
|
500: "#2D2D3A",
|
|
600: "#3A3A4A",
|
|
700: "#4A4A5D",
|
|
800: "#B8B8C6",
|
|
900: textPrimary,
|
|
},
|
|
text: {
|
|
primary: textPrimary,
|
|
secondary: textSecondary,
|
|
disabled,
|
|
},
|
|
divider,
|
|
background: { default: bg, paper },
|
|
action: {
|
|
hover: alpha("#FFFFFF", 0.06),
|
|
disabled: alpha("#FFFFFF", 0.5),
|
|
disabledBackground,
|
|
},
|
|
};
|
|
}
|
|
|
|
function buildCustomShadows(palette: any) {
|
|
const shadowColor = palette.text.primary;
|
|
const primaryColor = palette.primary.main;
|
|
return {
|
|
button: `0px 0.711px 1.422px 0px ${alpha(shadowColor, 0.05)}`,
|
|
section: `0px 1px 2px 0px ${alpha(shadowColor, 0.07)}`,
|
|
tooltip: `0px 12px 16px -4px ${alpha(shadowColor, 0.08)}, 0px 4px 6px -2px ${alpha(shadowColor, 0.03)}`,
|
|
focus: `0px 0px 0px 3px ${alpha(primaryColor, 0.2)}`,
|
|
};
|
|
}
|
|
|
|
function buildTypography() {
|
|
return {
|
|
fontFamily: "'Archivo', system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif",
|
|
letterSpacing: 0,
|
|
h1: { fontWeight: 500, fontSize: 36, lineHeight: "40px" },
|
|
h2: { fontWeight: 500, fontSize: 30, lineHeight: "34px" },
|
|
h3: { fontWeight: 500, fontSize: 26, lineHeight: "30px" },
|
|
h4: { fontWeight: 500, fontSize: 22, lineHeight: "26px" },
|
|
h5: { fontWeight: 500, fontSize: 18, lineHeight: "22px" },
|
|
h6: { fontWeight: 500, fontSize: 16, lineHeight: "20px" },
|
|
subtitle1: { fontWeight: 500, fontSize: 14, lineHeight: "18px" },
|
|
subtitle2: { fontWeight: 500, fontSize: 13, lineHeight: "17px" },
|
|
body1: { fontWeight: 400, fontSize: 14, lineHeight: "18px" },
|
|
body2: { fontWeight: 400, fontSize: 13, lineHeight: "17px" },
|
|
caption: { fontWeight: 400, fontSize: 12, lineHeight: "16px", letterSpacing: 0 },
|
|
overline: { fontWeight: 600, fontSize: 11, lineHeight: "14px", letterSpacing: "0.08em", textTransform: "uppercase" as const },
|
|
// Saasable uses caption1; keep as a custom variant for internal usage if needed.
|
|
caption1: { fontWeight: 500, fontSize: 12, lineHeight: "16px", letterSpacing: 0 },
|
|
button: { textTransform: "capitalize" as const },
|
|
};
|
|
}
|
|
|
|
export const getTheme = (_mode: "light" | "dark", accentColor: string) => {
|
|
const lightPalette = buildLightPalette(accentColor);
|
|
const darkPalette = buildDarkPalette(accentColor);
|
|
|
|
const theme = createTheme({
|
|
breakpoints: {
|
|
values: {
|
|
xs: 0,
|
|
sm: 768,
|
|
md: 1024,
|
|
lg: 1266,
|
|
xl: 1440,
|
|
},
|
|
},
|
|
cssVariables: {
|
|
cssVarPrefix: "",
|
|
colorSchemeSelector: "data-color-scheme",
|
|
},
|
|
colorSchemes: {
|
|
light: { palette: lightPalette, customShadows: buildCustomShadows(lightPalette) },
|
|
dark: { palette: darkPalette, customShadows: buildCustomShadows(darkPalette) },
|
|
},
|
|
shape: { borderRadius: 8 },
|
|
typography: buildTypography() as any,
|
|
} as any) as any;
|
|
|
|
theme.components = {
|
|
MuiCssBaseline: {
|
|
styleOverrides: {
|
|
body: {
|
|
WebkitFontSmoothing: "antialiased",
|
|
MozOsxFontSmoothing: "grayscale",
|
|
},
|
|
},
|
|
},
|
|
MuiPaper: {
|
|
defaultProps: { elevation: 0 },
|
|
styleOverrides: {
|
|
root: ({ theme }: any) => ({
|
|
border: `1px solid ${theme.vars.palette.divider}`,
|
|
boxShadow: "none",
|
|
backgroundImage: "none",
|
|
}),
|
|
},
|
|
},
|
|
MuiCard: {
|
|
defaultProps: { elevation: 0 },
|
|
styleOverrides: {
|
|
root: ({ theme }: any) => ({
|
|
border: `1px solid ${theme.vars.palette.divider}`,
|
|
borderRadius: 12,
|
|
backgroundImage: "none",
|
|
boxShadow: theme.vars.customShadows.section,
|
|
}),
|
|
},
|
|
},
|
|
MuiButton: {
|
|
defaultProps: { disableFocusRipple: true },
|
|
styleOverrides: {
|
|
root: ({ theme }: any) => ({
|
|
borderRadius: 8,
|
|
boxShadow: theme.vars.customShadows.button,
|
|
"&:hover": { boxShadow: theme.vars.customShadows.button },
|
|
}),
|
|
},
|
|
},
|
|
MuiOutlinedInput: {
|
|
defaultProps: { size: "small" },
|
|
styleOverrides: {
|
|
root: ({ theme }: any) => ({
|
|
borderRadius: 8,
|
|
boxShadow: theme.vars.customShadows.button,
|
|
background: theme.vars.palette.background.default,
|
|
paddingLeft: 10,
|
|
paddingRight: 10,
|
|
"&.Mui-disabled": {
|
|
cursor: "not-allowed",
|
|
input: { cursor: "not-allowed" },
|
|
"& .MuiOutlinedInput-notchedOutline": { borderColor: theme.vars.palette.divider },
|
|
},
|
|
}),
|
|
notchedOutline: ({ theme }: any) => ({ borderColor: theme.vars.palette.divider }),
|
|
multiline: { padding: 10 },
|
|
input: { paddingLeft: 0, paddingRight: 0 },
|
|
},
|
|
},
|
|
MuiListItemButton: {
|
|
styleOverrides: {
|
|
root: ({ theme }: any) => ({
|
|
borderRadius: 8,
|
|
"&.Mui-selected": {
|
|
backgroundColor: theme.vars.palette.action.hover,
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
MuiTableCell: {
|
|
styleOverrides: {
|
|
head: ({ theme }: any) => ({
|
|
fontWeight: 600,
|
|
color: theme.vars.palette.text.primary,
|
|
background: theme.vars.palette.grey[100],
|
|
}),
|
|
root: {
|
|
paddingTop: 10,
|
|
paddingBottom: 10,
|
|
},
|
|
},
|
|
},
|
|
} as any;
|
|
|
|
return theme;
|
|
};
|