feat: add premium license banner for custom roles page (#14595)

* feat: initial commit for premium banners

* feat: update design and copy

* fix: fix format

* fix: cleanup

* fix: paywall stories and popoverpaywall

* fix: updates for review comments

* chore: remove references to enterprise license

* chore:n remove references to enterprise license

* fix: format

* chore: simplify branding colors

* fix: fix color references
This commit is contained in:
Jaayden Halko
2024-09-11 14:02:34 -04:00
committed by GitHub
parent 3301212972
commit 7de576b596
30 changed files with 215 additions and 177 deletions
@@ -4,12 +4,12 @@ import {
Badges,
DisabledBadge,
EnabledBadge,
EnterpriseBadge,
EntitledBadge,
HealthyBadge,
NotHealthyBadge,
NotReachableBadge,
NotRegisteredBadge,
PremiumBadge,
PreviewBadge,
} from "./Badges";
@@ -50,9 +50,9 @@ export const Disabled: Story = {
children: <DisabledBadge />,
},
};
export const Enterprise: Story = {
export const Premium: Story = {
args: {
children: <EnterpriseBadge />,
children: <PremiumBadge />,
},
};
export const Preview: Story = {
+2 -19
View File
@@ -106,31 +106,14 @@ export const DisabledBadge: FC = forwardRef<
);
});
export const EnterpriseBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
backgroundColor: theme.roles.notice.background,
border: `1px solid ${theme.roles.notice.outline}`,
color: theme.roles.notice.text,
}),
]}
>
Enterprise
</span>
);
};
export const PremiumBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
backgroundColor: theme.roles.notice.background,
border: `1px solid ${theme.roles.notice.outline}`,
backgroundColor: theme.branding.background,
border: `1px solid ${theme.branding.border}`,
color: theme.roles.notice.text,
}),
]}
@@ -9,12 +9,10 @@ const meta: Meta<typeof Paywall> = {
export default meta;
type Story = StoryObj<typeof Paywall>;
const Example: Story = {
export const Premium: Story = {
args: {
message: "Black Lotus",
description:
"Adds 3 mana of any single color of your choice to your mana pool, then is discarded. Tapping this artifact can be played as an interrupt.",
},
};
export { Example as Paywall };
+37 -25
View File
@@ -2,7 +2,7 @@ import type { Interpolation, Theme } from "@emotion/react";
import TaskAltIcon from "@mui/icons-material/TaskAlt";
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import { EnterpriseBadge } from "components/Badges/Badges";
import { PremiumBadge } from "components/Badges/Badges";
import { Stack } from "components/Stack/Stack";
import type { FC, ReactNode } from "react";
import { docs } from "utils/docs";
@@ -19,11 +19,19 @@ export const Paywall: FC<PaywallProps> = ({
documentationLink,
}) => {
return (
<div css={styles.root}>
<div
css={[
styles.root,
(theme) => ({
backgroundImage: `linear-gradient(160deg, transparent, ${theme.branding.background})`,
border: `1px solid ${theme.branding.border}`,
}),
]}
>
<div>
<Stack direction="row" alignItems="center" css={{ marginBottom: 24 }}>
<h5 css={styles.title}>{message}</h5>
<EnterpriseBadge />
<PremiumBadge />
</Stack>
{description && <p css={styles.description}>{description}</p>}
@@ -36,20 +44,24 @@ export const Paywall: FC<PaywallProps> = ({
Read the documentation
</Link>
</div>
<div css={styles.separator}></div>
<div css={styles.separator} />
<Stack direction="column" alignItems="center" spacing={3}>
<ul css={styles.featureList}>
<li css={styles.feature}>
<FeatureIcon /> Template access control
<FeatureIcon />
High availability & workspace proxies
</li>
<li css={styles.feature}>
<FeatureIcon /> User groups
<FeatureIcon />
Multi-org & role-based access control
</li>
<li css={styles.feature}>
<FeatureIcon /> 24 hour support
<FeatureIcon />
24x7 global support with SLA
</li>
<li css={styles.feature}>
<FeatureIcon /> Audit logs
<FeatureIcon />
Unlimited Git & external auth integrations
</li>
</ul>
<Button
@@ -60,7 +72,7 @@ export const Paywall: FC<PaywallProps> = ({
variant="outlined"
color="neutral"
>
Learn about Enterprise
Learn about Premium
</Button>
</Stack>
</div>
@@ -68,21 +80,25 @@ export const Paywall: FC<PaywallProps> = ({
};
const FeatureIcon: FC = () => {
return <TaskAltIcon css={styles.featureIcon} />;
return (
<TaskAltIcon
css={[
(theme) => ({
color: theme.branding.border,
}),
]}
/>
);
};
const styles = {
root: (theme) => ({
root: () => ({
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
minHeight: 300,
maxWidth: 920,
margin: "auto",
minHeight: 280,
padding: 24,
backgroundImage: `linear-gradient(160deg, transparent, ${theme.roles.active.background})`,
border: `1px solid ${theme.roles.active.fill.outline}`,
borderRadius: 8,
gap: 32,
}),
@@ -92,25 +108,21 @@ const styles = {
fontSize: 22,
margin: 0,
},
description: (theme) => ({
marginTop: 16,
description: () => ({
fontFamily: "inherit",
maxWidth: 420,
lineHeight: "160%",
color: theme.palette.text.secondary,
fontSize: 16,
maxWidth: 460,
fontSize: 14,
}),
separator: (theme) => ({
width: 1,
height: 220,
backgroundColor: theme.palette.divider,
backgroundColor: theme.branding.divider,
marginLeft: 8,
}),
featureList: {
listStyle: "none",
margin: 0,
marginRight: 8,
padding: "0 12px",
padding: "0 24px",
fontSize: 14,
fontWeight: 500,
},
@@ -9,19 +9,10 @@ const meta: Meta<typeof PopoverPaywall> = {
export default meta;
type Story = StoryObj<typeof PopoverPaywall>;
export const Enterprise: Story = {
args: {
message: "Black Lotus",
description:
"Adds 3 mana of any single color of your choice to your mana pool, then is discarded. Tapping this artifact can be played as an interrupt.",
},
};
export const Premium: Story = {
args: {
message: "Black Lotus",
description:
"Adds 3 mana of any single color of your choice to your mana pool, then is discarded. Tapping this artifact can be played as an interrupt.",
licenseType: "premium",
},
};
+26 -23
View File
@@ -2,7 +2,7 @@ import type { Interpolation, Theme } from "@emotion/react";
import TaskAltIcon from "@mui/icons-material/TaskAlt";
import Button from "@mui/material/Button";
import Link from "@mui/material/Link";
import { EnterpriseBadge, PremiumBadge } from "components/Badges/Badges";
import { PremiumBadge } from "components/Badges/Badges";
import { Stack } from "components/Stack/Stack";
import type { FC, ReactNode } from "react";
import { docs } from "utils/docs";
@@ -11,21 +11,27 @@ export interface PopoverPaywallProps {
message: string;
description?: ReactNode;
documentationLink?: string;
licenseType?: "enterprise" | "premium";
}
export const PopoverPaywall: FC<PopoverPaywallProps> = ({
message,
description,
documentationLink,
licenseType = "enterprise",
}) => {
return (
<div css={styles.root}>
<div
css={[
styles.root,
(theme) => ({
backgroundImage: `linear-gradient(160deg, transparent, ${theme.branding.background})`,
border: `1px solid ${theme.branding.border}`,
}),
]}
>
<div>
<Stack direction="row" alignItems="center" css={{ marginBottom: 18 }}>
<h5 css={styles.title}>{message}</h5>
{licenseType === "premium" ? <PremiumBadge /> : <EnterpriseBadge />}
<PremiumBadge />
</Stack>
{description && <p css={styles.description}>{description}</p>}
@@ -38,26 +44,21 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
Read the documentation
</Link>
</div>
<div css={styles.separator}></div>
<div css={styles.separator} />
<Stack direction="column" alignItems="center" spacing={2}>
<ul css={styles.featureList}>
<li css={styles.feature}>
<FeatureIcon /> Template access control
<FeatureIcon /> High availability & workspace proxies
</li>
<li css={styles.feature}>
<FeatureIcon /> User groups
<FeatureIcon /> Multi-org & role-based access control
</li>
<li css={styles.feature}>
<FeatureIcon /> 24 hour support
<FeatureIcon /> 24x7 global support with SLA
</li>
<li css={styles.feature}>
<FeatureIcon /> Audit logs
<FeatureIcon /> Unlimited Git & external auth integrations
</li>
{licenseType === "premium" && (
<li css={styles.feature}>
<FeatureIcon /> Organizations
</li>
)}
</ul>
<Button
href={docs("/enterprise")}
@@ -67,7 +68,7 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
variant="outlined"
color="neutral"
>
Learn about {licenseType === "premium" ? "Premium" : "Enterprise"}
Learn about Premium
</Button>
</Stack>
</div>
@@ -75,7 +76,15 @@ export const PopoverPaywall: FC<PopoverPaywallProps> = ({
};
const FeatureIcon: FC = () => {
return <TaskAltIcon css={styles.featureIcon} />;
return (
<TaskAltIcon
css={[
(theme) => ({
color: theme.branding.border,
}),
]}
/>
);
};
const styles = {
@@ -85,8 +94,6 @@ const styles = {
alignItems: "center",
maxWidth: 600,
padding: "24px 36px",
backgroundImage: `linear-gradient(160deg, transparent, ${theme.roles.active.background})`,
border: `1px solid ${theme.roles.active.fill.outline}`,
borderRadius: 8,
gap: 18,
}),
@@ -118,10 +125,6 @@ const styles = {
fontSize: 13,
fontWeight: 500,
},
featureIcon: (theme) => ({
color: theme.roles.active.fill.outline,
fontSize: "1.5em",
}),
feature: {
display: "flex",
alignItems: "center",
+1 -1
View File
@@ -138,7 +138,7 @@ export const AuditPageView: FC<AuditPageViewProps> = ({
<Cond>
<Paywall
message="Audit logs"
description="Audit logs allow you to monitor user operations on your deployment. You need an Enterprise license to use this feature."
description="Audit logs allow you to monitor user operations on your deployment. You need an Premium license to use this feature."
documentationLink={docs("/admin/audit-logs")}
/>
</Cond>
@@ -5,8 +5,8 @@ import type { UpdateAppearanceConfig } from "api/typesGenerated";
import {
Badges,
DisabledBadge,
EnterpriseBadge,
EntitledBadge,
PremiumBadge,
} from "components/Badges/Badges";
import { PopoverPaywall } from "components/Paywall/PopoverPaywall";
import {
@@ -64,13 +64,13 @@ export const AppearanceSettingsPageView: FC<
<Popover mode="hover">
<PopoverTrigger>
<span>
<EnterpriseBadge />
<PremiumBadge />
</span>
</PopoverTrigger>
<PopoverContent css={{ transform: "translateY(-28px)" }}>
<PopoverPaywall
message="Appearance"
description="With an Enterprise license, you can customize the appearance of your deployment."
description="With a Premium license, you can customize the appearance of your deployment."
documentationLink="https://coder.com/docs/admin/appearance"
/>
</PopoverContent>
@@ -7,7 +7,7 @@ import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import type { DeploymentValues, ExternalAuthConfig } from "api/typesGenerated";
import { Alert } from "components/Alert/Alert";
import { EnterpriseBadge } from "components/Badges/Badges";
import { PremiumBadge } from "components/Badges/Badges";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import type { FC } from "react";
import { docs } from "utils/docs";
@@ -45,9 +45,9 @@ export const ExternalAuthSettingsPageView: FC<
marginBottom: 24,
}}
>
<Alert severity="info" actions={<EnterpriseBadge key="enterprise" />}>
<Alert severity="info" actions={<PremiumBadge key="enterprise" />}>
Integrating with multiple External authentication providers is an
Enterprise feature.
Premium feature.
</Alert>
</div>
@@ -58,7 +58,7 @@ export const LicenseCard: FC<LicenseCardProps> = ({
title="Confirm License Removal"
confirmLoading={isRemoving}
confirmText="Remove"
description="Removing this license will disable all Enterprise features. You add a new license at any time."
description="Removing this license will disable all Premium features. You add a new license at any time."
/>
<Stack
direction="row"
@@ -57,7 +57,7 @@ const LicensesSettingsPageView: FC<Props> = ({
>
<SettingsHeader
title="Licenses"
description="Manage licenses to unlock Enterprise features."
description="Manage licenses to unlock Premium features."
/>
<Stack direction="row" spacing={2}>
@@ -3,7 +3,7 @@ import {
Badges,
DisabledBadge,
EnabledBadge,
EnterpriseBadge,
PremiumBadge,
} from "components/Badges/Badges";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
@@ -34,7 +34,7 @@ export const ObservabilitySettingsPageView: FC<
<Badges>
{featureAuditLogEnabled ? <EnabledBadge /> : <DisabledBadge />}
<EnterpriseBadge />
<PremiumBadge />
</Badges>
</div>
@@ -3,7 +3,7 @@ import {
Badges,
DisabledBadge,
EnabledBadge,
EnterpriseBadge,
PremiumBadge,
} from "components/Badges/Badges";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
@@ -56,7 +56,7 @@ export const SecuritySettingsPageView: FC<SecuritySettingsPageViewProps> = ({
<Badges>
{featureBrowserOnlyEnabled ? <EnabledBadge /> : <DisabledBadge />}
<EnterpriseBadge />
<PremiumBadge />
</Badges>
</div>
+2 -2
View File
@@ -47,7 +47,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
<Cond condition={!isTemplateRBACEnabled}>
<Paywall
message="Groups"
description="Organize users into groups with restricted access to templates. You need an Enterprise license to use this feature."
description="Organize users into groups with restricted access to templates. You need an Premium license to use this feature."
documentationLink={docs("/admin/groups")}
/>
</Cond>
@@ -58,7 +58,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
<TableRow>
<TableCell width="50%">Name</TableCell>
<TableCell width="49%">Users</TableCell>
<TableCell width="1%"></TableCell>
<TableCell width="1%" />
</TableRow>
</TableHead>
<TableBody>
@@ -86,7 +86,6 @@ export const CreateOrganizationPageView: FC<
message="Organizations"
description="Organizations allow you to run a Coder deployment with multiple platform teams, all with unique use cases, templates, and even underlying infrastructure."
documentationLink={docs("/guides/using-organizations")}
licenseType="premium"
/>
</PopoverContent>
</Popover>
@@ -21,6 +21,14 @@ export const NotEnabled: Story = {
},
};
export const NotEnabledEmptyTable: Story = {
args: {
roles: [],
canAssignOrgRole: true,
isCustomRolesEnabled: false,
},
};
export const Enabled: Story = {
args: {
roles: [MockRoleWithOrgPermissions],
@@ -19,6 +19,7 @@ import {
ThreeDotsButton,
} from "components/MoreMenu/MoreMenu";
import { Paywall } from "components/Paywall/Paywall";
import { Stack } from "components/Stack/Stack";
import {
TableLoaderSkeleton,
TableRowSkeleton,
@@ -44,75 +45,74 @@ export const CustomRolesPageView: FC<CustomRolesPageViewProps> = ({
const isLoading = roles === undefined;
const isEmpty = Boolean(roles && roles.length === 0);
return (
<>
<ChooseOne>
<Cond condition={!isCustomRolesEnabled}>
<Paywall
message="Custom Roles"
description="Create custom roles to assign a specific set of permissions to a user. You need an Enterprise license to use this feature."
documentationLink={docs("/admin/groups")}
/>
</Cond>
<Cond>
<TableContainer>
<Table>
<TableHead>
<Stack spacing={4}>
{!isCustomRolesEnabled && (
<Paywall
message="Custom Roles"
description="Custom Roles are available in the Premium plan and require an upgraded license. Please contact your Customer Success team for more information."
documentationLink={docs("/admin/groups")}
/>
)}
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell width="40%">Name</TableCell>
<TableCell width="59%">Permissions</TableCell>
<TableCell width="1%" />
</TableRow>
</TableHead>
<TableBody>
<ChooseOne>
<Cond condition={isLoading}>
<TableLoader />
</Cond>
<Cond condition={isEmpty}>
<TableRow>
<TableCell width="40%">Name</TableCell>
<TableCell width="59%">Permissions</TableCell>
<TableCell width="1%" />
<TableCell colSpan={999}>
<EmptyState
message="No custom roles yet"
description={
canAssignOrgRole && isCustomRolesEnabled
? "Create your first custom role"
: !isCustomRolesEnabled
? "Upgrade to a premium license to create a custom role"
: "You don't have permission to create a custom role"
}
cta={
canAssignOrgRole &&
isCustomRolesEnabled && (
<Button
component={RouterLink}
to="create"
startIcon={<AddOutlined />}
variant="contained"
>
Create custom role
</Button>
)
}
/>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
<ChooseOne>
<Cond condition={isLoading}>
<TableLoader />
</Cond>
</Cond>
<Cond condition={isEmpty}>
<TableRow>
<TableCell colSpan={999}>
<EmptyState
message="No custom roles yet"
description={
canAssignOrgRole
? "Create your first custom role"
: "You don't have permission to create a custom role"
}
cta={
canAssignOrgRole && (
<Button
component={RouterLink}
to="create"
startIcon={<AddOutlined />}
variant="contained"
>
Create custom role
</Button>
)
}
/>
</TableCell>
</TableRow>
</Cond>
<Cond>
{roles?.map((role) => (
<RoleRow
key={role.name}
role={role}
canAssignOrgRole={canAssignOrgRole}
onDelete={() => onDeleteRole(role)}
/>
))}
</Cond>
</ChooseOne>
</TableBody>
</Table>
</TableContainer>
</Cond>
</ChooseOne>
</>
<Cond>
{roles?.map((role) => (
<RoleRow
key={role.name}
role={role}
canAssignOrgRole={canAssignOrgRole}
onDelete={() => onDeleteRole(role)}
/>
))}
</Cond>
</ChooseOne>
</TableBody>
</Table>
</TableContainer>
</Stack>
);
};
@@ -47,7 +47,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
<Cond condition={!isTemplateRBACEnabled}>
<Paywall
message="Groups"
description="Organize users into groups with restricted access to templates. You need an Enterprise license to use this feature."
description="Organize users into groups with restricted access to templates. You need a Premium license to use this feature."
documentationLink={docs("/admin/groups")}
/>
</Cond>
@@ -58,7 +58,7 @@ export const GroupsPageView: FC<GroupsPageViewProps> = ({
<TableRow>
<TableCell width="50%">Name</TableCell>
<TableCell width="49%">Users</TableCell>
<TableCell width="1%"></TableCell>
<TableCell width="1%" />
</TableRow>
</TableHead>
<TableBody>
@@ -40,7 +40,7 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ oidcConfig }) => {
<Cond condition={false}>
<Paywall
message="IdP Sync"
description="Configure group and role mappings to manage permissions outside of Coder."
description="Configure group and role mappings to manage permissions outside of Coder. You need an Premium license to use this feature."
documentationLink={docs("/admin/groups")}
/>
</Cond>
@@ -8,7 +8,7 @@ import {
type UpdateTemplateMeta,
WorkspaceAppSharingLevels,
} from "api/typesGenerated";
import { EnterpriseBadge } from "components/Badges/Badges";
import { PremiumBadge } from "components/Badges/Badges";
import {
FormFields,
FormFooter,
@@ -212,8 +212,8 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
alignItems="center"
css={{ marginTop: 16 }}
>
<EnterpriseBadge />
<span>Enterprise license required to enabled.</span>
<PremiumBadge />
<span>Premium license required to enabled.</span>
</Stack>
)}
</StackLabelHelperText>
@@ -241,9 +241,9 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
/>
{!accessControlEnabled && (
<Stack direction="row" spacing={2} alignItems="center">
<EnterpriseBadge />
<PremiumBadge />
<FormHelperText>
Enterprise license required to deprecate templates.
Premium license required to deprecate templates.
{template.deprecated &&
" You cannot change the message, but you may remove it to mark this template as no longer deprecated."}
</FormHelperText>
@@ -281,9 +281,9 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
</TextField>
{!portSharingControlsEnabled && (
<Stack direction="row" spacing={2} alignItems="center">
<EnterpriseBadge />
<PremiumBadge />
<FormHelperText>
Enterprise license required to control max port sharing level.
Premium license required to control max port sharing level.
</FormHelperText>
</Stack>
)}
@@ -32,7 +32,7 @@ export const TemplatePermissionsPage: FC = () => {
{!isTemplateRBACEnabled ? (
<Paywall
message="Template permissions"
description="Control access of templates for users and groups to templates. You need an Enterprise license to use this feature."
description="Control access of templates for users and groups to templates. You need an Premium license to use this feature."
documentationLink={docs("/admin/rbac")}
/>
) : (
@@ -12,7 +12,7 @@ import type { GroupsByUserId } from "api/queries/groups";
import type * as TypesGen from "api/typesGenerated";
import { AvatarData } from "components/AvatarData/AvatarData";
import { AvatarDataSkeleton } from "components/AvatarData/AvatarDataSkeleton";
import { EnterpriseBadge } from "components/Badges/Badges";
import { PremiumBadge } from "components/Badges/Badges";
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
import { EmptyState } from "components/EmptyState/EmptyState";
import { LastSeen } from "components/LastSeen/LastSeen";
@@ -208,7 +208,7 @@ export const UsersTableBody: FC<UsersTableBodyProps> = ({
disabled={!canViewActivity}
>
View activity
{!canViewActivity && <EnterpriseBadge />}
{!canViewActivity && <PremiumBadge />}
</MoreMenuItem>
<MoreMenuItem
onClick={() => onResetUserPassword(user)}
+6
View File
@@ -0,0 +1,6 @@
export interface Branding {
background: string;
divider: string;
border: string;
text: string;
}
+9
View File
@@ -0,0 +1,9 @@
import type { Branding } from "../branding";
import colors from "../tailwindColors";
export default {
background: colors.violet[950],
divider: colors.violet[900],
border: colors.violet[400],
text: colors.violet[50],
} satisfies Branding;
+2
View File
@@ -1,4 +1,5 @@
import { forDarkThemes } from "../externalImages";
import branding from "./branding";
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
@@ -8,6 +9,7 @@ export default {
...muiTheme,
externalImages: forDarkThemes,
experimental,
branding,
monaco,
roles,
};
+9
View File
@@ -0,0 +1,9 @@
import type { Branding } from "../branding";
import colors from "../tailwindColors";
export default {
background: colors.violet[950],
divider: colors.violet[900],
border: colors.violet[400],
text: colors.violet[50],
} satisfies Branding;
+2
View File
@@ -1,4 +1,5 @@
import { forDarkThemes } from "../externalImages";
import branding from "./branding";
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
@@ -8,6 +9,7 @@ export default {
...muiTheme,
externalImages: forDarkThemes,
experimental,
branding,
monaco,
roles,
};
+5
View File
@@ -1,6 +1,7 @@
// biome-ignore lint/nursery/noRestrictedImports: We still use `Theme` as a basis for our actual theme, for now.
import type { Theme as MuiTheme } from "@mui/material/styles";
import type * as monaco from "monaco-editor";
import type { Branding } from "./branding";
import dark from "./dark";
import darkBlue from "./darkBlue";
import type { NewTheme } from "./experimental";
@@ -20,6 +21,10 @@ export interface Theme extends Omit<MuiTheme, "palette"> {
/** Theme properties that we're testing out but haven't committed to. */
experimental: NewTheme;
//** Theme colors related to marketing */
branding: Branding;
monaco: monaco.editor.IStandaloneThemeData;
externalImages: ExternalImageModeStyles;
}
+9
View File
@@ -0,0 +1,9 @@
import type { Branding } from "../branding";
import colors from "../tailwindColors";
export default {
background: colors.violet[100],
divider: colors.violet[300],
border: colors.violet[600],
text: colors.violet[950],
} satisfies Branding;
+2
View File
@@ -1,4 +1,5 @@
import { forLightThemes } from "../externalImages";
import branding from "./branding";
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
@@ -8,6 +9,7 @@ export default {
...muiTheme,
externalImages: forLightThemes,
experimental,
branding,
monaco,
roles,
};