diff --git a/site/src/AppRouter.tsx b/site/src/AppRouter.tsx
index dcdd25800f126..0e6d1334839bc 100644
--- a/site/src/AppRouter.tsx
+++ b/site/src/AppRouter.tsx
@@ -21,6 +21,8 @@ import WorkspacesPage from "./pages/WorkspacesPage/WorkspacesPage";
import UserSettingsLayout from "./pages/UserSettingsPage/Layout";
import { TemplateSettingsLayout } from "./pages/TemplateSettingsPage/TemplateSettingsLayout";
import { WorkspaceSettingsLayout } from "./pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
+import { ThemeOverride } from "contexts/ThemeProvider";
+import themes from "theme";
// Lazy load pages
// - Pages that are secondary, not in the main navigation or not usually accessed
@@ -384,7 +386,11 @@ export const AppRouter: FC = () => {
/>
}
+ element={
+
+
+
+ }
/>
} />
} />
diff --git a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx
index 46efd3712ca55..ec9dcb18f87c1 100644
--- a/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx
+++ b/site/src/components/Dashboard/DeploymentBanner/DeploymentBannerView.tsx
@@ -36,6 +36,7 @@ import { TerminalIcon } from "components/Icons/TerminalIcon";
import { RocketIcon } from "components/Icons/RocketIcon";
import ErrorIcon from "@mui/icons-material/ErrorOutline";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
+import colors from "theme/tailwindColors";
import { getDisplayWorkspaceStatus } from "utils/workspace";
import { HelpTooltipTitle } from "components/HelpTooltip/HelpTooltip";
import { Stack } from "components/Stack/Stack";
@@ -439,9 +440,9 @@ const styles = {
height: 16px;
}
`,
- unhealthy: (theme) => css`
- background-color: ${theme.colors.red[10]};
- `,
+ unhealthy: {
+ backgroundColor: colors.red[700],
+ },
group: css`
display: flex;
align-items: center;
diff --git a/site/src/components/IconField/IconField.tsx b/site/src/components/IconField/IconField.tsx
index 834556ea56be9..ea578d07734b9 100644
--- a/site/src/components/IconField/IconField.tsx
+++ b/site/src/components/IconField/IconField.tsx
@@ -102,8 +102,8 @@ const IconField: FC = ({ onPickEmoji, ...textFieldProps }) => {
styles={css`
em-emoji-picker {
--rgb-background: ${theme.palette.background.paper};
- --rgb-input: ${theme.colors.gray[17]};
- --rgb-color: ${theme.colors.gray[4]};
+ --rgb-input: ${theme.palette.primary.main};
+ --rgb-color: ${theme.palette.text.primary};
// Hack to prevent the right side from being cut off
width: 350px;
diff --git a/site/src/components/Markdown/Markdown.tsx b/site/src/components/Markdown/Markdown.tsx
index e6defa3733288..e52a307112470 100644
--- a/site/src/components/Markdown/Markdown.tsx
+++ b/site/src/components/Markdown/Markdown.tsx
@@ -11,7 +11,7 @@ import { type FC, memo } from "react";
import ReactMarkdown, { type Options } from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import gfm from "remark-gfm";
-import colors from "theme/tailwind";
+import colors from "theme/tailwindColors";
import { dracula } from "react-syntax-highlighter/dist/cjs/styles/prism";
interface MarkdownProps {
diff --git a/site/src/pages/AuditPage/AuditLogRow/AuditLogDiff/AuditLogDiff.tsx b/site/src/pages/AuditPage/AuditLogRow/AuditLogDiff/AuditLogDiff.tsx
index 3fcb4dc524845..50fc7fd44eb3a 100644
--- a/site/src/pages/AuditPage/AuditLogRow/AuditLogDiff/AuditLogDiff.tsx
+++ b/site/src/pages/AuditPage/AuditLogRow/AuditLogDiff/AuditLogDiff.tsx
@@ -1,7 +1,7 @@
import { type Interpolation, type Theme } from "@emotion/react";
import { type FC } from "react";
import type { AuditLog } from "api/typesGenerated";
-import colors from "theme/tailwind";
+import colors from "theme/tailwindColors";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
const getDiffValue = (value: unknown): string => {
diff --git a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx
index be6092f0b0b88..1e6a5777d2b5b 100644
--- a/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx
+++ b/site/src/pages/DeploySettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx
@@ -19,7 +19,7 @@ import {
import { Fieldset } from "components/DeploySettingsLayout/Fieldset";
import { Stack } from "components/Stack/Stack";
import { getFormHelpers } from "utils/formUtils";
-import colors from "theme/tailwind";
+import colors from "theme/tailwindColors";
export type AppearanceSettingsPageViewProps = {
appearance: UpdateAppearanceConfig;
diff --git a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx
index ed4ddc9d3d35a..7788caee5b0f1 100644
--- a/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx
+++ b/site/src/pages/TemplatePage/TemplateInsightsPage/TemplateInsightsPage.tsx
@@ -420,7 +420,10 @@ const TemplateUsagePanel: FC = ({
const totalInSeconds =
validUsage?.reduce((total, usage) => total + usage.seconds, 0) ?? 1;
const usageColors = chroma
- .scale([theme.colors.green[8], theme.colors.blue[8]])
+ .scale([
+ theme.experimental.roles.success.fill,
+ theme.experimental.roles.notice.fill,
+ ])
.mode("lch")
.colors(validUsage?.length ?? 0);
// The API returns a row for each app, even if the user didn't use it.
diff --git a/site/src/pages/TerminalPage/TerminalPage.tsx b/site/src/pages/TerminalPage/TerminalPage.tsx
index 7130abfa57219..0271c1ec245dc 100644
--- a/site/src/pages/TerminalPage/TerminalPage.tsx
+++ b/site/src/pages/TerminalPage/TerminalPage.tsx
@@ -108,7 +108,7 @@ const TerminalPage: FC = () => {
fontFamily: MONOSPACE_FONT_FAMILY,
fontSize: 16,
theme: {
- background: theme.colors.gray[16],
+ background: theme.palette.background.default,
},
});
if (renderer === "webgl") {
diff --git a/site/src/pages/WorkspacesPage/LastUsed.tsx b/site/src/pages/WorkspacesPage/LastUsed.tsx
index c048a6f726a17..7395151113a91 100644
--- a/site/src/pages/WorkspacesPage/LastUsed.tsx
+++ b/site/src/pages/WorkspacesPage/LastUsed.tsx
@@ -40,7 +40,7 @@ export const LastUsed: FC = ({ lastUsedAt }) => {
);
if (t.isAfter(now.subtract(1, "hour"))) {
- circle = ;
+ circle = ;
// Since the agent reports on a 10m interval,
// the last_used_at can be inaccurate when recent.
message = "Now";
@@ -49,15 +49,14 @@ export const LastUsed: FC = ({ lastUsedAt }) => {
} else if (t.isAfter(now.subtract(1, "month"))) {
circle = ;
} else if (t.isAfter(now.subtract(100, "year"))) {
- circle = ;
+ circle = ;
} else {
- // color = theme.palette.error.light
message = "Never";
}
return (
({
- textTransform: "none",
- letterSpacing: "normal",
- fontWeight: 500,
- height: BUTTON_MD_HEIGHT,
- padding: "8px 16px",
- borderRadius: "6px",
- fontSize: 14,
-
- whiteSpace: "nowrap",
- ":focus-visible": {
- outline: `2px solid ${theme.palette.primary.main}`,
- },
-
- "& .MuiLoadingButton-loadingIndicator": {
- width: 14,
- height: 14,
- },
-
- "& .MuiLoadingButton-loadingIndicator .MuiCircularProgress-root": {
- width: "inherit !important",
- height: "inherit !important",
- },
- }),
- sizeSmall: {
- height: BUTTON_SM_HEIGHT,
- },
- sizeLarge: {
- height: BUTTON_LG_HEIGHT,
- },
- sizeXlarge: {
- height: BUTTON_XL_HEIGHT,
- },
- outlined: {
- ":hover": {
- border: `1px solid ${colors.gray[11]}`,
- },
- },
- outlinedNeutral: {
- borderColor: colors.gray[12],
-
- "&.Mui-disabled": {
- borderColor: colors.gray[13],
- color: colors.gray[11],
-
- "& > .MuiLoadingButton-loadingIndicator": {
- color: colors.gray[11],
- },
- },
- },
- containedNeutral: {
- backgroundColor: colors.gray[14],
-
- "&:hover": {
- backgroundColor: colors.gray[13],
- },
- },
- iconSizeMedium: {
- "& > .MuiSvgIcon-root": {
- fontSize: 14,
- },
- },
- iconSizeSmall: {
- "& > .MuiSvgIcon-root": {
- fontSize: 13,
- },
- },
- startIcon: {
- marginLeft: "-2px",
- },
- },
- },
- MuiButtonGroup: {
- styleOverrides: {
- root: {
- ">button:hover+button": {
- // The !important is unfortunate, but necessary for the border.
- borderLeftColor: `${colors.gray[11]} !important`,
- },
- },
- },
- },
- MuiLoadingButton: {
- defaultProps: {
- variant: "outlined",
- color: "neutral",
- },
- },
- MuiTableContainer: {
- styleOverrides: {
- root: {
- borderRadius,
- border: `1px solid ${muiTheme.palette.divider}`,
- },
- },
- },
- MuiTable: {
- styleOverrides: {
- root: ({ theme }) => ({
- borderCollapse: "unset",
- border: "none",
- boxShadow: `0 0 0 1px ${muiTheme.palette.background.default} inset`,
- overflow: "hidden",
-
- "& td": {
- paddingTop: 16,
- paddingBottom: 16,
- background: "transparent",
- },
-
- [theme.breakpoints.down("md")]: {
- minWidth: 1000,
- },
- }),
- },
- },
- MuiTableCell: {
- styleOverrides: {
- head: {
- fontSize: 14,
- color: muiTheme.palette.text.secondary,
- fontWeight: 600,
- background: muiTheme.palette.background.paper,
- },
- root: {
- fontSize: 16,
- background: muiTheme.palette.background.paper,
- borderBottom: `1px solid ${muiTheme.palette.divider}`,
- padding: "12px 8px",
- // This targets the first+last td elements, and also the first+last elements
- // of a TableCellLink.
- "&:not(:only-child):first-of-type, &:not(:only-child):first-of-type > a":
- {
- paddingLeft: 32,
- },
- "&:not(:only-child):last-child, &:not(:only-child):last-child > a": {
- paddingRight: 32,
- },
- },
- },
- },
- MuiTableRow: {
- styleOverrides: {
- root: {
- "&:last-child .MuiTableCell-body": {
- borderBottom: 0,
- },
- },
- },
- },
- MuiLink: {
- defaultProps: {
- underline: "hover",
- },
- },
- MuiPaper: {
- defaultProps: {
- elevation: 0,
- },
- styleOverrides: {
- root: {
- border: `1px solid ${muiTheme.palette.divider}`,
- backgroundImage: "none",
- },
- },
- },
- MuiSkeleton: {
- styleOverrides: {
- root: {
- backgroundColor: muiTheme.palette.divider,
- },
- },
- },
- MuiLinearProgress: {
- styleOverrides: {
- root: {
- borderRadius: 999,
- },
- },
- },
- MuiChip: {
- styleOverrides: {
- root: {
- backgroundColor: colors.gray[12],
- },
- },
- },
- MuiMenu: {
- defaultProps: {
- anchorOrigin: {
- vertical: "bottom",
- horizontal: "right",
- },
- transformOrigin: {
- vertical: "top",
- horizontal: "right",
- },
- },
- styleOverrides: {
- paper: {
- marginTop: 8,
- borderRadius: 4,
- padding: "4px 0",
- minWidth: 160,
- },
- root: {
- // It should be the same as the menu padding
- "& .MuiDivider-root": {
- marginTop: `4px !important`,
- marginBottom: `4px !important`,
- },
- },
- },
- },
- MuiMenuItem: {
- styleOverrides: {
- root: {
- gap: 12,
-
- "& .MuiSvgIcon-root": {
- fontSize: 20,
- },
- },
- },
- },
- MuiSnackbar: {
- styleOverrides: {
- anchorOriginBottomRight: {
- bottom: `${24 + 36}px !important`, // 36 is the bottom bar height
- },
- },
- },
- MuiSnackbarContent: {
- styleOverrides: {
- root: {
- borderRadius: "4px !important",
- },
- },
- },
- MuiTextField: {
- defaultProps: {
- InputLabelProps: {
- shrink: true,
- },
- },
- },
- MuiInputBase: {
- defaultProps: {
- color: "primary",
- },
- styleOverrides: {
- root: {
- height: BUTTON_LG_HEIGHT,
- },
- sizeSmall: {
- height: BUTTON_MD_HEIGHT,
- fontSize: 14,
- },
- multiline: {
- height: "auto",
- },
- colorPrimary: {
- // Same as button
- "& .MuiOutlinedInput-notchedOutline": {
- borderColor: colors.gray[12],
- },
- // The default outlined input color is white, which seemed jarring.
- "&:hover:not(.Mui-error):not(.Mui-focused) .MuiOutlinedInput-notchedOutline":
- {
- borderColor: colors.gray[11],
- },
- },
- },
- },
- MuiFormHelperText: {
- defaultProps: {
- sx: {
- marginLeft: 0,
- marginTop: 1,
- },
- },
- },
- MuiRadio: {
- defaultProps: {
- disableRipple: true,
- },
- },
- MuiCheckbox: {
- styleOverrides: {
- root: {
- /**
- * Adds focus styling to checkboxes (which doesn't exist normally, for
- * some reason?).
- *
- * The checkbox component is a root span with a checkbox input inside
- * it. MUI does not allow you to use selectors like (& input) to
- * target the inner checkbox (even though you can use & td to style
- * tables). Tried every combination of selector possible (including
- * lots of !important), and the main issue seems to be that the
- * styling just never gets processed for it to get injected into the
- * CSSOM.
- *
- * Had to settle for adding styling to the span itself (which does
- * make the styling more obvious, even if there's not much room for
- * customization).
- */
- "&.Mui-focusVisible": {
- boxShadow: `0 0 0 2px ${colors.blue[7]}`,
- },
-
- "&.Mui-disabled": {
- color: colors.gray[11],
- },
- },
- },
- },
- MuiSwitch: {
- defaultProps: { color: "primary" },
- styleOverrides: {
- root: {
- ".Mui-focusVisible .MuiSwitch-thumb": {
- // Had to thicken outline to make sure that the focus color didn't
- // bleed into the thumb and was still easily-visible
- boxShadow: `0 0 0 3px ${colors.blue[7]}`,
- },
- },
- },
- },
- MuiAutocomplete: {
- styleOverrides: {
- root: {
- // Not sure why but since the input has padding we don't need it here
- "& .MuiInputBase-root": {
- padding: 0,
- },
- },
- },
- },
- MuiList: {
- defaultProps: {
- disablePadding: true,
- },
- },
- MuiTabs: {
- defaultProps: {
- textColor: "primary",
- indicatorColor: "primary",
- },
- },
- MuiTooltip: {
- styleOverrides: {
- tooltip: {
- lineHeight: "150%",
- borderRadius: 4,
- background: muiTheme.palette.divider,
- padding: "8px 16px",
- },
- },
- },
- MuiAlert: {
- defaultProps: {
- variant: "outlined",
- },
- styleOverrides: {
- root: ({ theme }) => ({
- background: theme.palette.background.paper,
- }),
- action: {
- paddingTop: 2, // Idk why it is not aligned as expected
- },
- icon: {
- fontSize: 16,
- marginTop: "4px", // The size of text is 24 so (24 - 16)/2 = 4
- },
- message: ({ theme }) => ({
- color: theme.palette.text.primary,
- }),
- outlinedWarning: {
- [`& .${alertClasses.icon}`]: {
- color: muiTheme.palette.warning.light,
- },
- },
- outlinedInfo: {
- [`& .${alertClasses.icon}`]: {
- color: muiTheme.palette.primary.light,
- },
- },
- outlinedError: {
- [`& .${alertClasses.icon}`]: {
- color: muiTheme.palette.error.light,
- },
- },
- },
- },
- MuiAlertTitle: {
- styleOverrides: {
- root: {
- fontSize: "inherit",
- marginBottom: 0,
- },
- },
- },
-
- MuiIconButton: {
- styleOverrides: {
- root: {
- "&.Mui-focusVisible": {
- boxShadow: `0 0 0 2px ${colors.blue[7]}`,
- },
- },
- },
- },
- },
-} as ThemeOptions);
-
export default muiTheme;
diff --git a/site/src/theme/darkBlue/colors.ts b/site/src/theme/darkBlue/colors.ts
deleted file mode 100644
index 694d86388f785..0000000000000
--- a/site/src/theme/darkBlue/colors.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import tw from "../tailwind";
-
-export default {
- white: "#fff",
-
- gray: {
- 17: tw.slate[900],
- 16: tw.slate[900],
- 14: tw.slate[800],
- 13: tw.slate[700],
- 12: tw.slate[600],
- 11: tw.slate[500],
- 9: tw.slate[400],
- 6: tw.slate[300],
- 4: tw.slate[200],
- 2: tw.slate[100],
- 1: tw.slate[50],
- },
-
- red: {
- 15: tw.red[950],
- 12: tw.red[800],
- 10: tw.red[700],
- 9: tw.red[600],
- 8: tw.red[500],
- 6: tw.red[400],
- 2: tw.red[50],
- },
-
- orange: {
- 15: tw.amber[950],
- 14: tw.amber[900],
- 12: tw.amber[800],
- 11: tw.amber[700],
- 10: tw.amber[600],
- 9: tw.amber[500],
- 7: tw.amber[400],
- },
-
- yellow: {
- 5: tw.yellow[300],
- },
-
- green: {
- 15: tw.green[950],
- 13: tw.green[700],
- 12: tw.green[600],
- 11: tw.green[500],
- 9: tw.green[400],
- 8: tw.green[300],
- },
-
- blue: {
- 14: tw.blue[950],
- 9: tw.blue[600],
- 8: tw.blue[500],
- 7: tw.blue[400],
- 6: tw.blue[300],
- 3: tw.blue[200],
- 1: tw.blue[50],
- },
-};
diff --git a/site/src/theme/darkBlue/experimental.ts b/site/src/theme/darkBlue/experimental.ts
index 9b15df75a932b..954d0fa6e9a95 100644
--- a/site/src/theme/darkBlue/experimental.ts
+++ b/site/src/theme/darkBlue/experimental.ts
@@ -1,5 +1,5 @@
import { type NewTheme } from "../experimental";
-import colors from "../tailwind";
+import colors from "../tailwindColors";
export default {
l1: {
diff --git a/site/src/theme/darkBlue/index.ts b/site/src/theme/darkBlue/index.ts
index 4a1d9094c6bc2..7c487ee146132 100644
--- a/site/src/theme/darkBlue/index.ts
+++ b/site/src/theme/darkBlue/index.ts
@@ -1,11 +1,9 @@
-import colors from "./colors";
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
export default {
...muiTheme,
- colors,
experimental,
monaco,
};
diff --git a/site/src/theme/darkBlue/mui.ts b/site/src/theme/darkBlue/mui.ts
index f9308dc00044b..ecf5022323005 100644
--- a/site/src/theme/darkBlue/mui.ts
+++ b/site/src/theme/darkBlue/mui.ts
@@ -1,66 +1,58 @@
-import colors from "./colors";
-import { createTheme, type ThemeOptions } from "@mui/material/styles";
-import {
- BODY_FONT_FAMILY,
- borderRadius,
- BUTTON_LG_HEIGHT,
- BUTTON_MD_HEIGHT,
- BUTTON_SM_HEIGHT,
- BUTTON_XL_HEIGHT,
-} from "../constants";
-// eslint-disable-next-line no-restricted-imports -- We need MUI here
-import { alertClasses } from "@mui/material/Alert";
+import { createTheme } from "@mui/material/styles";
+import { BODY_FONT_FAMILY, borderRadius } from "../constants";
+import tw from "../tailwindColors";
+import { components } from "../mui";
-let muiTheme = createTheme({
+const muiTheme = createTheme({
palette: {
mode: "dark",
primary: {
- main: colors.blue[7],
- contrastText: colors.blue[1],
- light: colors.blue[6],
- dark: colors.blue[9],
+ main: tw.sky[500],
+ contrastText: tw.sky[50],
+ light: tw.sky[300],
+ dark: tw.sky[400],
},
secondary: {
- main: colors.gray[11],
- contrastText: colors.gray[4],
- dark: colors.gray[9],
+ main: tw.gray[500],
+ contrastText: tw.gray[200],
+ dark: tw.gray[400],
},
background: {
- default: colors.gray[17],
- paper: colors.gray[16],
+ default: tw.gray[900],
+ paper: tw.gray[900],
},
text: {
- primary: colors.gray[1],
- secondary: colors.gray[6],
- disabled: colors.gray[9],
+ primary: tw.gray[50],
+ secondary: tw.gray[300],
+ disabled: tw.gray[400],
},
- divider: colors.gray[13],
+ divider: tw.gray[700],
warning: {
- light: colors.orange[9],
- main: colors.orange[12],
- dark: colors.orange[15],
+ light: tw.amber[500],
+ main: tw.amber[800],
+ dark: tw.amber[950],
},
success: {
- main: colors.green[11],
- dark: colors.green[12],
+ main: tw.green[500],
+ dark: tw.green[600],
},
info: {
- light: colors.blue[7],
- main: colors.blue[9],
- dark: colors.blue[14],
- contrastText: colors.gray[4],
+ light: tw.blue[400],
+ main: tw.blue[600],
+ dark: tw.blue[950],
+ contrastText: tw.gray[200],
},
error: {
- light: colors.red[6],
- main: colors.red[8],
- dark: colors.red[15],
- contrastText: colors.gray[4],
+ light: tw.red[400],
+ main: tw.red[500],
+ dark: tw.red[950],
+ contrastText: tw.gray[200],
},
action: {
- hover: colors.gray[14],
+ hover: tw.gray[800],
},
neutral: {
- main: colors.gray[1],
+ main: tw.gray[50],
},
},
typography: {
@@ -79,474 +71,7 @@ let muiTheme = createTheme({
shape: {
borderRadius,
},
+ components,
});
-muiTheme = createTheme(muiTheme, {
- components: {
- MuiCssBaseline: {
- styleOverrides: `
- html, body, #root, #storybook-root {
- height: 100%;
- }
-
- button, input {
- font-family: ${BODY_FONT_FAMILY};
- }
-
- input:-webkit-autofill,
- input:-webkit-autofill:hover,
- input:-webkit-autofill:focus,
- input:-webkit-autofill:active {
- -webkit-box-shadow: 0 0 0 100px ${muiTheme.palette.background.default} inset !important;
- }
-
- ::placeholder {
- color: ${muiTheme.palette.text.disabled};
- }
- `,
- },
- MuiAvatar: {
- styleOverrides: {
- root: {
- width: 36,
- height: 36,
- fontSize: 18,
-
- "& .MuiSvgIcon-root": {
- width: "50%",
- },
- },
- colorDefault: {
- backgroundColor: colors.gray[6],
- },
- },
- },
- // Button styles are based on
- // https://tailwindui.com/components/application-ui/elements/buttons
- MuiButtonBase: {
- defaultProps: {
- disableRipple: true,
- },
- },
- MuiButton: {
- defaultProps: {
- variant: "outlined",
- color: "neutral",
- },
- styleOverrides: {
- root: ({ theme }) => ({
- textTransform: "none",
- letterSpacing: "normal",
- fontWeight: 500,
- height: BUTTON_MD_HEIGHT,
- padding: "8px 16px",
- borderRadius: "6px",
- fontSize: 14,
-
- whiteSpace: "nowrap",
- ":focus-visible": {
- outline: `2px solid ${theme.palette.primary.main}`,
- },
-
- "& .MuiLoadingButton-loadingIndicator": {
- width: 14,
- height: 14,
- },
-
- "& .MuiLoadingButton-loadingIndicator .MuiCircularProgress-root": {
- width: "inherit !important",
- height: "inherit !important",
- },
- }),
- sizeSmall: {
- height: BUTTON_SM_HEIGHT,
- },
- sizeLarge: {
- height: BUTTON_LG_HEIGHT,
- },
- sizeXlarge: {
- height: BUTTON_XL_HEIGHT,
- },
- outlined: {
- ":hover": {
- border: `1px solid ${colors.gray[11]}`,
- },
- },
- outlinedNeutral: {
- borderColor: colors.gray[12],
-
- "&.Mui-disabled": {
- borderColor: colors.gray[13],
- color: colors.gray[11],
-
- "& > .MuiLoadingButton-loadingIndicator": {
- color: colors.gray[11],
- },
- },
- },
- containedNeutral: {
- backgroundColor: colors.gray[14],
-
- "&:hover": {
- backgroundColor: colors.gray[13],
- },
- },
- iconSizeMedium: {
- "& > .MuiSvgIcon-root": {
- fontSize: 14,
- },
- },
- iconSizeSmall: {
- "& > .MuiSvgIcon-root": {
- fontSize: 13,
- },
- },
- startIcon: {
- marginLeft: "-2px",
- },
- },
- },
- MuiButtonGroup: {
- styleOverrides: {
- root: {
- ">button:hover+button": {
- // The !important is unfortunate, but necessary for the border.
- borderLeftColor: `${colors.gray[11]} !important`,
- },
- },
- },
- },
- MuiLoadingButton: {
- defaultProps: {
- variant: "outlined",
- color: "neutral",
- },
- },
- MuiTableContainer: {
- styleOverrides: {
- root: {
- borderRadius,
- border: `1px solid ${muiTheme.palette.divider}`,
- },
- },
- },
- MuiTable: {
- styleOverrides: {
- root: ({ theme }) => ({
- borderCollapse: "unset",
- border: "none",
- boxShadow: `0 0 0 1px ${muiTheme.palette.background.default} inset`,
- overflow: "hidden",
-
- "& td": {
- paddingTop: 16,
- paddingBottom: 16,
- background: "transparent",
- },
-
- [theme.breakpoints.down("md")]: {
- minWidth: 1000,
- },
- }),
- },
- },
- MuiTableCell: {
- styleOverrides: {
- head: {
- fontSize: 14,
- color: muiTheme.palette.text.secondary,
- fontWeight: 600,
- background: muiTheme.palette.background.paper,
- },
- root: {
- fontSize: 16,
- background: muiTheme.palette.background.paper,
- borderBottom: `1px solid ${muiTheme.palette.divider}`,
- padding: "12px 8px",
- // This targets the first+last td elements, and also the first+last elements
- // of a TableCellLink.
- "&:not(:only-child):first-of-type, &:not(:only-child):first-of-type > a":
- {
- paddingLeft: 32,
- },
- "&:not(:only-child):last-child, &:not(:only-child):last-child > a": {
- paddingRight: 32,
- },
- },
- },
- },
- MuiTableRow: {
- styleOverrides: {
- root: {
- "&:last-child .MuiTableCell-body": {
- borderBottom: 0,
- },
- },
- },
- },
- MuiLink: {
- defaultProps: {
- underline: "hover",
- },
- },
- MuiPaper: {
- defaultProps: {
- elevation: 0,
- },
- styleOverrides: {
- root: {
- border: `1px solid ${muiTheme.palette.divider}`,
- backgroundImage: "none",
- },
- },
- },
- MuiSkeleton: {
- styleOverrides: {
- root: {
- backgroundColor: muiTheme.palette.divider,
- },
- },
- },
- MuiLinearProgress: {
- styleOverrides: {
- root: {
- borderRadius: 999,
- },
- },
- },
- MuiChip: {
- styleOverrides: {
- root: {
- backgroundColor: colors.gray[12],
- },
- },
- },
- MuiMenu: {
- defaultProps: {
- anchorOrigin: {
- vertical: "bottom",
- horizontal: "right",
- },
- transformOrigin: {
- vertical: "top",
- horizontal: "right",
- },
- },
- styleOverrides: {
- paper: {
- marginTop: 8,
- borderRadius: 4,
- padding: "4px 0",
- minWidth: 160,
- },
- root: {
- // It should be the same as the menu padding
- "& .MuiDivider-root": {
- marginTop: `4px !important`,
- marginBottom: `4px !important`,
- },
- },
- },
- },
- MuiMenuItem: {
- styleOverrides: {
- root: {
- gap: 12,
-
- "& .MuiSvgIcon-root": {
- fontSize: 20,
- },
- },
- },
- },
- MuiSnackbar: {
- styleOverrides: {
- anchorOriginBottomRight: {
- bottom: `${24 + 36}px !important`, // 36 is the bottom bar height
- },
- },
- },
- MuiSnackbarContent: {
- styleOverrides: {
- root: {
- borderRadius: "4px !important",
- },
- },
- },
- MuiTextField: {
- defaultProps: {
- InputLabelProps: {
- shrink: true,
- },
- },
- },
- MuiInputBase: {
- defaultProps: {
- color: "primary",
- },
- styleOverrides: {
- root: {
- height: BUTTON_LG_HEIGHT,
- },
- sizeSmall: {
- height: BUTTON_MD_HEIGHT,
- fontSize: 14,
- },
- multiline: {
- height: "auto",
- },
- colorPrimary: {
- // Same as button
- "& .MuiOutlinedInput-notchedOutline": {
- borderColor: colors.gray[12],
- },
- // The default outlined input color is white, which seemed jarring.
- "&:hover:not(.Mui-error):not(.Mui-focused) .MuiOutlinedInput-notchedOutline":
- {
- borderColor: colors.gray[11],
- },
- },
- },
- },
- MuiFormHelperText: {
- defaultProps: {
- sx: {
- marginLeft: 0,
- marginTop: 1,
- },
- },
- },
- MuiRadio: {
- defaultProps: {
- disableRipple: true,
- },
- },
- MuiCheckbox: {
- styleOverrides: {
- root: {
- /**
- * Adds focus styling to checkboxes (which doesn't exist normally, for
- * some reason?).
- *
- * The checkbox component is a root span with a checkbox input inside
- * it. MUI does not allow you to use selectors like (& input) to
- * target the inner checkbox (even though you can use & td to style
- * tables). Tried every combination of selector possible (including
- * lots of !important), and the main issue seems to be that the
- * styling just never gets processed for it to get injected into the
- * CSSOM.
- *
- * Had to settle for adding styling to the span itself (which does
- * make the styling more obvious, even if there's not much room for
- * customization).
- */
- "&.Mui-focusVisible": {
- boxShadow: `0 0 0 2px ${colors.blue[7]}`,
- },
-
- "&.Mui-disabled": {
- color: colors.gray[11],
- },
- },
- },
- },
- MuiSwitch: {
- defaultProps: { color: "primary" },
- styleOverrides: {
- root: {
- ".Mui-focusVisible .MuiSwitch-thumb": {
- // Had to thicken outline to make sure that the focus color didn't
- // bleed into the thumb and was still easily-visible
- boxShadow: `0 0 0 3px ${colors.blue[7]}`,
- },
- },
- },
- },
- MuiAutocomplete: {
- styleOverrides: {
- root: {
- // Not sure why but since the input has padding we don't need it here
- "& .MuiInputBase-root": {
- padding: 0,
- },
- },
- },
- },
- MuiList: {
- defaultProps: {
- disablePadding: true,
- },
- },
- MuiTabs: {
- defaultProps: {
- textColor: "primary",
- indicatorColor: "primary",
- },
- },
- MuiTooltip: {
- styleOverrides: {
- tooltip: {
- lineHeight: "150%",
- borderRadius: 4,
- background: muiTheme.palette.divider,
- padding: "8px 16px",
- },
- },
- },
- MuiAlert: {
- defaultProps: {
- variant: "outlined",
- },
- styleOverrides: {
- root: ({ theme }) => ({
- background: theme.palette.background.paper,
- }),
- action: {
- paddingTop: 2, // Idk why it is not aligned as expected
- },
- icon: {
- fontSize: 16,
- marginTop: "4px", // The size of text is 24 so (24 - 16)/2 = 4
- },
- message: ({ theme }) => ({
- color: theme.palette.text.primary,
- }),
- outlinedWarning: {
- [`& .${alertClasses.icon}`]: {
- color: muiTheme.palette.warning.light,
- },
- },
- outlinedInfo: {
- [`& .${alertClasses.icon}`]: {
- color: muiTheme.palette.primary.light,
- },
- },
- outlinedError: {
- [`& .${alertClasses.icon}`]: {
- color: muiTheme.palette.error.light,
- },
- },
- },
- },
- MuiAlertTitle: {
- styleOverrides: {
- root: {
- fontSize: "inherit",
- marginBottom: 0,
- },
- },
- },
-
- MuiIconButton: {
- styleOverrides: {
- root: {
- "&.Mui-focusVisible": {
- boxShadow: `0 0 0 2px ${colors.blue[7]}`,
- },
- },
- },
- },
- },
-} as ThemeOptions);
-
export default muiTheme;
diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts
index 6f889785286f9..bb7a620582f58 100644
--- a/site/src/theme/index.ts
+++ b/site/src/theme/index.ts
@@ -4,10 +4,8 @@ import dark from "./dark";
import darkBlue from "./darkBlue";
import light from "./light";
import type { NewTheme } from "./experimental";
-import type { Colors } from "./colors";
export interface Theme extends MuiTheme {
- colors: Colors;
experimental: NewTheme;
monaco: monaco.editor.IStandaloneThemeData;
}
diff --git a/site/src/theme/light/colors.ts b/site/src/theme/light/colors.ts
index 374c120e8f289..74508b31db47b 100644
--- a/site/src/theme/light/colors.ts
+++ b/site/src/theme/light/colors.ts
@@ -1,4 +1,4 @@
-import tw from "../tailwind";
+import tw from "../tailwindColors";
export default {
white: "#fff",
diff --git a/site/src/theme/light/experimental.ts b/site/src/theme/light/experimental.ts
index 87ea50527c9f9..c230da896c3bf 100644
--- a/site/src/theme/light/experimental.ts
+++ b/site/src/theme/light/experimental.ts
@@ -1,5 +1,5 @@
import { type NewTheme } from "../experimental";
-import colors from "../tailwind";
+import colors from "../tailwindColors";
export default {
l1: {
diff --git a/site/src/theme/light/mui.ts b/site/src/theme/light/mui.ts
index 23040ee3f7a2f..82c8efdde2b14 100644
--- a/site/src/theme/light/mui.ts
+++ b/site/src/theme/light/mui.ts
@@ -9,7 +9,7 @@ import {
BUTTON_SM_HEIGHT,
BUTTON_XL_HEIGHT,
} from "../constants";
-import tw from "../tailwind";
+import tw from "../tailwindColors";
let muiTheme = createTheme({
palette: {
diff --git a/site/src/theme/mui.ts b/site/src/theme/mui.ts
index 9e604024f98d6..899eda97820f9 100644
--- a/site/src/theme/mui.ts
+++ b/site/src/theme/mui.ts
@@ -1,3 +1,18 @@
+/* eslint-disable @typescript-eslint/no-explicit-any
+-- we need to hack around the MUI types a little */
+import { type ThemeOptions } from "@mui/material/styles";
+// eslint-disable-next-line no-restricted-imports -- We need MUI here
+import { alertClasses } from "@mui/material/Alert";
+import {
+ BODY_FONT_FAMILY,
+ borderRadius,
+ BUTTON_LG_HEIGHT,
+ BUTTON_MD_HEIGHT,
+ BUTTON_SM_HEIGHT,
+ BUTTON_XL_HEIGHT,
+} from "./constants";
+import tw from "./tailwindColors";
+
export type PaletteIndex =
| "primary"
| "secondary"
@@ -9,3 +24,469 @@ export type PaletteIndex =
| "success"
| "action"
| "neutral";
+
+export const components: ThemeOptions["components"] = {
+ MuiCssBaseline: {
+ styleOverrides: (theme) => `
+ html, body, #root, #storybook-root {
+ height: 100%;
+ }
+
+ button, input {
+ font-family: ${BODY_FONT_FAMILY};
+ }
+
+ input:-webkit-autofill,
+ input:-webkit-autofill:hover,
+ input:-webkit-autofill:focus,
+ input:-webkit-autofill:active {
+ -webkit-box-shadow: 0 0 0 100px ${theme.palette.background.default} inset !important;
+ }
+
+ ::placeholder {
+ color: ${theme.palette.text.disabled};
+ }
+ `,
+ },
+ MuiAvatar: {
+ styleOverrides: {
+ root: {
+ width: 36,
+ height: 36,
+ fontSize: 18,
+
+ "& .MuiSvgIcon-root": {
+ width: "50%",
+ },
+ },
+ colorDefault: ({ theme }) => ({
+ backgroundColor: theme.palette.primary.light,
+ }),
+ },
+ },
+ // Button styles are based on
+ // https://tailwindui.com/components/application-ui/elements/buttons
+ MuiButtonBase: {
+ defaultProps: {
+ disableRipple: true,
+ },
+ },
+ MuiButton: {
+ defaultProps: {
+ variant: "outlined",
+ color: "neutral",
+ },
+ styleOverrides: {
+ root: ({ theme }) => ({
+ textTransform: "none",
+ letterSpacing: "normal",
+ fontWeight: 500,
+ height: BUTTON_MD_HEIGHT,
+ padding: "8px 16px",
+ borderRadius: "6px",
+ fontSize: 14,
+
+ whiteSpace: "nowrap",
+ ":focus-visible": {
+ outline: `2px solid ${theme.palette.primary.main}`,
+ },
+
+ "& .MuiLoadingButton-loadingIndicator": {
+ width: 14,
+ height: 14,
+ },
+
+ "& .MuiLoadingButton-loadingIndicator .MuiCircularProgress-root": {
+ width: "inherit !important",
+ height: "inherit !important",
+ },
+ }),
+ sizeSmall: {
+ height: BUTTON_SM_HEIGHT,
+ },
+ sizeLarge: {
+ height: BUTTON_LG_HEIGHT,
+ },
+ ["sizeXlarge" as any]: {
+ height: BUTTON_XL_HEIGHT,
+ },
+ outlined: ({ theme }) => ({
+ ":hover": {
+ border: `1px solid ${theme.palette.secondary.main}`,
+ },
+ }),
+ ["outlinedNeutral" as any]: {
+ borderColor: tw.zinc[600],
+
+ "&.Mui-disabled": {
+ borderColor: tw.zinc[700],
+ color: tw.zinc[500],
+
+ "& > .MuiLoadingButton-loadingIndicator": {
+ color: tw.zinc[500],
+ },
+ },
+ },
+ ["containedNeutral" as any]: {
+ backgroundColor: tw.zinc[800],
+
+ "&:hover": {
+ backgroundColor: tw.zinc[700],
+ },
+ },
+ iconSizeMedium: {
+ "& > .MuiSvgIcon-root": {
+ fontSize: 14,
+ },
+ },
+ iconSizeSmall: {
+ "& > .MuiSvgIcon-root": {
+ fontSize: 13,
+ },
+ },
+ startIcon: {
+ marginLeft: "-2px",
+ },
+ },
+ },
+ MuiButtonGroup: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ ">button:hover+button": {
+ // The !important is unfortunate, but necessary for the border.
+ borderLeftColor: `${theme.palette.secondary.main} !important`,
+ },
+ }),
+ },
+ },
+ ["MuiLoadingButton" as any]: {
+ defaultProps: {
+ variant: "outlined",
+ color: "neutral",
+ },
+ },
+ MuiTableContainer: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ borderRadius,
+ border: `1px solid ${theme.palette.divider}`,
+ }),
+ },
+ },
+ MuiTable: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ borderCollapse: "unset",
+ border: "none",
+ boxShadow: `0 0 0 1px ${theme.palette.background.default} inset`,
+ overflow: "hidden",
+
+ "& td": {
+ paddingTop: 16,
+ paddingBottom: 16,
+ background: "transparent",
+ },
+
+ [theme.breakpoints.down("md")]: {
+ minWidth: 1000,
+ },
+ }),
+ },
+ },
+ MuiTableCell: {
+ styleOverrides: {
+ head: ({ theme }) => ({
+ fontSize: 14,
+ color: theme.palette.text.secondary,
+ fontWeight: 600,
+ background: theme.palette.background.paper,
+ }),
+ root: ({ theme }) => ({
+ fontSize: 16,
+ background: theme.palette.background.paper,
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ padding: "12px 8px",
+ // This targets the first+last td elements, and also the first+last elements
+ // of a TableCellLink.
+ "&:not(:only-child):first-of-type, &:not(:only-child):first-of-type > a":
+ {
+ paddingLeft: 32,
+ },
+ "&:not(:only-child):last-child, &:not(:only-child):last-child > a": {
+ paddingRight: 32,
+ },
+ }),
+ },
+ },
+ MuiTableRow: {
+ styleOverrides: {
+ root: {
+ "&:last-child .MuiTableCell-body": {
+ borderBottom: 0,
+ },
+ },
+ },
+ },
+ MuiLink: {
+ defaultProps: {
+ underline: "hover",
+ },
+ },
+ MuiPaper: {
+ defaultProps: {
+ elevation: 0,
+ },
+ styleOverrides: {
+ root: ({ theme }) => ({
+ border: `1px solid ${theme.palette.divider}`,
+ backgroundImage: "none",
+ }),
+ },
+ },
+ MuiSkeleton: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ backgroundColor: theme.palette.divider,
+ }),
+ },
+ },
+ MuiLinearProgress: {
+ styleOverrides: {
+ root: {
+ borderRadius: 999,
+ },
+ },
+ },
+ MuiChip: {
+ styleOverrides: {
+ root: {
+ backgroundColor: tw.zinc[600],
+ },
+ },
+ },
+ MuiMenu: {
+ defaultProps: {
+ anchorOrigin: {
+ vertical: "bottom",
+ horizontal: "right",
+ },
+ transformOrigin: {
+ vertical: "top",
+ horizontal: "right",
+ },
+ },
+ styleOverrides: {
+ paper: {
+ marginTop: 8,
+ borderRadius: 4,
+ padding: "4px 0",
+ minWidth: 160,
+ },
+ root: {
+ // It should be the same as the menu padding
+ "& .MuiDivider-root": {
+ marginTop: `4px !important`,
+ marginBottom: `4px !important`,
+ },
+ },
+ },
+ },
+ MuiMenuItem: {
+ styleOverrides: {
+ root: {
+ gap: 12,
+
+ "& .MuiSvgIcon-root": {
+ fontSize: 20,
+ },
+ },
+ },
+ },
+ MuiSnackbar: {
+ styleOverrides: {
+ anchorOriginBottomRight: {
+ bottom: `${24 + 36}px !important`, // 36 is the bottom bar height
+ },
+ },
+ },
+ MuiSnackbarContent: {
+ styleOverrides: {
+ root: {
+ borderRadius: "4px !important",
+ },
+ },
+ },
+ MuiTextField: {
+ defaultProps: {
+ InputLabelProps: {
+ shrink: true,
+ },
+ },
+ },
+ MuiInputBase: {
+ defaultProps: {
+ color: "primary",
+ },
+ styleOverrides: {
+ root: {
+ height: BUTTON_LG_HEIGHT,
+ },
+ sizeSmall: {
+ height: BUTTON_MD_HEIGHT,
+ fontSize: 14,
+ },
+ multiline: {
+ height: "auto",
+ },
+ ["colorPrimary" as any]: {
+ // Same as button
+ "& .MuiOutlinedInput-notchedOutline": {
+ borderColor: tw.zinc[600],
+ },
+ // The default outlined input color is white, which seemed jarring.
+ "&:hover:not(.Mui-error):not(.Mui-focused) .MuiOutlinedInput-notchedOutline":
+ {
+ borderColor: tw.zinc[500],
+ },
+ },
+ },
+ },
+ MuiFormHelperText: {
+ defaultProps: {
+ sx: {
+ marginLeft: 0,
+ marginTop: 1,
+ },
+ },
+ },
+ MuiRadio: {
+ defaultProps: {
+ disableRipple: true,
+ },
+ },
+ MuiCheckbox: {
+ styleOverrides: {
+ root: {
+ /**
+ * Adds focus styling to checkboxes (which doesn't exist normally, for
+ * some reason?).
+ *
+ * The checkbox component is a root span with a checkbox input inside
+ * it. MUI does not allow you to use selectors like (& input) to
+ * target the inner checkbox (even though you can use & td to style
+ * tables). Tried every combination of selector possible (including
+ * lots of !important), and the main issue seems to be that the
+ * styling just never gets processed for it to get injected into the
+ * CSSOM.
+ *
+ * Had to settle for adding styling to the span itself (which does
+ * make the styling more obvious, even if there's not much room for
+ * customization).
+ */
+ "&.Mui-focusVisible": {
+ boxShadow: `0 0 0 2px ${tw.blue[400]}`,
+ },
+
+ "&.Mui-disabled": {
+ color: tw.zinc[500],
+ },
+ },
+ },
+ },
+ MuiSwitch: {
+ defaultProps: { color: "primary" },
+ styleOverrides: {
+ root: {
+ ".Mui-focusVisible .MuiSwitch-thumb": {
+ // Had to thicken outline to make sure that the focus color didn't
+ // bleed into the thumb and was still easily-visible
+ boxShadow: `0 0 0 3px ${tw.blue[400]}`,
+ },
+ },
+ },
+ },
+ MuiAutocomplete: {
+ styleOverrides: {
+ root: {
+ // Not sure why but since the input has padding we don't need it here
+ "& .MuiInputBase-root": {
+ padding: 0,
+ },
+ },
+ },
+ },
+ MuiList: {
+ defaultProps: {
+ disablePadding: true,
+ },
+ },
+ MuiTabs: {
+ defaultProps: {
+ textColor: "primary",
+ indicatorColor: "primary",
+ },
+ },
+ MuiTooltip: {
+ styleOverrides: {
+ tooltip: ({ theme }) => ({
+ lineHeight: "150%",
+ borderRadius: 4,
+ background: theme.palette.divider,
+ padding: "8px 16px",
+ }),
+ },
+ },
+ MuiAlert: {
+ defaultProps: {
+ variant: "outlined",
+ },
+ styleOverrides: {
+ root: ({ theme }) => ({
+ background: theme.palette.background.paper,
+ }),
+ action: {
+ paddingTop: 2, // Idk why it is not aligned as expected
+ },
+ icon: {
+ fontSize: 16,
+ marginTop: "4px", // The size of text is 24 so (24 - 16)/2 = 4
+ },
+ message: ({ theme }) => ({
+ color: theme.palette.text.primary,
+ }),
+ outlinedWarning: ({ theme }) => ({
+ [`& .${alertClasses.icon}`]: {
+ color: theme.palette.warning.light,
+ },
+ }),
+ outlinedInfo: ({ theme }) => ({
+ [`& .${alertClasses.icon}`]: {
+ color: theme.palette.primary.light,
+ },
+ }),
+ outlinedError: ({ theme }) => ({
+ [`& .${alertClasses.icon}`]: {
+ color: theme.palette.error.light,
+ },
+ }),
+ },
+ },
+ MuiAlertTitle: {
+ styleOverrides: {
+ root: {
+ fontSize: "inherit",
+ marginBottom: 0,
+ },
+ },
+ },
+
+ MuiIconButton: {
+ styleOverrides: {
+ root: {
+ "&.Mui-focusVisible": {
+ boxShadow: `0 0 0 2px ${tw.blue[400]}`,
+ },
+ },
+ },
+ },
+};
diff --git a/site/src/theme/tailwind.ts b/site/src/theme/tailwindColors.ts
similarity index 100%
rename from site/src/theme/tailwind.ts
rename to site/src/theme/tailwindColors.ts