gendesign/frontend/src/components/ui/Badge.tsx
lekss361 2548f71f40
Some checks failed
Deploy / changes (push) Successful in 5s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Failing after 1m6s
Deploy / deploy (push) Has been skipped
feat(sf-fe-a3): UI primitives — HeadlineBar + Badge + Drawer (#341)
2026-05-17 22:06:53 +00:00

92 lines
2.1 KiB
TypeScript

import type { ReactNode } from "react";
export type BadgeVariant =
| "success"
| "warning"
| "danger"
| "info"
| "neutral";
export type BadgeSize = "sm" | "md";
export interface BadgeProps {
variant: BadgeVariant;
size?: BadgeSize;
icon?: ReactNode;
children: ReactNode;
}
const VARIANT_STYLES: Record<
BadgeVariant,
{ background: string; color: string; border: string }
> = {
success: {
background: "var(--success-soft)",
color: "var(--success)",
border: "transparent",
},
warning: {
background: "var(--warn-soft)",
color: "var(--warn)",
border: "transparent",
},
danger: {
background: "var(--danger-soft)",
color: "var(--danger)",
border: "transparent",
},
info: {
background: "var(--accent-soft)",
color: "var(--accent)",
border: "transparent",
},
neutral: {
background: "var(--bg-card-alt)",
color: "var(--fg-secondary)",
border: "var(--border-card)",
},
};
const SIZE_STYLES: Record<
BadgeSize,
{ fontSize: number; padding: string; gap: number }
> = {
sm: { fontSize: 11, padding: "2px 6px", gap: 3 },
md: { fontSize: 12, padding: "3px 8px", gap: 4 },
};
/**
* Badge — inline-flex semantic badge with variants aligned to design tokens.
* Variants: success | warning | danger | info | neutral.
* Icon slot accepts any ReactNode (use Lucide icons, size 12-14px).
*/
export function Badge({ variant, size = "md", icon, children }: BadgeProps) {
const vs = VARIANT_STYLES[variant];
const ss = SIZE_STYLES[size];
return (
<span
style={{
display: "inline-flex",
alignItems: "center",
gap: ss.gap,
background: vs.background,
color: vs.color,
border: `1px solid ${vs.border}`,
borderRadius: 6,
padding: ss.padding,
fontSize: ss.fontSize,
fontWeight: 500,
lineHeight: 1.4,
whiteSpace: "nowrap",
fontVariantNumeric: "tabular-nums",
}}
>
{icon && (
<span style={{ display: "flex", alignItems: "center", flexShrink: 0 }}>
{icon}
</span>
)}
{children}
</span>
);
}