fix: resolve <Badges /> to use <Badge /> (#21747)

Continuing the work from #21740 

This pull-request updates all of our badges to use the `<Badge />`
component. This is inline with our Figma design/guidelines, so
going-forth and we're standardised across the application. I've added
`<EnterpriseBadge />` and `<DeprecatedBadge />` to the
`Badges.stories.tsx` so we can track these in future (they were missing
previously).

In `site/src/components/Form/Form.tsx` we were using these components
within a `<h2 />` which would cause invalid semantic HTML. I chose the
easy route around this and made them sit in their own `<header>` with a
flex.

### Preview

| Old | New |
| --- | --- |
| <img width="512" height="288" alt="BADGES_OLD"
src="https://github.com/user-attachments/assets/196b0a53-37b2-4aee-b66e-454ac0ff1271"
/> | <img width="512" height="288" alt="BADGES_OLD-1"
src="https://github.com/user-attachments/assets/f0fb2871-40e2-4f0d-972c-cbf4249cf2d7"
/> |
| <img width="512" height="288" alt="DEPRECATED_OLD"
src="https://github.com/user-attachments/assets/cce36b6c-e91a-47f6-8d20-02b9f40ea44e"
/> | <img width="512" height="289" alt="DEPRECATED_NEW"
src="https://github.com/user-attachments/assets/8a1f5168-d128-4733-819e-c1cb6641b83b"
/> |
| <img width="512" height="288" alt="ENTERPRISE_OLD"
src="https://github.com/user-attachments/assets/aba677ce-23c7-4820-913b-886d049f81ef"
/> | <img width="512" height="288" alt="ENTERPRISE_NEW"
src="https://github.com/user-attachments/assets/eca9729d-c98a-4848-9f10-28e42e2c3cd3"
/> |

---------

Co-authored-by: Ben Potter <me@bpmct.net>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jake Howell
2026-01-31 12:22:58 +11:00
committed by GitHub
parent 3d97f677e5
commit b14a709adb
4 changed files with 55 additions and 114 deletions
+2
View File
@@ -24,6 +24,8 @@ const badgeVariants = cva(
"border border-solid border-border-destructive bg-surface-red text-highlight-red shadow",
green:
"border border-solid border-border-green bg-surface-green text-highlight-green shadow",
purple:
"border border-solid border-border-purple bg-surface-purple text-highlight-purple shadow",
info: "border border-solid border-border-pending bg-surface-sky text-highlight-sky shadow",
},
size: {
@@ -2,8 +2,10 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
import {
AlphaBadge,
Badges,
DeprecatedBadge,
DisabledBadge,
EnabledBadge,
EnterpriseBadge,
EntitledBadge,
HealthyBadge,
NotHealthyBadge,
@@ -65,3 +67,13 @@ export const Alpha: Story = {
children: <AlphaBadge />,
},
};
export const Enterprise: Story = {
args: {
children: <EnterpriseBadge />,
},
};
export const Deprecated: Story = {
args: {
children: <DeprecatedBadge />,
},
};
+36 -111
View File
@@ -1,4 +1,3 @@
import type { Interpolation, Theme } from "@emotion/react";
import { Badge } from "components/Badge/Badge";
import { Stack } from "components/Stack/Stack";
import {
@@ -12,72 +11,50 @@ import {
type HTMLAttributes,
type PropsWithChildren,
} from "react";
import { cn } from "utils/cn";
const styles = {
badge: {
fontSize: 10,
height: 24,
fontWeight: 600,
textTransform: "uppercase",
letterSpacing: "0.085em",
padding: "0 12px",
borderRadius: 9999,
display: "flex",
alignItems: "center",
width: "fit-content",
whiteSpace: "nowrap",
},
enabledBadge: (theme) => ({
border: `1px solid ${theme.roles.success.outline}`,
backgroundColor: theme.roles.success.background,
color: theme.roles.success.text,
}),
errorBadge: (theme) => ({
border: `1px solid ${theme.roles.error.outline}`,
backgroundColor: theme.roles.error.background,
color: theme.roles.error.text,
}),
warnBadge: (theme) => ({
border: `1px solid ${theme.roles.warning.outline}`,
backgroundColor: theme.roles.warning.background,
color: theme.roles.warning.text,
}),
} satisfies Record<string, Interpolation<Theme>>;
export const EnabledBadge: FC = () => {
return (
<span css={[styles.badge, styles.enabledBadge]} className="option-enabled">
<Badge className="option-enabled" variant="green" border="solid">
Enabled
</span>
</Badge>
);
};
export const EntitledBadge: FC = () => {
return <span css={[styles.badge, styles.enabledBadge]}>Entitled</span>;
return (
<Badge border="solid" variant="green">
Entitled
</Badge>
);
};
interface HealthyBadge {
interface HealthyBadgeProps {
derpOnly?: boolean;
}
export const HealthyBadge: FC<HealthyBadge> = ({ derpOnly }) => {
export const HealthyBadge: FC<HealthyBadgeProps> = ({ derpOnly }) => {
return (
<span css={[styles.badge, styles.enabledBadge]}>
<Badge variant="green" border="solid">
{derpOnly ? "Healthy (DERP only)" : "Healthy"}
</span>
</Badge>
);
};
export const NotHealthyBadge: FC = () => {
return <span css={[styles.badge, styles.errorBadge]}>Unhealthy</span>;
return (
<Badge variant="destructive" border="solid">
Unhealthy
</Badge>
);
};
export const NotRegisteredBadge: FC = () => {
return (
<Tooltip>
<TooltipTrigger asChild>
<span css={[styles.badge, styles.warnBadge]}>Never seen</span>
<Badge variant="warning" border="solid">
Never seen
</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">
Workspace Proxy has never come online and needs to be started.
@@ -90,7 +67,9 @@ export const NotReachableBadge: FC = () => {
return (
<Tooltip>
<TooltipTrigger asChild>
<span css={[styles.badge, styles.warnBadge]}>Not reachable</span>
<Badge variant="warning" border="solid">
Not reachable
</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">
Workspace Proxy not responding to http(s) requests.
@@ -100,42 +79,21 @@ export const NotReachableBadge: FC = () => {
};
export const DisabledBadge: FC = forwardRef<
HTMLSpanElement,
HTMLAttributes<HTMLSpanElement>
HTMLDivElement,
HTMLAttributes<HTMLDivElement>
>((props, ref) => {
return (
<span
{...props}
ref={ref}
css={[
styles.badge,
(theme) => ({
border: `1px solid ${theme.experimental.l1.outline}`,
backgroundColor: theme.experimental.l1.background,
color: theme.experimental.l1.text,
}),
]}
className="option-disabled"
>
<Badge ref={ref} {...props} className="option-disabled">
Disabled
</span>
</Badge>
);
});
export const EnterpriseBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
backgroundColor: theme.branding.enterprise.background,
border: `1px solid ${theme.branding.enterprise.border}`,
color: theme.branding.enterprise.text,
}),
]}
>
<Badge variant="info" border="solid">
Enterprise
</span>
</Badge>
);
};
@@ -147,13 +105,7 @@ export const PremiumBadge: FC<PremiumBadgeProps> = ({
children = "Premium",
}) => {
return (
<Badge
size="sm"
className={cn(
"bg-surface-purple border border-solid border-border-purple uppercase",
"tracking-[0.085em] text-white rounded-full px-3 font-semibold",
)}
>
<Badge variant="purple" border="solid">
{children}
</Badge>
);
@@ -161,52 +113,25 @@ export const PremiumBadge: FC<PremiumBadgeProps> = ({
export const PreviewBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
border: `1px solid ${theme.roles.preview.outline}`,
backgroundColor: theme.roles.preview.background,
color: theme.roles.preview.text,
}),
]}
>
<Badge variant="purple" border="solid">
Preview
</span>
</Badge>
);
};
export const AlphaBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
border: `1px solid ${theme.roles.preview.outline}`,
backgroundColor: theme.roles.preview.background,
color: theme.roles.preview.text,
}),
]}
>
<Badge variant="purple" border="solid">
Alpha
</span>
</Badge>
);
};
export const DeprecatedBadge: FC = () => {
return (
<span
css={[
styles.badge,
(theme) => ({
border: `1px solid ${theme.roles.danger.outline}`,
backgroundColor: theme.roles.danger.background,
color: theme.roles.danger.text,
}),
]}
>
<Badge variant="warning" border="solid">
Deprecated
</span>
</Badge>
);
};
+5 -3
View File
@@ -108,11 +108,13 @@ export const FormSection = forwardRef<HTMLDivElement, FormSectionProps>(
]}
className={classes.sectionInfo}
>
<h2 css={styles.formSectionInfoTitle} className={classes.infoTitle}>
{title}
<header className="flex items-center gap-4">
<h2 css={styles.formSectionInfoTitle} className={classes.infoTitle}>
{title}
</h2>
{alpha && <AlphaBadge />}
{deprecated && <DeprecatedBadge />}
</h2>
</header>
<div css={styles.formSectionInfoDescription}>{description}</div>
</div>