mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat(site): bring back dark blue (#11071)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import turbosnap from "vite-plugin-turbosnap";
|
||||
import { mergeConfig } from "vite";
|
||||
|
||||
module.exports = {
|
||||
stories: ["../src/**/*.stories.tsx"],
|
||||
|
||||
@@ -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>
|
||||
|
||||
Vendored
+2
-2
@@ -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 {}
|
||||
}
|
||||
|
||||
Vendored
-6
@@ -24,9 +24,3 @@ declare module "@mui/material/Button" {
|
||||
xlarge: true;
|
||||
}
|
||||
}
|
||||
|
||||
declare module "@mui/system" {
|
||||
interface Theme {
|
||||
experimental: NewTheme;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-9
@@ -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]};
|
||||
}
|
||||
`}
|
||||
>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
-9
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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>>;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
@@ -0,0 +1,9 @@
|
||||
import colors from "./colors";
|
||||
import experimental from "./experimental";
|
||||
import muiTheme from "./mui";
|
||||
|
||||
export default {
|
||||
...muiTheme,
|
||||
colors,
|
||||
experimental,
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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],
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
@@ -0,0 +1,9 @@
|
||||
import colors from "./colors";
|
||||
import experimental from "./experimental";
|
||||
import muiTheme from "./mui";
|
||||
|
||||
export default {
|
||||
...muiTheme,
|
||||
colors,
|
||||
experimental,
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user