feat(site): bring back dark blue (#11071)

This commit is contained in:
Kayla Washburn
2023-12-08 14:38:35 -07:00
committed by GitHub
parent 716759aacf
commit ebd6c1b573
36 changed files with 1762 additions and 932 deletions
-1
View File
@@ -1,5 +1,4 @@
import turbosnap from "vite-plugin-turbosnap";
import { mergeConfig } from "vite";
module.exports = {
stories: ["../src/**/*.stories.tsx"],
+3 -9
View File
@@ -6,22 +6,16 @@ import {
import { ThemeProvider as EmotionThemeProvider } from "@emotion/react";
import { withRouter } from "storybook-addon-react-router-v6";
import { HelmetProvider } from "react-helmet-async";
import { dark } from "theme/mui";
import { dark as experimental } from "theme/experimental";
import theme from "theme";
import colors from "theme/tailwind";
import "theme/globalFonts";
import { QueryClient, QueryClientProvider } from "react-query";
const theme = {
...dark,
experimental,
};
export const decorators = [
(Story) => (
<StyledEngineProvider injectFirst>
<MuiThemeProvider theme={theme}>
<EmotionThemeProvider theme={theme}>
<MuiThemeProvider theme={theme.dark}>
<EmotionThemeProvider theme={theme.dark}>
<CssBaseline />
<Story />
</EmotionThemeProvider>
+2 -2
View File
@@ -1,5 +1,5 @@
import type { Theme as MuiTheme } from "@mui/material/styles";
import type { Theme as CoderTheme } from "theme";
declare module "@emotion/react" {
interface Theme extends MuiTheme {}
interface Theme extends CoderTheme {}
}
-6
View File
@@ -24,9 +24,3 @@ declare module "@mui/material/Button" {
xlarge: true;
}
}
declare module "@mui/system" {
interface Theme {
experimental: NewTheme;
}
}
+3 -9
View File
@@ -6,8 +6,7 @@ import { HelmetProvider } from "react-helmet-async";
import { AppRouter } from "./AppRouter";
import { ErrorBoundary } from "./components/ErrorBoundary/ErrorBoundary";
import { GlobalSnackbar } from "./components/GlobalSnackbar/GlobalSnackbar";
import { dark } from "./theme/mui";
import { dark as experimental } from "./theme/experimental";
import theme from "./theme";
import "./theme/globalFonts";
import {
StyledEngineProvider,
@@ -30,16 +29,11 @@ const defaultQueryClient = new QueryClient({
},
});
const theme = {
...dark,
experimental,
};
export const ThemeProviders: FC<PropsWithChildren> = ({ children }) => {
return (
<StyledEngineProvider injectFirst>
<MuiThemeProvider theme={theme}>
<EmotionThemeProvider theme={theme}>
<MuiThemeProvider theme={theme.dark}>
<EmotionThemeProvider theme={theme.dark}>
<CssBaseline enableColorScheme />
{children}
</EmotionThemeProvider>
@@ -37,7 +37,6 @@ import { RocketIcon } from "components/Icons/RocketIcon";
import ErrorIcon from "@mui/icons-material/ErrorOutline";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
import { getDisplayWorkspaceStatus } from "utils/workspace";
import { colors } from "theme/colors";
import { HelpTooltipTitle } from "components/HelpTooltip/HelpTooltip";
import { Stack } from "components/Stack/Stack";
import { type ClassName, useClassName } from "hooks/useClassName";
@@ -373,9 +372,14 @@ const ValueSeparator: FC = () => {
};
const HealthIssue: FC<PropsWithChildren> = ({ children }) => {
const theme = useTheme();
return (
<Stack direction="row" spacing={1} alignItems="center">
<ErrorIcon css={{ width: 16, height: 16 }} htmlColor={colors.red[10]} />
<ErrorIcon
css={{ width: 16, height: 16 }}
htmlColor={theme.colors.red[10]}
/>
{children}
</Stack>
);
@@ -435,8 +439,8 @@ const styles = {
height: 16px;
}
`,
unhealthy: css`
background-color: ${colors.red[10]};
unhealthy: (theme) => css`
background-color: ${theme.colors.red[10]};
`,
group: css`
display: flex;
@@ -1,15 +1,14 @@
import Link from "@mui/material/Link";
import {
css,
type CSSObject,
type Interpolation,
type Theme,
useTheme,
} from "@emotion/react";
import Link from "@mui/material/Link";
import { css } from "@emotion/react";
import { useState } from "react";
import { type FC, useState } from "react";
import { Expander } from "components/Expander/Expander";
import { Pill } from "components/Pill/Pill";
import { colors } from "theme/colors";
export const Language = {
licenseIssue: "License Issue",
@@ -32,7 +31,7 @@ export interface LicenseBannerViewProps {
warnings: string[];
}
export const LicenseBannerView: React.FC<LicenseBannerViewProps> = ({
export const LicenseBannerView: FC<LicenseBannerViewProps> = ({
errors,
warnings,
}) => {
@@ -48,7 +47,9 @@ export const LicenseBannerView: React.FC<LicenseBannerViewProps> = ({
display: flex;
align-items: center;
padding: 12px;
background-color: ${type === "error" ? colors.red[10] : colors.orange[10]};
background-color: ${type === "error"
? theme.colors.red[10]
: theme.colors.orange[10]};
`;
if (messages.length === 1) {
@@ -10,13 +10,12 @@ import MenuIcon from "@mui/icons-material/Menu";
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
import { type FC, type ReactNode, useRef, useState } from "react";
import { NavLink, useLocation, useNavigate } from "react-router-dom";
import { colors } from "theme/colors";
import type * as TypesGen from "api/typesGenerated";
import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants";
import { ProxyContextValue } from "contexts/ProxyContext";
import type * as TypesGen from "api/typesGenerated";
import type { ProxyContextValue } from "contexts/ProxyContext";
import { displayError } from "components/GlobalSnackbar/utils";
import { CoderIcon } from "components/Icons/CoderIcon";
import { ProxyStatusLatency } from "components/ProxyStatusLatency/ProxyStatusLatency";
import { CoderIcon } from "components/Icons/CoderIcon";
import { usePermissions } from "hooks/usePermissions";
import { UserDropdown } from "./UserDropdown/UserDropdown";
@@ -124,8 +123,8 @@ export const NavbarView: FC<NavbarViewProps> = ({
<nav
css={{
height: navHeight,
borderBottom: `1px solid ${theme.palette.divider}`,
backgroundColor: theme.palette.background.paper,
borderBottom: `1px solid ${theme.palette.divider}`,
}}
>
<div css={styles.wrapper}>
@@ -290,9 +289,9 @@ const ProxyMenu: FC<ProxyMenuProps> = ({ proxyContextValue }) => {
<div
css={{
width: "100%",
maxWidth: "320px",
fontSize: 14,
padding: 16,
maxWidth: "320px",
lineHeight: "140%",
}}
>
@@ -308,12 +307,12 @@ const ProxyMenu: FC<ProxyMenuProps> = ({ proxyContextValue }) => {
Select a region nearest to you
</h4>
<p
css={(theme) => ({
css={{
fontSize: 13,
color: theme.palette.text.secondary,
lineHeight: "inherit",
marginTop: 0.5,
})}
}}
>
Workspace proxies improve terminal and web app connections to
workspaces. This does not apply to CLI connections. A region must be
@@ -457,7 +456,7 @@ const styles = {
},
link: (theme) => css`
align-items: center;
color: ${colors.gray[6]};
color: ${theme.colors.gray[6]};
display: flex;
flex: 1;
font-size: 16px;
@@ -1,12 +1,11 @@
import Badge from "@mui/material/Badge";
import { type FC, type PropsWithChildren } from "react";
import { colors } from "theme/colors";
import { type FC, type ReactNode } from "react";
import type * as TypesGen from "api/typesGenerated";
import { BUTTON_SM_HEIGHT, navHeight } from "theme/constants";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import { UserDropdownContent } from "./UserDropdownContent";
import { css } from "@emotion/react";
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
import {
Popover,
PopoverContent,
@@ -19,67 +18,49 @@ export interface UserDropdownProps {
supportLinks?: TypesGen.LinkConfig[];
onSignOut: () => void;
isDefaultOpen?: boolean;
children?: ReactNode;
}
export const UserDropdown: FC<PropsWithChildren<UserDropdownProps>> = ({
export const UserDropdown: FC<UserDropdownProps> = ({
buildInfo,
user,
supportLinks,
onSignOut,
isDefaultOpen,
}: UserDropdownProps) => {
}) => {
const theme = useTheme();
return (
<Popover isDefaultOpen={isDefaultOpen}>
{(popover) => (
<>
<PopoverTrigger>
<button
css={css`
background: none;
border: 0;
cursor: pointer;
height: ${navHeight}px;
padding: 12px 0;
&:hover {
background-color: transparent;
}
`}
data-testid="user-dropdown-trigger"
>
<div
css={{
display: "flex",
alignItems: "center",
minWidth: 0,
maxWidth: 300,
}}
>
<button css={styles.button} data-testid="user-dropdown-trigger">
<div css={styles.badgeContainer}>
<Badge overlap="circular">
<UserAvatar
css={{
width: BUTTON_SM_HEIGHT,
height: BUTTON_SM_HEIGHT,
fontSize: 16,
}}
css={styles.avatar}
username={user.username}
avatarURL={user.avatar_url}
/>
</Badge>
<DropdownArrow color={colors.gray[6]} close={popover.isOpen} />
<DropdownArrow
color={theme.colors.gray[6]}
close={popover.isOpen}
/>
</div>
</button>
</PopoverTrigger>
<PopoverContent
horizontal="right"
css={(theme) => ({
css={{
".MuiPaper-root": {
minWidth: "auto",
width: 260,
boxShadow: theme.shadows[6],
},
})}
}}
>
<UserDropdownContent
user={user}
@@ -93,3 +74,30 @@ export const UserDropdown: FC<PropsWithChildren<UserDropdownProps>> = ({
</Popover>
);
};
const styles = {
button: css`
background: none;
border: 0;
cursor: pointer;
height: ${navHeight}px;
padding: 12px 0;
&:hover {
background-color: transparent;
}
`,
badgeContainer: {
display: "flex",
alignItems: "center",
minWidth: 0,
maxWidth: 300,
},
avatar: {
width: BUTTON_SM_HEIGHT,
height: BUTTON_SM_HEIGHT,
fontSize: 16,
},
} satisfies Record<string, Interpolation<Theme>>;
@@ -1,6 +1,5 @@
import { Pill } from "components/Pill/Pill";
import ReactMarkdown from "react-markdown";
import { colors } from "theme/colors";
import { css, useTheme } from "@emotion/react";
import { readableForegroundColor } from "utils/colors";
@@ -38,7 +37,7 @@ export const ServiceBannerView: React.FC<ServiceBannerViewProps> = ({
align-items: center;
&.error {
background-color: ${colors.red[12]};
background-color: ${theme.colors.red[12]};
}
`}
>
+2 -3
View File
@@ -6,7 +6,6 @@ import Picker from "@emoji-mart/react";
import { type FC } from "react";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
import { Stack } from "components/Stack/Stack";
import { colors } from "theme/colors";
import data from "@emoji-mart/data/sets/14/twitter.json";
import icons from "theme/icons.json";
import {
@@ -103,8 +102,8 @@ const IconField: FC<IconFieldProps> = ({ onPickEmoji, ...textFieldProps }) => {
styles={css`
em-emoji-picker {
--rgb-background: ${theme.palette.background.paper};
--rgb-input: ${colors.gray[17]};
--rgb-color: ${colors.gray[4]};
--rgb-input: ${theme.colors.gray[17]};
--rgb-color: ${theme.colors.gray[4]};
// Hack to prevent the right side from being cut off
width: 350px;
+4 -5
View File
@@ -1,6 +1,5 @@
import { type FC, type ReactNode, useMemo, forwardRef } from "react";
import { css, type Interpolation, type Theme } from "@emotion/react";
import { colors } from "theme/colors";
import type { ThemeRole } from "theme/experimental";
export type PillType = ThemeRole | keyof typeof themeOverrides;
@@ -14,10 +13,10 @@ export interface PillProps {
}
const themeOverrides = {
neutral: {
backgroundColor: colors.gray[13],
borderColor: colors.gray[6],
},
neutral: (theme) => ({
backgroundColor: theme.colors.gray[13],
borderColor: theme.colors.gray[6],
}),
} satisfies Record<string, Interpolation<Theme>>;
const themeStyles = (type: ThemeRole) => (theme: Theme) => {
+2 -3
View File
@@ -18,7 +18,7 @@ import {
} from "components/WorkspaceBuildLogs/Logs";
import { useProxy } from "contexts/ProxyContext";
import {
FC,
type FC,
useCallback,
useEffect,
useLayoutEffect,
@@ -28,7 +28,6 @@ import {
} from "react";
import AutoSizer from "react-virtualized-auto-sizer";
import { FixedSizeList as List, ListOnScrollProps } from "react-window";
import { colors } from "theme/colors";
import { Stack } from "../Stack/Stack";
import { AgentLatency } from "./AgentLatency";
import { AgentMetadata } from "./AgentMetadata";
@@ -693,7 +692,7 @@ const styles = {
"&:hover": {
color: theme.palette.text.primary,
backgroundColor: colors.gray[14],
backgroundColor: theme.colors.gray[14],
},
"& svg": {
@@ -5,7 +5,6 @@ import OpenInNewOutlined from "@mui/icons-material/OpenInNewOutlined";
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import type { FC } from "react";
import { useQuery } from "react-query";
import { colors } from "theme/colors";
import { docs } from "utils/docs";
import { getAgentListeningPorts } from "api/api";
import type {
@@ -195,7 +194,7 @@ const classNames = {
} satisfies Record<string, ClassName>;
const styles = {
portCount: {
portCount: (theme) => ({
fontSize: 12,
fontWeight: 500,
height: 20,
@@ -205,9 +204,9 @@ const styles = {
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: colors.gray[11],
backgroundColor: theme.colors.gray[11],
marginLeft: 8,
},
}),
portLink: (theme) => ({
color: theme.palette.text.primary,
@@ -5,7 +5,7 @@ import Switch from "@mui/material/Switch";
import TextField from "@mui/material/TextField";
import Link from "@mui/material/Link";
import { useTheme } from "@emotion/react";
import { useState } from "react";
import { type FC, useState } from "react";
import { BlockPicker } from "react-color";
import { useFormik } from "formik";
import type { UpdateAppearanceConfig } from "api/typesGenerated";
@@ -19,7 +19,6 @@ import {
import { Fieldset } from "components/DeploySettingsLayout/Fieldset";
import { Stack } from "components/Stack/Stack";
import { getFormHelpers } from "utils/formUtils";
import { colors } from "theme/colors";
export type AppearanceSettingsPageViewProps = {
appearance: UpdateAppearanceConfig;
@@ -30,14 +29,11 @@ export type AppearanceSettingsPageViewProps = {
) => void;
};
const fallbackBgColor = colors.blue[7];
export const AppearanceSettingsPageView = ({
appearance,
isEntitled,
onSaveAppearance,
}: AppearanceSettingsPageViewProps): JSX.Element => {
export const AppearanceSettingsPageView: FC<
AppearanceSettingsPageViewProps
> = ({ appearance, isEntitled, onSaveAppearance }) => {
const theme = useTheme();
const fallbackBgColor = theme.colors.blue[7];
const applicationNameForm = useFormik<{
application_name: string;
@@ -49,7 +49,6 @@ import {
} from "components/HelpTooltip/HelpTooltip";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import { getLatencyColor } from "utils/latency";
import { colors } from "theme/colors";
import { Loader } from "components/Loader/Loader";
import {
insightsTemplate,
@@ -412,7 +411,7 @@ const TemplateUsagePanel: FC<TemplateUsagePanelProps> = ({
const totalInSeconds =
validUsage?.reduce((total, usage) => total + usage.seconds, 0) ?? 1;
const usageColors = chroma
.scale([colors.green[8], colors.blue[8]])
.scale([theme.colors.green[8], theme.colors.blue[8]])
.mode("lch")
.colors(validUsage?.length ?? 0);
// The API returns a row for each app, even if the user didn't use it.
@@ -1,6 +1,7 @@
import { type CSSObject, type Interpolation, type Theme } from "@emotion/react";
import Button from "@mui/material/Button";
import TableCell from "@mui/material/TableCell";
import { type FC } from "react";
import { useNavigate } from "react-router-dom";
import type { TemplateVersion } from "api/typesGenerated";
import { Pill } from "components/Pill/Pill";
@@ -9,7 +10,6 @@ import { TimelineEntry } from "components/Timeline/TimelineEntry";
import { UserAvatar } from "components/UserAvatar/UserAvatar";
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
import { useClickableTableRow } from "hooks/useClickableTableRow";
import { colors } from "theme/colors";
export interface VersionRowProps {
version: TemplateVersion;
@@ -19,7 +19,7 @@ export interface VersionRowProps {
onArchiveClick?: (templateVersionId: string) => void;
}
export const VersionRow: React.FC<VersionRowProps> = ({
export const VersionRow: FC<VersionRowProps> = ({
version,
isActive,
isLatest,
@@ -39,7 +39,7 @@ export const VersionRow: React.FC<VersionRowProps> = ({
<TimelineEntry
data-testid={`version-${version.id}`}
{...clickableProps}
css={[styles.row, isActive && styles.active]}
css={[styles.row]}
className={clickableProps.className}
>
<TableCell css={styles.versionCell}>
@@ -130,7 +130,7 @@ const styles = {
row: (theme) => ({
"&:hover $promoteButton": {
color: theme.palette.text.primary,
borderColor: colors.gray[11],
borderColor: theme.colors.gray[11],
"&:hover": {
borderColor: theme.palette.text.primary,
},
@@ -146,10 +146,6 @@ const styles = {
padding: "16px 32px",
},
active: (theme) => ({
backgroundColor: theme.palette.background.paper,
}),
versionCell: {
padding: "0 !important",
position: "relative",
@@ -8,7 +8,6 @@ import { type CSSProperties, type FC, useState } from "react";
import { css } from "@emotion/react";
import { FileTree } from "utils/filetree";
import { DockerIcon } from "components/Icons/DockerIcon";
import { colors } from "theme/colors";
const sortFileTree = (fileTree: FileTree) => (a: string, b: string) => {
const contentA = fileTree[a];
@@ -28,13 +27,21 @@ type ContextMenu = {
clientY: number;
};
export const FileTreeView: FC<{
interface FileTreeViewProps {
onSelect: (path: string) => void;
onDelete: (path: string) => void;
onRename: (path: string) => void;
fileTree: FileTree;
activePath?: string;
}> = ({ fileTree, activePath, onDelete, onRename, onSelect }) => {
}
export const FileTreeView: FC<FileTreeViewProps> = ({
fileTree,
activePath,
onDelete,
onRename,
onSelect,
}) => {
const [contextMenu, setContextMenu] = useState<ContextMenu | undefined>();
const buildTreeItems = (
@@ -51,7 +58,7 @@ export const FileTreeView: FC<{
icon = <FileTypeMarkdown />;
}
if (filename.endsWith("Dockerfile")) {
icon = <FileTypeDockerfile />;
icon = <DockerIcon />;
}
return (
@@ -93,7 +100,7 @@ export const FileTreeView: FC<{
&.active {
& > .MuiTreeItem-content {
color: ${theme.palette.text.primary};
background: ${colors.gray[14]};
background: ${theme.colors.gray[14]};
pointer-events: none;
}
}
@@ -206,7 +213,7 @@ export const FileTreeView: FC<{
);
};
const FileTypeTerraform = () => (
const FileTypeTerraform: FC = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="#813cf3">
<title>file_type_terraform</title>
<polygon points="12.042 6.858 20.071 11.448 20.071 20.462 12.042 15.868 12.042 6.858 12.042 6.858" />
@@ -216,7 +223,7 @@ const FileTypeTerraform = () => (
</svg>
);
const FileTypeMarkdown = () => (
const FileTypeMarkdown: FC = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="#755838">
<rect
x="2.5"
@@ -232,5 +239,3 @@ const FileTypeMarkdown = () => (
<polygon points="22.955 20.636 18.864 16.136 21.591 16.136 21.591 11.364 24.318 11.364 24.318 16.136 27.045 16.136 22.955 20.636" />
</svg>
);
const FileTypeDockerfile = () => <DockerIcon />;
@@ -112,10 +112,10 @@ const styles = {
},
},
featuredExamples: (theme) => ({
featuredExamples: {
display: "flex",
flexWrap: "wrap",
justifyContent: "center",
gap: theme.spacing(2),
}),
gap: 16,
},
} satisfies Record<string, Interpolation<Theme>>;
@@ -36,7 +36,6 @@ import {
import { EmptyTemplates } from "./EmptyTemplates";
import { useClickableTableRow } from "hooks/useClickableTableRow";
import type { Template, TemplateExample } from "api/typesGenerated";
import { colors } from "theme/colors";
import ArrowForwardOutlined from "@mui/icons-material/ArrowForwardOutlined";
import { Avatar } from "components/Avatar/Avatar";
import { ErrorAlert } from "components/Alert/ErrorAlert";
@@ -275,7 +274,7 @@ const styles = {
tableRow: (theme) => ({
"&:hover .actionButton": {
color: theme.palette.text.primary,
borderColor: colors.gray[11],
borderColor: theme.colors.gray[11],
"&:hover": {
borderColor: theme.palette.text.primary,
},
+6 -9
View File
@@ -2,7 +2,6 @@ import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import { type FC, useCallback, useEffect, useRef, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { colors } from "theme/colors";
import { v4 as uuidv4 } from "uuid";
import * as XTerm from "xterm";
import { WebglAddon } from "xterm-addon-webgl";
@@ -43,8 +42,9 @@ export const Language = {
};
const TerminalPage: FC = () => {
const theme = useTheme();
const navigate = useNavigate();
const { proxy } = useProxy();
const { proxy, proxyLatencies } = useProxy();
const params = useParams() as { username: string; workspace: string };
const username = params.username.replace("@", "");
const xtermRef = useRef<HTMLDivElement>(null);
@@ -68,11 +68,8 @@ const TerminalPage: FC = () => {
? getMatchingAgentOrFirst(workspace.data, workspaceNameParts?.[1])
: undefined;
const dashboard = useDashboard();
const proxyContext = useProxy();
const selectedProxy = proxyContext.proxy.proxy;
const latency = selectedProxy
? proxyContext.proxyLatencies[selectedProxy.id]
: undefined;
const selectedProxy = proxy.proxy;
const latency = selectedProxy ? proxyLatencies[selectedProxy.id] : undefined;
const lifecycleState = workspaceAgent?.lifecycle_state;
const prevLifecycleState = useRef(lifecycleState);
@@ -113,7 +110,7 @@ const TerminalPage: FC = () => {
fontFamily: MONOSPACE_FONT_FAMILY,
fontSize: 16,
theme: {
background: colors.gray[16],
background: theme.colors.gray[16],
},
});
if (renderer === "webgl") {
@@ -149,7 +146,7 @@ const TerminalPage: FC = () => {
window.removeEventListener("resize", listener);
terminal.dispose();
};
}, [renderer, config.isLoading, xtermRef, handleWebLinkRef]);
}, [theme, renderer, config.isLoading, xtermRef, handleWebLinkRef]);
// Updates the reconnection token into the URL if necessary.
useEffect(() => {
@@ -1,5 +1,4 @@
import { type FC, type HTMLAttributes } from "react";
import { colors } from "theme/colors";
export const Sidebar: FC<HTMLAttributes<HTMLElement>> = ({
children,
@@ -33,7 +32,7 @@ export const SidebarItem: FC<SidebarItemProps> = ({
return (
<button
css={(theme) => ({
background: active ? colors.gray[13] : "none",
background: active ? theme.colors.gray[13] : "none",
border: "none",
fontSize: 14,
width: "100%",
@@ -1,66 +1,12 @@
import { Workspace, CreateWorkspaceBuildRequest } from "api/typesGenerated";
import { useId, useState, FormEvent } from "react";
import { type FC, type FormEvent, useId, useState } from "react";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { type Interpolation, type Theme } from "@emotion/react";
import { colors } from "theme/colors";
import TextField from "@mui/material/TextField";
import { docs } from "utils/docs";
import Link from "@mui/material/Link";
import Checkbox from "@mui/material/Checkbox";
const styles = {
workspaceInfo: (theme) => ({
display: "flex",
justifyContent: "space-between",
borderRadius: 6,
padding: 16,
marginBottom: 20,
lineHeight: "1.3em",
border: `1px solid ${theme.palette.divider}`,
"& .name": {
fontSize: 16,
fontWeight: 600,
color: theme.palette.text.primary,
},
"& .label": {
fontSize: 12,
color: theme.palette.text.secondary,
},
"& .info": {
fontSize: 12,
fontWeight: 500,
color: theme.palette.text.primary,
},
}),
orphanContainer: (theme) => ({
marginTop: 24,
display: "flex",
backgroundColor: colors.orange[15],
justifyContent: "space-between",
border: `1px solid ${colors.orange[11]}`,
borderRadius: 8,
padding: 12,
gap: 8,
lineHeight: "18px",
"& .option": {
color: colors.orange[11],
"&.Mui-checked": {
color: colors.orange[11],
},
},
"& .info": {
fontSize: 14,
fontWeight: 600,
color: theme.experimental.roles.danger.text,
},
}),
} satisfies Record<string, Interpolation<Theme>>;
interface WorkspaceDeleteDialogProps {
workspace: Workspace;
canUpdateTemplate: boolean;
@@ -69,15 +15,15 @@ interface WorkspaceDeleteDialogProps {
onConfirm: (arg: CreateWorkspaceBuildRequest["orphan"]) => void;
workspaceBuildDateStr: string;
}
export const WorkspaceDeleteDialog = (props: WorkspaceDeleteDialogProps) => {
const {
workspace,
canUpdateTemplate,
isOpen,
onCancel,
onConfirm,
workspaceBuildDateStr,
} = props;
export const WorkspaceDeleteDialog: FC<WorkspaceDeleteDialogProps> = ({
workspace,
canUpdateTemplate,
isOpen,
onCancel,
onConfirm,
workspaceBuildDateStr,
}) => {
const hookId = useId();
const [userConfirmationText, setUserConfirmationText] = useState("");
const [orphanWorkspace, setOrphanWorkspace] =
@@ -188,3 +134,56 @@ export const WorkspaceDeleteDialog = (props: WorkspaceDeleteDialogProps) => {
/>
);
};
const styles = {
workspaceInfo: (theme) => ({
display: "flex",
justifyContent: "space-between",
borderRadius: 6,
padding: 16,
marginBottom: 20,
lineHeight: "1.3em",
border: `1px solid ${theme.palette.divider}`,
"& .name": {
fontSize: 16,
fontWeight: 600,
color: theme.palette.text.primary,
},
"& .label": {
fontSize: 12,
color: theme.palette.text.secondary,
},
"& .info": {
fontSize: 12,
fontWeight: 500,
color: theme.palette.text.primary,
},
}),
orphanContainer: (theme) => ({
marginTop: 24,
display: "flex",
backgroundColor: theme.colors.orange[15],
justifyContent: "space-between",
border: `1px solid ${theme.colors.orange[11]}`,
borderRadius: 8,
padding: 12,
gap: 8,
lineHeight: "18px",
"& .option": {
color: theme.colors.orange[11],
"&.Mui-checked": {
color: theme.colors.orange[11],
},
},
"& .info": {
fontSize: 14,
fontWeight: 600,
color: theme.experimental.roles.danger.text,
},
}),
} satisfies Record<string, Interpolation<Theme>>;
+2 -3
View File
@@ -3,7 +3,6 @@ import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import { useTheme } from "@emotion/react";
import { Stack } from "components/Stack/Stack";
import { colors } from "theme/colors";
dayjs.extend(relativeTime);
@@ -41,7 +40,7 @@ export const LastUsed: FC<LastUsedProps> = ({ lastUsedAt }) => {
);
if (t.isAfter(now.subtract(1, "hour"))) {
circle = <Circle color={colors.green[9]} />;
circle = <Circle color={theme.colors.green[9]} />;
// Since the agent reports on a 10m interval,
// the last_used_at can be inaccurate when recent.
message = "Now";
@@ -50,7 +49,7 @@ export const LastUsed: FC<LastUsedProps> = ({ lastUsedAt }) => {
} else if (t.isAfter(now.subtract(1, "month"))) {
circle = <Circle color={theme.palette.warning.light} />;
} else if (t.isAfter(now.subtract(100, "year"))) {
circle = <Circle color={colors.red[10]} />;
circle = <Circle color={theme.colors.red[10]} />;
} else {
// color = theme.palette.error.light
message = "Never";
+48 -50
View File
@@ -1,62 +1,60 @@
import tw from "./tailwind";
export const colors = {
white: "#fff",
export interface Colors {
white: string;
gray: {
17: tw.zinc[950],
16: tw.zinc[900],
14: tw.zinc[800],
13: tw.zinc[700],
12: tw.zinc[600],
11: tw.zinc[500],
9: tw.zinc[400],
6: tw.zinc[300],
4: tw.zinc[200],
2: tw.zinc[100],
1: tw.zinc[50],
},
17: string;
16: string;
14: string;
13: string;
12: string;
11: string;
9: string;
6: string;
4: string;
2: string;
1: string;
};
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],
},
15: string;
12: string;
10: string;
9: string;
8: string;
6: string;
2: string;
};
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],
},
15: string;
14: string;
12: string;
11: string;
10: string;
9: string;
7: string;
};
yellow: {
5: tw.yellow[300],
},
5: string;
};
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],
},
15: string;
13: string;
12: string;
11: string;
9: string;
8: string;
};
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],
},
};
14: string;
9: string;
8: string;
7: string;
6: string;
3: string;
1: string;
};
}
+62
View File
@@ -0,0 +1,62 @@
import tw from "../tailwind";
export default {
white: "#fff",
gray: {
17: tw.zinc[950],
16: tw.zinc[900],
14: tw.zinc[800],
13: tw.zinc[700],
12: tw.zinc[600],
11: tw.zinc[500],
9: tw.zinc[400],
6: tw.zinc[300],
4: tw.zinc[200],
2: tw.zinc[100],
1: tw.zinc[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],
},
};
+130
View File
@@ -0,0 +1,130 @@
import { type NewTheme } from "../experimental";
import colors from "../tailwind";
export default {
l1: {
background: colors.gray[950],
outline: colors.gray[700],
fill: colors.gray[600],
text: colors.white,
},
l2: {
background: colors.gray[900],
outline: colors.gray[700],
fill: "#f00",
text: colors.white,
disabled: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.gray[200],
},
hover: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
},
l3: {
background: colors.gray[800],
outline: colors.gray[700],
fill: colors.gray[600],
text: colors.white,
disabled: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.gray[200],
},
hover: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
},
roles: {
danger: {
background: colors.orange[950],
outline: colors.orange[500],
fill: colors.orange[600],
text: colors.orange[50],
disabled: {
background: colors.orange[950],
outline: colors.orange[800],
fill: colors.orange[800],
text: colors.orange[200],
},
hover: {
background: colors.orange[900],
outline: colors.orange[500],
fill: colors.orange[500],
text: colors.white,
},
},
error: {
background: colors.red[950],
outline: colors.red[500],
fill: colors.red[600],
text: colors.red[50],
},
warning: {
background: colors.amber[950],
outline: colors.amber[300],
fill: "#f00",
text: colors.amber[50],
},
notice: {
background: colors.yellow[950],
outline: colors.yellow[200],
fill: colors.yellow[500],
text: colors.yellow[50],
},
info: {
background: colors.blue[950],
outline: colors.blue[400],
fill: "#f00",
text: colors.blue[50],
},
success: {
background: colors.green[950],
outline: colors.green[500],
fill: colors.green[600],
text: colors.green[50],
disabled: {
background: colors.green[950],
outline: colors.green[800],
fill: colors.green[800],
text: colors.green[200],
},
hover: {
background: colors.green[900],
outline: colors.green[500],
fill: colors.green[500],
text: colors.white,
},
},
active: {
background: colors.sky[950],
outline: colors.sky[500],
fill: colors.sky[600],
text: colors.sky[50],
disabled: {
background: colors.sky[950],
outline: colors.sky[800],
fill: colors.sky[800],
text: colors.sky[200],
},
hover: {
background: colors.sky[900],
outline: colors.sky[500],
fill: colors.sky[500],
text: colors.white,
},
},
},
} satisfies NewTheme;
+9
View File
@@ -0,0 +1,9 @@
import colors from "./colors";
import experimental from "./experimental";
import muiTheme from "./mui";
export default {
...muiTheme,
colors,
experimental,
};
+551
View File
@@ -0,0 +1,551 @@
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";
let muiTheme = createTheme({
palette: {
mode: "dark",
primary: {
main: colors.blue[7],
contrastText: colors.blue[1],
light: colors.blue[6],
dark: colors.blue[9],
},
secondary: {
main: colors.gray[11],
contrastText: colors.gray[4],
dark: colors.gray[9],
},
background: {
default: colors.gray[17],
paper: colors.gray[16],
},
text: {
primary: colors.gray[1],
secondary: colors.gray[6],
disabled: colors.gray[9],
},
divider: colors.gray[13],
warning: {
light: colors.orange[9],
main: colors.orange[12],
dark: colors.orange[15],
},
success: {
main: colors.green[11],
dark: colors.green[12],
},
info: {
light: colors.blue[7],
main: colors.blue[9],
dark: colors.blue[14],
contrastText: colors.gray[4],
},
error: {
light: colors.red[6],
main: colors.red[8],
dark: colors.red[15],
contrastText: colors.gray[4],
},
action: {
hover: colors.gray[14],
},
neutral: {
main: colors.gray[1],
},
},
typography: {
fontFamily: BODY_FONT_FAMILY,
body1: {
fontSize: "1rem" /* 16px at default scaling */,
lineHeight: "160%",
},
body2: {
fontSize: "0.875rem" /* 14px at default scaling */,
lineHeight: "160%",
},
},
shape: {
borderRadius,
},
});
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,
},
},
},
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;
+62
View File
@@ -0,0 +1,62 @@
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],
},
};
+130
View File
@@ -0,0 +1,130 @@
import { type NewTheme } from "../experimental";
import colors from "../tailwind";
export default {
l1: {
background: colors.gray[950],
outline: colors.gray[700],
fill: colors.gray[600],
text: colors.white,
},
l2: {
background: colors.gray[900],
outline: colors.gray[700],
fill: "#f00",
text: colors.white,
disabled: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.gray[200],
},
hover: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
},
l3: {
background: colors.gray[800],
outline: colors.gray[700],
fill: colors.gray[600],
text: colors.white,
disabled: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.gray[200],
},
hover: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
},
roles: {
danger: {
background: colors.orange[950],
outline: colors.orange[500],
fill: colors.orange[600],
text: colors.orange[50],
disabled: {
background: colors.orange[950],
outline: colors.orange[800],
fill: colors.orange[800],
text: colors.orange[200],
},
hover: {
background: colors.orange[900],
outline: colors.orange[500],
fill: colors.orange[500],
text: colors.white,
},
},
error: {
background: colors.red[950],
outline: colors.red[500],
fill: colors.red[600],
text: colors.red[50],
},
warning: {
background: colors.amber[950],
outline: colors.amber[300],
fill: "#f00",
text: colors.amber[50],
},
notice: {
background: colors.yellow[950],
outline: colors.yellow[200],
fill: colors.yellow[500],
text: colors.yellow[50],
},
info: {
background: colors.blue[950],
outline: colors.blue[400],
fill: "#f00",
text: colors.blue[50],
},
success: {
background: colors.green[950],
outline: colors.green[500],
fill: colors.green[600],
text: colors.green[50],
disabled: {
background: colors.green[950],
outline: colors.green[800],
fill: colors.green[800],
text: colors.green[200],
},
hover: {
background: colors.green[900],
outline: colors.green[500],
fill: colors.green[500],
text: colors.white,
},
},
active: {
background: colors.sky[950],
outline: colors.sky[500],
fill: colors.sky[600],
text: colors.sky[50],
disabled: {
background: colors.sky[950],
outline: colors.sky[800],
fill: colors.sky[800],
text: colors.sky[200],
},
hover: {
background: colors.sky[900],
outline: colors.sky[500],
fill: colors.sky[500],
text: colors.white,
},
},
},
} satisfies NewTheme;
+9
View File
@@ -0,0 +1,9 @@
import colors from "./colors";
import experimental from "./experimental";
import muiTheme from "./mui";
export default {
...muiTheme,
colors,
experimental,
};
+551
View File
@@ -0,0 +1,551 @@
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";
let muiTheme = createTheme({
palette: {
mode: "dark",
primary: {
main: colors.blue[7],
contrastText: colors.blue[1],
light: colors.blue[6],
dark: colors.blue[9],
},
secondary: {
main: colors.gray[11],
contrastText: colors.gray[4],
dark: colors.gray[9],
},
background: {
default: colors.gray[17],
paper: colors.gray[16],
},
text: {
primary: colors.gray[1],
secondary: colors.gray[6],
disabled: colors.gray[9],
},
divider: colors.gray[13],
warning: {
light: colors.orange[9],
main: colors.orange[12],
dark: colors.orange[15],
},
success: {
main: colors.green[11],
dark: colors.green[12],
},
info: {
light: colors.blue[7],
main: colors.blue[9],
dark: colors.blue[14],
contrastText: colors.gray[4],
},
error: {
light: colors.red[6],
main: colors.red[8],
dark: colors.red[15],
contrastText: colors.gray[4],
},
action: {
hover: colors.gray[14],
},
neutral: {
main: colors.gray[1],
},
},
typography: {
fontFamily: BODY_FONT_FAMILY,
body1: {
fontSize: "1rem" /* 16px at default scaling */,
lineHeight: "160%",
},
body2: {
fontSize: "0.875rem" /* 14px at default scaling */,
lineHeight: "160%",
},
},
shape: {
borderRadius,
},
});
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,
},
},
},
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;
+7 -127
View File
@@ -1,12 +1,15 @@
import colors from "./tailwind";
export type ThemeRole = keyof NewTheme["roles"];
export type InteractiveThemeRole = keyof {
[K in keyof NewTheme["roles"] as NewTheme["roles"][K] extends InteractiveRole
? K
: never]: unknown;
};
export interface NewTheme {
l1: Role; // page background, things which sit at the "root level"
l2: InteractiveRole; // sidebars, table headers, navigation
l3: InteractiveRole; // buttons, inputs
modal: Role; // modals/popovers/dropdowns
roles: {
danger: InteractiveRole; // delete, immutable parameters, stuff that sucks to fix
@@ -15,7 +18,7 @@ export interface NewTheme {
notice: Role; // like info, but actionable. "this is fine, but you may want to..."
info: Role; // just sharing :)
success: InteractiveRole; // yay!! it's working!!
active: Role; // selected items, focused inputs, in progress
active: InteractiveRole; // selected items, focused inputs, in progress
};
}
@@ -31,126 +34,3 @@ export interface InteractiveRole extends Role {
disabled: Role;
hover: Role;
}
export const dark: NewTheme = {
l1: {
background: colors.gray[950],
outline: colors.gray[700],
fill: colors.gray[600],
text: colors.white,
},
l2: {
background: colors.gray[900],
outline: colors.gray[700],
fill: "#f00",
text: colors.white,
disabled: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.gray[200],
},
hover: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
},
l3: {
background: colors.gray[800],
outline: colors.gray[700],
fill: colors.gray[600],
text: colors.white,
disabled: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.gray[200],
},
hover: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
},
modal: {
background: "#f00",
outline: "#f00",
fill: "#f00",
text: colors.white,
},
roles: {
danger: {
background: colors.orange[950],
outline: colors.orange[500],
fill: colors.orange[600],
text: colors.orange[50],
disabled: {
background: colors.orange[950],
outline: colors.orange[600],
fill: colors.orange[800],
text: colors.orange[200],
},
hover: {
background: colors.orange[900],
outline: colors.orange[500],
fill: colors.orange[500],
text: colors.orange[50],
},
},
error: {
background: colors.red[950],
outline: colors.red[500],
fill: colors.red[600],
text: colors.red[50],
},
warning: {
background: colors.amber[950],
outline: colors.amber[300],
fill: "#f00",
text: colors.amber[50],
},
notice: {
background: colors.yellow[950],
outline: colors.yellow[200],
fill: colors.yellow[500],
text: colors.yellow[50],
},
info: {
background: colors.blue[950],
outline: colors.blue[400],
fill: "#f00",
text: colors.blue[50],
},
success: {
background: colors.green[950],
outline: colors.green[500],
fill: colors.green[600],
text: colors.green[50],
disabled: {
background: colors.green[950],
outline: colors.green[600],
fill: colors.green[800],
text: colors.green[200],
},
hover: {
background: colors.green[900],
outline: colors.green[500],
fill: colors.green[500],
text: colors.green[50],
},
},
active: {
background: colors.sky[950],
outline: colors.sky[500],
fill: colors.sky[600],
text: colors.sky[50],
},
},
};
+17
View File
@@ -0,0 +1,17 @@
import { type Theme as MuiTheme } from "@mui/material/styles";
import dark from "./dark";
import darkBlue from "./darkBlue";
import { type NewTheme } from "./experimental";
import { Colors } from "./colors";
export interface Theme extends MuiTheme {
colors: Colors;
experimental: NewTheme;
}
const theme = {
dark,
darkBlue,
} satisfies Record<string, Theme>;
export default theme;
-546
View File
@@ -1,16 +1,3 @@
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";
export type PaletteIndex =
| "primary"
| "secondary"
@@ -22,536 +9,3 @@ export type PaletteIndex =
| "success"
| "action"
| "neutral";
export let dark = createTheme({
palette: {
mode: "dark",
primary: {
main: colors.blue[7],
contrastText: colors.blue[1],
light: colors.blue[6],
dark: colors.blue[9],
},
secondary: {
main: colors.gray[11],
contrastText: colors.gray[4],
dark: colors.gray[9],
},
background: {
default: colors.gray[17],
paper: colors.gray[16],
},
text: {
primary: colors.gray[1],
secondary: colors.gray[6],
disabled: colors.gray[9],
},
divider: colors.gray[13],
warning: {
light: colors.orange[9],
main: colors.orange[12],
dark: colors.orange[15],
},
success: {
main: colors.green[11],
dark: colors.green[12],
},
info: {
light: colors.blue[7],
main: colors.blue[9],
dark: colors.blue[14],
contrastText: colors.gray[4],
},
error: {
light: colors.red[6],
main: colors.red[8],
dark: colors.red[15],
contrastText: colors.gray[4],
},
action: {
hover: colors.gray[14],
},
neutral: {
main: colors.gray[1],
},
},
typography: {
fontFamily: BODY_FONT_FAMILY,
body1: {
fontSize: "1rem" /* 16px at default scaling */,
lineHeight: "160%",
},
body2: {
fontSize: "0.875rem" /* 14px at default scaling */,
lineHeight: "160%",
},
},
shape: {
borderRadius,
},
});
dark = createTheme(dark, {
components: {
MuiCssBaseline: {
styleOverrides: `
html, body, #root, #storybook-root {
height: 100%;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 100px ${dark.palette.background.default} inset !important;
}
::placeholder {
color: ${dark.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 ${dark.palette.divider}`,
},
},
},
MuiTable: {
styleOverrides: {
root: ({ theme }) => ({
borderCollapse: "unset",
border: "none",
boxShadow: `0 0 0 1px ${dark.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: dark.palette.text.secondary,
fontWeight: 600,
background: dark.palette.background.paper,
},
root: {
fontSize: 16,
background: dark.palette.background.paper,
borderBottom: `1px solid ${dark.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 ${dark.palette.divider}`,
backgroundImage: "none",
},
},
},
MuiSkeleton: {
styleOverrides: {
root: {
backgroundColor: dark.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: dark.palette.divider,
},
},
},
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: dark.palette.warning.light,
},
},
outlinedInfo: {
[`& .${alertClasses.icon}`]: {
color: dark.palette.primary.light,
},
},
outlinedError: {
[`& .${alertClasses.icon}`]: {
color: dark.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);