From 9f53b5d68c57f8b6aa7682e5ceae127b0d8f8530 Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Thu, 26 Aug 2021 15:43:15 +1000 Subject: [PATCH] rename theme --- www/src/theme/colors.ts | 178 +++++++++++ www/src/theme/components.tsx | 575 +++++++++++++++++++++++++++++++++++ www/src/theme/palette.ts | 148 +++++++++ www/src/theme/typography.ts | 146 +++++++++ 4 files changed, 1047 insertions(+) create mode 100644 www/src/theme/colors.ts create mode 100644 www/src/theme/components.tsx create mode 100644 www/src/theme/palette.ts create mode 100644 www/src/theme/typography.ts diff --git a/www/src/theme/colors.ts b/www/src/theme/colors.ts new file mode 100644 index 00000000..767a2043 --- /dev/null +++ b/www/src/theme/colors.ts @@ -0,0 +1,178 @@ +import { ThemeOptions } from "@material-ui/core/styles"; +import { Shadows } from "@material-ui/core/styles/shadows"; + +import { colord, extend } from "colord"; +import lchPlugin from "colord/plugins/lch"; +extend([lchPlugin]); + +declare module "@material-ui/core/styles" { + interface Palette { + input: string; + } + interface PaletteOptions { + input?: string; + } +} + +export const PRIMARY = "#420AFD"; +// export const PRIMARY = "#ED4747"; +// export const PRIMARY = "#FA0"; +// export const PRIMARY = "#0F0"; +// export const PRIMARY = "#F15A29"; +// export const PRIMARY = "#c4492c"; +// export const PRIMARY = "#0070EB"; +// export const PRIMARY = "#015FB8"; +// export const PRIMARY = "#E8016D"; +export const ERROR = "#B00020"; // https://material.io/design/color/dark-theme.html#ui-application + +export const DARK_PRIMARY = "#B0B6FD"; // l: 75, c: 65, h: 275 + +export const colorsLight = ( + _primary: Parameters[0] = PRIMARY +): ThemeOptions => { + const primary = colord(_primary); + const h = primary.toLch().h; + const secondary = colord({ l: 10, c: 20, h }); + const secondaryDark = colord({ l: 0, c: 0, h }); + const bgDefault = colord({ l: 98, c: 2, h }); + const shadowBase = colord({ l: 0, c: 20, h }); + const tooltip = shadowBase.alpha(0.8); + + return { + palette: { + primary: { main: primary.toHslString() }, + secondary: { + main: secondary.toHslString(), + dark: secondaryDark.toHslString(), + }, + error: { main: ERROR }, + success: { light: "#34c759", main: "#00802e", dark: "#105e24" }, + background: { default: bgDefault.toHslString() }, + divider: shadowBase.alpha(0.12).toRgbString(), // Using hsl string breaks table borders + input: "#fff", + }, + shadows: new Array(25).fill(undefined).map((_, i) => { + // Based on https://tailwindcss.com/docs/box-shadow + // with additional “outline” shadow + // and bigger shadow from https://github.com/outline/outline/blob/37fd7ec97a496094077a59f4d10fa0081516e3ef/shared/theme.js#L148 + + if (i === 0) return "none"; + + if (i < 4) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.04).toHslString()}, 0 1px 3px 0 ${shadowBase.alpha(0.1).toHslString()}, 0 1px 2px 0 ${shadowBase.alpha(0.06).toHslString()}`; + + if (i < 8) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.05).toHslString()}, 0 4px 6px -1px ${shadowBase.alpha(0.1).toHslString()}, 0 2px 4px ${shadowBase.alpha(0.06).toHslString()}`; + + if (i < 16) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.06).toHslString()}, 0 10px 15px -3px ${shadowBase.alpha(0.1).toHslString()}, 0 4px 6px ${shadowBase.alpha(0.05).toHslString()}, 0 30px 40px ${shadowBase.alpha(0.05).toHslString()}`; + + if (i < 24) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.08).toHslString()}, 0 20px 25px -5px ${shadowBase.alpha(0.1).toHslString()}, 0 10px 10px ${shadowBase.alpha(0.04).toHslString()}, 0 40px 60px ${shadowBase.alpha(0.06).toHslString()}`; + + else + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.08).toHslString()}, 0 25px 50px -12px ${shadowBase.alpha(0.25).toHslString()}, 0 50px 80px ${shadowBase.alpha(0.06).toHslString()}`; + }) as Shadows, + + components: { + MuiBackdrop: { + styleOverrides: { + root: { + backgroundColor: colord({ l: 0, c: 1, h }).alpha(0.6).toHslString(), + }, + invisible: { backgroundColor: "transparent" }, + }, + }, + MuiTooltip: { + styleOverrides: { + tooltip: { backgroundColor: tooltip.toHslString() }, + }, + }, + MuiSlider: { + styleOverrides: { + valueLabel: { backgroundColor: secondary.toHslString() }, + }, + }, + }, + }; +}; + +export const colorsDark = ( + _primary: Parameters[0] = DARK_PRIMARY +): ThemeOptions => { + const primary = colord(_primary); + const h = primary.toLch().h; + const secondary = colord({ l: 96, c: 1, h }); + const bgDefault = colord({ l: 5, c: 2, h }); + const bgPaper = colord({ l: 5, c: 2, h }); + const shadowBase = colord({ l: 0, c: 2, h }); + + return { + palette: { + mode: "dark", + primary: { main: primary.toHslString() }, + secondary: { main: secondary.toHslString() }, + background: { + default: bgDefault.toHslString(), + paper: bgPaper.toHslString(), + }, + error: { + main: colord({ + l: 65, + c: 72, + h: colord(ERROR).toLch().h, + }).toHslString(), + }, + action: { + hover: "rgba(255, 255, 255, 0.08)", + hoverOpacity: 0.08, + }, + // success: { light: "#34c759" }, + // input: "rgba(255, 255, 255, 0.24)", + input: "rgba(255, 255, 255, 0.06)", + }, + shadows: new Array(25).fill(undefined).map((_, i) => { + // Based on https://tailwindcss.com/docs/box-shadow + // with additional “outline” shadow + // and bigger shadow from https://github.com/outline/outline/blob/37fd7ec97a496094077a59f4d10fa0081516e3ef/shared/theme.js#L148 + + if (i === 0) return "none"; + + if (i < 4) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.04 * 4).toHslString()}, 0 1px 3px 0 ${shadowBase.alpha(0.1 * 4).toHslString()}, 0 1px 2px 0 ${shadowBase.alpha(0.06 * 4).toHslString()}`; + + if (i < 8) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.05 * 4).toHslString()}, 0 4px 6px -1px ${shadowBase.alpha(0.1 * 4).toHslString()}, 0 2px 4px ${shadowBase.alpha(0.06 * 4).toHslString()}`; + + if (i < 16) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.06 * 4).toHslString()}, 0 10px 15px -3px ${shadowBase.alpha(0.1 * 4).toHslString()}, 0 4px 6px ${shadowBase.alpha(0.05 * 4).toHslString()}, 0 30px 40px ${shadowBase.alpha(0.05 * 4).toHslString()}`; + + if (i < 24) + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.08 * 4).toHslString()}, 0 20px 25px -5px ${shadowBase.alpha(0.1 * 4).toHslString()}, 0 10px 10px ${shadowBase.alpha(0.04 * 4).toHslString()}, 0 40px 60px ${shadowBase.alpha(0.06 * 4).toHslString()}`; + + else + // prettier-ignore + return `0 0 0 1px ${shadowBase.alpha(0.08 * 4).toHslString()}, 0 25px 50px -12px ${shadowBase.alpha(0.25 * 4).toHslString()}, 0 50px 80px ${shadowBase.alpha(0.06 * 4).toHslString()}`; + }) as Shadows, + + components: { + MuiBackdrop: { + styleOverrides: { + root: { + backgroundColor: colord({ l: 0, c: 1, h }).alpha(0.6).toHslString(), + }, + invisible: { backgroundColor: "transparent" }, + }, + }, + }, + }; +}; diff --git a/www/src/theme/components.tsx b/www/src/theme/components.tsx new file mode 100644 index 00000000..954cd5e6 --- /dev/null +++ b/www/src/theme/components.tsx @@ -0,0 +1,575 @@ +import { Theme, ThemeOptions } from "@material-ui/core/styles"; + +import { colord, extend } from "colord"; +import mixPlugin from "colord/plugins/mix"; +extend([mixPlugin]); + +declare module "@material-ui/core/styles/createTransitions" { + interface Easing { + strong: string; + } +} + +export const components = (theme: Theme): ThemeOptions => { + const colorDividerHalf = colord(theme.palette.divider) + .alpha(colord(theme.palette.divider).alpha() / 2) + .toHslString(); + + const buttonPrimaryHover = colord(theme.palette.primary.main) + .mix(theme.palette.primary.contrastText, 0.12) + .alpha(1) + .toHslString(); + const buttonSecondaryHover = colord(theme.palette.secondary.main) + .mix(theme.palette.secondary.contrastText, 0.12) + .alpha(1) + .toHslString(); + + const fabBackgroundColor = + theme.palette.mode === "dark" + ? colord(theme.palette.background.default) + .mix(theme.palette.action.selected, 0.24) + .alpha(1) + .toHslString() + : theme.palette.background.paper; + + const transitionEasingStrong = "cubic-bezier(0.85, 0, 0, 1)"; // https://docs.microsoft.com/en-us/windows/apps/design/signature-experiences/motion#animation-properties + + return { + transitions: { + easing: { strong: transitionEasingStrong }, + }, + components: { + MuiContainer: { + defaultProps: { maxWidth: "xl" }, + }, + MuiPaper: { + styleOverrides: { + rounded: { borderRadius: (theme.shape.borderRadius as number) * 2 }, + }, + }, + MuiDialog: { + styleOverrides: { + paper: { borderRadius: (theme.shape.borderRadius as number) * 2 }, + }, + }, + MuiSnackbar: { + styleOverrides: { + root: { + left: `calc(env(safe-area-inset-left) + ${theme.spacing(1)})`, + bottom: `calc(env(safe-area-inset-bottom) + ${theme.spacing(1)})`, + }, + }, + }, + MuiSnackbarContent: { + styleOverrides: { + root: { + borderRadius: (theme.shape.borderRadius as number) * 2, + backgroundColor: theme.palette.secondary.main, + }, + }, + }, + + MuiTextField: { + defaultProps: { + variant: "filled", + size: "small", + }, + }, + MuiInputBase: { + styleOverrides: { + inputSizeSmall: theme.typography.body2, + }, + }, + MuiFilledInput: { + styleOverrides: { + root: { + backgroundColor: theme.palette.input, + "&:hover:not(.Mui-disabled), &:focus, &.Mui-focused": { + backgroundColor: theme.palette.input, + }, + + boxShadow: `0 0 0 1px ${ + theme.palette.mode === "dark" + ? colorDividerHalf + : theme.palette.divider + } inset`, + borderRadius: theme.shape.borderRadius, + + overflow: "hidden", + "&::before": { + borderRadius: theme.shape.borderRadius, + height: (theme.shape.borderRadius as number) * 2, + + borderColor: theme.palette.text.disabled, + }, + "&.Mui-focused::before, &.Mui-focused:hover::before": { + borderColor: theme.palette.primary.main, + }, + + "&.Mui-disabled": { + backgroundColor: + theme.palette.mode === "dark" + ? "transparent" + : theme.palette.action.disabledBackground, + }, + }, + colorSecondary: { + "&.Mui-focused::before, &.Mui-focused:hover::before": { + borderColor: theme.palette.secondary.main, + }, + }, + inputSizeSmall: { + padding: "6px 12px", + height: 20, + }, + multiline: { padding: 0 }, + }, + }, + MuiInputLabel: { + defaultProps: { + shrink: true, + }, + styleOverrides: { + filled: { + "&, &.MuiInputLabel-shrink": { transform: "none" }, + + position: "static", + padding: "2px 12px", + pointerEvents: "auto", + + maxWidth: "none", + overflow: "visible", + whiteSpace: "normal", + + ...theme.typography.caption, + fontWeight: 500, + }, + }, + }, + + MuiSelect: { + styleOverrides: { + select: { + // If Select option is a MenuItem, don’t add spacing + "& > *": { + margin: 0, + paddingTop: 0, + paddingBottom: 0, + }, + }, + icon: { + transition: theme.transitions.create("transform", { + duration: theme.transitions.duration.short, + }), + }, + iconOpen: { transform: "rotate(180deg)" }, + }, + }, + MuiListItem: { + styleOverrides: { + root: { + width: `calc(100% - ${theme.spacing(2)})`, + margin: theme.spacing(0.5, 1), + padding: theme.spacing(0.5, 1), + borderRadius: theme.shape.borderRadius, + }, + }, + defaultProps: { dense: true }, + }, + MuiMenu: { + styleOverrides: { + list: { padding: theme.spacing(0.5, 0) }, + }, + }, + MuiMenuItem: { + styleOverrides: { + root: { + width: `calc(100% - ${theme.spacing(1)})`, + margin: theme.spacing(0, 0.5), + padding: theme.spacing(0.75, 0.75, 0.75, 1.5), + minHeight: 32, + borderRadius: theme.shape.borderRadius, + + "&.Mui-selected": { + backgroundColor: theme.palette.action.selected, + "&::before": { + content: "''", + display: "block", + position: "absolute", + top: (32 - 16) / 2, + left: 0, + + width: 3, + height: 16, + borderRadius: 1.5, + backgroundColor: theme.palette.primary.main, + }, + }, + + "& + .MuiDivider-root": { + marginTop: theme.spacing(0.5), + marginBottom: theme.spacing(0.5), + }, + }, + }, + defaultProps: { dense: true }, + }, + MuiListSubheader: { + defaultProps: { disableSticky: true }, + styleOverrides: { + root: { + ...theme.typography.subtitle2, + color: theme.palette.text.primary, + lineHeight: "32px", + userSelect: "none", + }, + }, + }, + MuiListItemIcon: { + styleOverrides: { + root: { + ".Mui-selected &": { color: "inherit" }, + }, + }, + }, + MuiListItemSecondaryAction: { + styleOverrides: { + root: { right: theme.spacing(0.75) }, + }, + }, + + MuiButton: { + styleOverrides: { + root: { + minHeight: 32, + paddingTop: theme.spacing(0.5), + paddingBottom: theme.spacing(0.5), + }, + sizeSmall: { + minHeight: 28, + paddingTop: theme.spacing(0.25), + paddingBottom: theme.spacing(0.25), + }, + sizeLarge: { + minHeight: 48, + fontSize: "1rem", + borderRadius: (theme.shape.borderRadius as number) * (16 / 14), + }, + + outlined: { + "&, &:hover, &.Mui-disabled": { border: "none" }, + boxShadow: + theme.palette.mode === "dark" + ? `0 0 0 1px ${colorDividerHalf} inset, + 0 1px 0 0 ${colorDividerHalf} inset` + : `0 0 0 1px ${theme.palette.divider} inset, + 0 -1px 0 0 ${theme.palette.divider} inset`, + backgroundColor: theme.palette.input, + + "&.Mui-disabled": { + boxShadow: `0 0 0 1px ${theme.palette.divider} inset`, + backgroundColor: "transparent", + }, + }, + outlinedPrimary: { + "&:hover": { + backgroundColor: colord(theme.palette.input) + .mix( + theme.palette.primary.main, + theme.palette.action.hoverOpacity + ) + .toHslString(), + }, + }, + outlinedSecondary: { + "&:hover": { + backgroundColor: colord(theme.palette.input) + .mix( + theme.palette.secondary.main, + theme.palette.action.hoverOpacity + ) + .toHslString(), + }, + }, + + contained: { + boxShadow: `${theme.shadows[2]}, 0 -1px 0 0 rgba(0, 0, 0, 0.12) inset`, + }, + containedPrimary: { + "&:hover": { backgroundColor: buttonPrimaryHover }, + }, + containedSecondary: { + "&:hover": { backgroundColor: buttonSecondaryHover }, + }, + }, + }, + MuiButtonGroup: { + styleOverrides: { + grouped: { minWidth: 32 }, + }, + }, + + MuiIconButton: { + defaultProps: { + TouchRippleProps: { center: false }, + }, + styleOverrides: { + sizeSmall: { borderRadius: theme.shape.borderRadius }, + }, + }, + MuiFab: { + styleOverrides: { + root: { + "&:not(.MuiFab-primary):not(.MuiFab-secondary):not(.Mui-disabled)": { + backgroundColor: fabBackgroundColor, + color: theme.palette.text.primary, + + "&:hover": { + backgroundColor: colord(fabBackgroundColor) + .mix( + theme.palette.action.hover, + theme.palette.action.hoverOpacity + ) + .alpha(1) + .toHslString(), + }, + }, + + "&.Mui-disabled": { + backgroundColor: colord(theme.palette.background.default) + .mix( + theme.palette.action.disabledBackground, + colord(theme.palette.action.disabledBackground).alpha() + ) + .alpha(1) + .toHslString(), + }, + }, + primary: { + boxShadow: `${theme.shadows[6]}, 0 -1px 0 0 rgba(0, 0, 0, 0.12) inset`, + "&:hover": { backgroundColor: buttonPrimaryHover }, + }, + secondary: { + boxShadow: `${theme.shadows[6]}, 0 -1px 0 0 rgba(0, 0, 0, 0.12) inset`, + "&:hover": { backgroundColor: buttonSecondaryHover }, + }, + sizeSmall: { width: 36, height: 36 }, + }, + }, + + MuiChip: { + defaultProps: { size: "small" }, + styleOverrides: { + sizeMedium: { + height: "auto", + minHeight: 32, + padding: "4px 0", + "&.MuiChip-outlined": { padding: "3px 0" }, + }, + sizeSmall: { + height: "auto", + minHeight: 24, + padding: "2px 0", + "&.MuiChip-outlined": { padding: "1px 0" }, + }, + + clickable: { + "&.MuiChip-filledPrimary:hover": { + backgroundColor: buttonPrimaryHover, + }, + "&.MuiChip-filledSecondary:hover": { + backgroundColor: buttonSecondaryHover, + }, + }, + }, + }, + + MuiSwitch: { + defaultProps: { size: "small" }, + styleOverrides: { + sizeMedium: { + width: 42 + (38 - 24), + height: 24 + (38 - 24), + padding: (38 - 24) / 2, + + "& .MuiSwitch-thumb": { width: 16, height: 16 }, + "& .MuiSwitch-switchBase": { padding: 11 }, + }, + sizeSmall: { + width: 36 + (28 - 20), + height: 20 + (28 - 20), + padding: (28 - 20) / 2, + + "& .MuiSwitch-thumb": { width: 12, height: 12 }, + "& .MuiSwitch-switchBase": { padding: 8 }, + }, + + track: { + borderRadius: 24 / 2, + backgroundColor: "transparent", + boxShadow: `0 0 0 1px ${theme.palette.text.disabled} inset`, + ".Mui-disabled + &": { + backgroundColor: theme.palette.text.disabled, + }, + + opacity: 1, + ".MuiSwitch-switchBase.Mui-checked:not(.Mui-disabled) + &": { + opacity: 1, + }, + ".MuiSwitch-switchBase.Mui-checked + &": { + boxShadow: "none", + }, + }, + switchBase: { + color: theme.palette.text.primary, + "&.Mui-checked": { transform: "translateX(18px)" }, + }, + thumb: { + borderRadius: 24 / 2, + boxShadow: theme.shadows[1], + + background: theme.palette.text.secondary, + ".MuiSwitch-switchBase.Mui-checked &": { + backgroundColor: theme.palette.secondary.contrastText, + }, + + transition: theme.transitions.create( + ["width", "transform", "background-color"], + { duration: theme.transitions.duration.shortest } + ), + + ".MuiSwitch-root:hover .MuiSwitch-switchBase:not(.Mui-disabled) &": { + transform: `scale(${1 + 2 / 16})`, + }, + ".MuiSwitch-root.MuiSwitch-sizeSmall:hover .MuiSwitch-switchBase:not(.Mui-disabled) &": { + transform: `scale(${1 + 2 / 12})`, + }, + + ".MuiSwitch-root:active .MuiSwitch-switchBase:not(.Mui-disabled) &": { + width: 16 + 4, + }, + ".MuiSwitch-root.MuiSwitch-sizeSmall:active .MuiSwitch-switchBase:not(.Mui-disabled) &": { + width: 12 + 4, + }, + "& + .MuiTouchRipple-root": { + borderRadius: 24 / 2, + zIndex: -1, + }, + + ".MuiSwitch-root:active .MuiSwitch-switchBase.Mui-checked:not(.Mui-disabled) &": { + transform: `translateX(-4px) scale(${1 + 2 / 16})`, + "& + .MuiTouchRipple-root": { left: -4 }, + }, + ".MuiSwitch-root.MuiSwitch-sizeSmall:active .MuiSwitch-switchBase.Mui-checked:not(.Mui-disabled) &": { + transform: `translateX(-4px) scale(${1 + 2 / 12})`, + }, + + ".MuiSwitch-switchBase.Mui-disabled &": { + opacity: theme.palette.action.disabledOpacity, + }, + ".MuiSwitch-switchBase.Mui-disabled.Mui-checked &": { + opacity: 1, + }, + }, + + colorSuccess: { + "&.Mui-checked": { + color: theme.palette.success.light, + "& + .MuiSwitch-track": { + backgroundColor: theme.palette.success.light, + }, + }, + }, + } as any, + }, + MuiSlider: { + styleOverrides: { + thumb: { + color: theme.palette.common.white, + transformOrigin: "50% 50%", + + "&::before": { + boxShadow: + theme.palette.mode === "dark" + ? theme.shadows[1].replace(")", ", 0.24)") + : `${theme.shadows[1]}, 0 0 0 1px ${theme.palette.divider}`, + }, + + "&:hover": { + "& > input": { transform: `scale(${1 + 2 / 20})` }, + }, + }, + + mark: { width: 3, height: 3 }, + + valueLabel: { + borderRadius: theme.shape.borderRadius, + + "&::before": { + borderRadius: (theme.shape.borderRadius as number) / 2, + width: 10, + height: 10, + bottom: 1, + }, + }, + }, + }, + + MuiTabs: { + defaultProps: { + TabIndicatorProps: { + children: , + }, + }, + styleOverrides: { + indicator: { + display: "flex", + justifyContent: "center", + alignItems: "center", + backgroundColor: "transparent", + + transitionTimingFunction: transitionEasingStrong, + transitionDuration: `${theme.transitions.duration.complex}ms`, + + height: 3, + ".MuiTabs-vertical &": { width: 3 }, + + "& > .MuiTabs-indicatorSpan": { + width: "100%", + height: "100%", + maxWidth: 32, + maxHeight: 16, + + borderRadius: 1.5, + backgroundColor: theme.palette.primary.main, + + transition: theme.transitions.create("transform", { + duration: theme.transitions.duration.shortest, + }), + ".MuiTabs-root:active &": { transform: "scaleX(1.25)" }, + ".MuiTabs-vertical:active &": { transform: "scaleY(1.25)" }, + }, + }, + }, + }, + MuiTab: { + styleOverrides: { + root: { + borderRadius: theme.shape.borderRadius, + + transition: theme.transitions.create( + ["background-color", "color"], + { duration: theme.transitions.duration.shortest } + ), + "&:hover": { backgroundColor: theme.palette.action.hover }, + "&.Mui-selected:hover": { + backgroundColor: colord(theme.palette.primary.main) + .alpha(theme.palette.action.hoverOpacity) + .toHslString(), + }, + }, + }, + }, + }, + }; +}; diff --git a/www/src/theme/palette.ts b/www/src/theme/palette.ts new file mode 100644 index 00000000..8e674342 --- /dev/null +++ b/www/src/theme/palette.ts @@ -0,0 +1,148 @@ +export const palette = { + aBlack: { + 500: "#282829", + }, + aWhite: { + 500: "#ffffff", + }, + aRed: { + 100: "#fae4e5", + 300: "#fb8c8c", + 500: "#ed4747", + 600: "#e91c1c", + 700: "#c12929", + }, + aGray: { + 50: "#fafafa", + 100: "#f2f2f2", + 200: "#e9e9e9", + 300: "#cccccc", + 500: "#999999", + 700: "#595959", + }, + + gray: { + 100: "#f2f2f2", + 300: "#cccccc", + 500: "#999999", + 700: "#595959", + }, + blueGray: { + 100: "#93a4ad", + 300: "#647c8a", + 500: "#485a63", + 700: "#394c55", + }, + indigo: { + 100: "#7986cb", + 300: "#3f51b5", + 500: "#303f9f", + 700: "#213092", + }, + blue: { + 100: "#64b5f6", + 300: "#2196f3", + 500: "#1976d2", + 700: "#0a59a8", + }, + lightBlue: { + 100: "#4fc3f7", + 300: "#03a9f4", + 500: "#0b8ed6", + 700: "#007fc5", + }, + cyan: { + 100: "#4dd0e1", + 300: "#00bcd4", + 500: "#0097a7", + 700: "#258493", + }, + teal: { + 100: "#4db6ac", + 300: "#049587", + 500: "#037b6d", + 700: "#006055", + }, + green: { + 100: "#81c784", + 300: "#4caf50", + 500: "#388e3c", + 700: "#2e7d32", + }, + lightGreen: { + 100: "#aed581", + 300: "#8bc34a", + 500: "#689f38", + 700: "#4f6f33", + }, + lime: { + 100: "#dce775", + 300: "#cddc39", + 500: "#afb42b", + 700: "#8e9d01", + }, + yellow: { + 100: "#fff176", + 300: "#ffeb3b", + 500: "#fbc02d", + 700: "#d07e04", + }, + amber: { + 100: "#ffd54f", + 300: "#ffc107", + 500: "#ffa000", + 700: "#b38c2b", + }, + orange: { + 100: "#ffb74d", + 300: "#ff9800", + 500: "#f57c00", + 700: "#bb661e", + }, + brown: { + 100: "#a1887f", + 300: "#795548", + 500: "#5d4037", + 700: "#4e3229", + }, + tangerine: { + 100: "#ff8a65", + 300: "#ff5722", + 500: "#e64a19", + 700: "#c0360a", + }, + errorRed: { + 100: "#e57373", + 300: "#f44336", + 500: "#d32f2f", + 700: "#c62323", + }, + pink: { + 100: "#f06292", + 300: "#e91e63", + 500: "#c2185b", + 700: "#b0104f", + }, + purple: { + 100: "#ba68c8", + 300: "#9c27b0", + 500: "#7b1fa2", + 700: "#650e89", + }, + violet: { + 100: "#9575cd", + 300: "#673ab7", + 500: "#512da8", + 700: "#341878", + }, +} as const; + +export const paletteToMui = ( + color: Record<"700" | "300" | "500" | "100", string> +) => ({ + main: color[500], + light: color[300], + dark: color[700], +}); + +export default palette; diff --git a/www/src/theme/typography.ts b/www/src/theme/typography.ts new file mode 100644 index 00000000..25d15589 --- /dev/null +++ b/www/src/theme/typography.ts @@ -0,0 +1,146 @@ +import { ThemeOptions } from "@material-ui/core/styles"; +import { + FontStyle, + TypographyStyleOptions, +} from "@material-ui/core/styles/createTypography"; + +declare module "@material-ui/core/styles/createTypography" { + interface FontStyle { + fontFamilyMono: string; + fontFamilyHeading: string; + } +} + +export const BODY_FONT = "Inter, system-ui, sans-serif"; +export const HEADING_FONT = "Space Grotesk, " + BODY_FONT; +export const MONO_FONT = "IBM Plex Mono, ui-monospace, monospace"; + +export const ROOT_FONT_SIZE = 16; +export const toRem = (px: number) => `${px / ROOT_FONT_SIZE}rem`; +export const toEm = (px: number, root: number) => `${px / root}em`; + +export const typography = ({ + fontFamily = BODY_FONT, + fontFamilyMono = MONO_FONT, + fontFamilyHeading = HEADING_FONT, + + fontWeightLight = 300, + fontWeightRegular = 400, + fontWeightMedium = 500, + fontWeightBold = 600, +}: Partial): ThemeOptions => { + const fontStyleBody: TypographyStyleOptions = { + fontFamily: fontFamily, + fontFeatureSettings: + fontFamily !== BODY_FONT + ? "normal" + : `"calt", "ss01", "ss03", "cv05", "cv09"`, + }; + const fontStyleHeading: TypographyStyleOptions = { + fontFamily: fontFamilyHeading, + fontFeatureSettings: + fontFamilyHeading !== HEADING_FONT ? "normal" : `"ss02", "ss03"`, + }; + + return { + typography: { + fontFamily, + fontFamilyMono, + fontFamilyHeading, + + fontWeightLight, + fontWeightRegular, + fontWeightMedium, + fontWeightBold, + + h1: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(96), + letterSpacing: toEm(-1.5, 96), + lineHeight: 112 / 96, + }, + h2: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(60), + letterSpacing: toEm(-0.75, 60), + lineHeight: 72 / 60, + }, + h3: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(48), + letterSpacing: toEm(-0.5, 48), + lineHeight: 60 / 48, + }, + h4: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(34), + letterSpacing: toEm(-0.35, 34), + lineHeight: 44 / 34, + }, + h5: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(24), + // letterSpacing: toEm(-0.2, 24), + lineHeight: 32 / 24, + }, + h6: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(20), + // letterSpacing: toEm(-0.15, 20), + lineHeight: 28 / 20, + }, + subtitle1: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(16), + letterSpacing: toEm(0.2, 16), + lineHeight: 24 / 16, + }, + subtitle2: { + ...fontStyleHeading, + fontWeight: 600, + fontSize: toRem(14), + letterSpacing: toEm(0.25, 14), + lineHeight: 20 / 14, + }, + body1: { + ...fontStyleBody, + fontSize: toRem(16), + letterSpacing: toEm(0.5, 16), + lineHeight: 24 / 16, + }, + body2: { + ...fontStyleBody, + fontSize: toRem(14), + letterSpacing: toEm(0.25, 14), + lineHeight: 20 / 14, + }, + button: { + ...fontStyleBody, + fontWeight: 500, + fontSize: toRem(14), + letterSpacing: toEm(0.25, 14), + lineHeight: 20 / 14, + textTransform: "none", + }, + caption: { + ...fontStyleBody, + fontSize: toRem(12), + letterSpacing: toEm(0.4, 12), + lineHeight: 20 / 12, + }, + overline: { + ...fontStyleBody, + fontSize: toRem(12), + letterSpacing: toEm(1.5, 12), + lineHeight: 20 / 12, + }, + }, + }; +};