Base Components
Badge
A badge is a piece of text that is visually stylized to differentiate it as contextual metadata. It can be used to add a status, category, or other metadata to a design.
Badge
Installation
Install the following dependencies:
terminal
npm install @radix-ui/react-slot
Create a badge.tsx
file and paste the following code into it.
/components/ui/badge.tsx
// AlignUI Badge v0.0.0
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import type { PolymorphicComponentProps } from '@/utils/polymorphic';
import { recursiveCloneChildren } from '@/utils/recursive-clone-children';
import { tv, type VariantProps } from '@/utils/tv';
const BADGE_ROOT_NAME = 'BadgeRoot';
const BADGE_ICON_NAME = 'BadgeIcon';
const BADGE_DOT_NAME = 'BadgeDot';
export const badgeVariants = tv({
slots: {
root: 'inline-flex items-center justify-center rounded-full leading-none transition duration-200 ease-out',
icon: 'shrink-0',
dot: [
// base
'dot',
'flex items-center justify-center',
// before
'before:size-1 before:rounded-full before:bg-current',
],
},
variants: {
size: {
small: {
root: 'h-4 gap-1.5 px-2 text-subheading-2xs uppercase has-[>.dot]:gap-2',
icon: '-mx-1 size-3',
dot: '-mx-2 size-4',
},
medium: {
root: 'h-5 gap-1.5 px-2 text-label-xs',
icon: '-mx-1 size-4',
dot: '-mx-1.5 size-4',
},
},
variant: {
filled: {
root: 'text-static-white',
},
light: {},
lighter: {},
stroke: {
root: 'ring-1 ring-inset ring-current',
},
},
color: {
gray: {},
blue: {},
orange: {},
red: {},
green: {},
yellow: {},
purple: {},
sky: {},
pink: {},
teal: {},
},
disabled: {
true: {
root: 'pointer-events-none',
},
},
square: {
true: {},
},
},
compoundVariants: [
//#region variant=filled
{
variant: 'filled',
color: 'gray',
class: {
root: 'bg-faded-base',
},
},
{
variant: 'filled',
color: 'blue',
class: {
root: 'bg-information-base',
},
},
{
variant: 'filled',
color: 'orange',
class: {
root: 'bg-warning-base',
},
},
{
variant: 'filled',
color: 'red',
class: {
root: 'bg-error-base',
},
},
{
variant: 'filled',
color: 'green',
class: {
root: 'bg-success-base',
},
},
{
variant: 'filled',
color: 'yellow',
class: {
root: 'bg-away-base',
},
},
{
variant: 'filled',
color: 'purple',
class: {
root: 'bg-feature-base',
},
},
{
variant: 'filled',
color: 'sky',
class: {
root: 'bg-verified-base',
},
},
{
variant: 'filled',
color: 'pink',
class: {
root: 'bg-highlighted-base',
},
},
{
variant: 'filled',
color: 'teal',
class: {
root: 'bg-stable-base',
},
},
// #endregion
//#region variant=light
{
variant: 'light',
color: 'gray',
class: {
root: 'bg-faded-light text-faded-dark',
},
},
{
variant: 'light',
color: 'blue',
class: {
root: 'bg-information-light text-information-dark',
},
},
{
variant: 'light',
color: 'orange',
class: {
root: 'bg-warning-light text-warning-dark',
},
},
{
variant: 'light',
color: 'red',
class: {
root: 'bg-error-light text-error-dark',
},
},
{
variant: 'light',
color: 'green',
class: {
root: 'bg-success-light text-success-dark',
},
},
{
variant: 'light',
color: 'yellow',
class: {
root: 'bg-away-light text-away-dark',
},
},
{
variant: 'light',
color: 'purple',
class: {
root: 'bg-feature-light text-feature-dark',
},
},
{
variant: 'light',
color: 'sky',
class: {
root: 'bg-verified-light text-verified-dark',
},
},
{
variant: 'light',
color: 'pink',
class: {
root: 'bg-highlighted-light text-highlighted-dark',
},
},
{
variant: 'light',
color: 'teal',
class: {
root: 'bg-stable-light text-stable-dark',
},
},
//#endregion
//#region variant=lighter
{
variant: 'lighter',
color: 'gray',
class: {
root: 'bg-faded-lighter text-faded-base',
},
},
{
variant: 'lighter',
color: 'blue',
class: {
root: 'bg-information-lighter text-information-base',
},
},
{
variant: 'lighter',
color: 'orange',
class: {
root: 'bg-warning-lighter text-warning-base',
},
},
{
variant: 'lighter',
color: 'red',
class: {
root: 'bg-error-lighter text-error-base',
},
},
{
variant: 'lighter',
color: 'green',
class: {
root: 'bg-success-lighter text-success-base',
},
},
{
variant: 'lighter',
color: 'yellow',
class: {
root: 'bg-away-lighter text-away-base',
},
},
{
variant: 'lighter',
color: 'purple',
class: {
root: 'bg-feature-lighter text-feature-base',
},
},
{
variant: 'lighter',
color: 'sky',
class: {
root: 'bg-verified-lighter text-verified-base',
},
},
{
variant: 'lighter',
color: 'pink',
class: {
root: 'bg-highlighted-lighter text-highlighted-base',
},
},
{
variant: 'lighter',
color: 'teal',
class: {
root: 'bg-stable-lighter text-stable-base',
},
},
//#endregion
//#region variant=stroke
{
variant: 'stroke',
color: 'gray',
class: {
root: 'text-faded-base',
},
},
{
variant: 'stroke',
color: 'blue',
class: {
root: 'text-information-base',
},
},
{
variant: 'stroke',
color: 'orange',
class: {
root: 'text-warning-base',
},
},
{
variant: 'stroke',
color: 'red',
class: {
root: 'text-error-base',
},
},
{
variant: 'stroke',
color: 'green',
class: {
root: 'text-success-base',
},
},
{
variant: 'stroke',
color: 'yellow',
class: {
root: 'text-away-base',
},
},
{
variant: 'stroke',
color: 'purple',
class: {
root: 'text-feature-base',
},
},
{
variant: 'stroke',
color: 'sky',
class: {
root: 'text-verified-base',
},
},
{
variant: 'stroke',
color: 'pink',
class: {
root: 'text-highlighted-base',
},
},
{
variant: 'stroke',
color: 'teal',
class: {
root: 'text-stable-base',
},
},
//#endregion
//#region square
{
size: 'small',
square: true,
class: {
root: 'min-w-4 px-1',
},
},
{
size: 'medium',
square: true,
class: {
root: 'min-w-5 px-1',
},
},
//#endregion
//#region disabled
{
disabled: true,
variant: ['stroke', 'filled', 'light', 'lighter'],
color: [
'red',
'gray',
'blue',
'orange',
'green',
'yellow',
'purple',
'sky',
'pink',
'teal',
],
class: {
root: [
'ring-1 ring-inset ring-stroke-soft-200',
'bg-transparent text-text-disabled-300',
],
},
},
//#endregion
],
defaultVariants: {
variant: 'filled',
size: 'small',
color: 'gray',
},
});
type BadgeSharedProps = VariantProps<typeof badgeVariants>;
type BadgeRootProps = VariantProps<typeof badgeVariants> &
React.HTMLAttributes<HTMLDivElement> & {
asChild?: boolean;
};
const BadgeRoot = React.forwardRef<HTMLDivElement, BadgeRootProps>(
(
{
asChild,
size,
variant,
color,
disabled,
square,
children,
className,
...rest
},
forwardedRef,
) => {
const uniqueId = React.useId();
const Component = asChild ? Slot : 'div';
const { root } = badgeVariants({ size, variant, color, disabled, square });
const sharedProps: BadgeSharedProps = {
size,
variant,
color,
};
const extendedChildren = recursiveCloneChildren(
children as React.ReactElement[],
sharedProps,
[BADGE_ICON_NAME, BADGE_DOT_NAME],
uniqueId,
asChild,
);
return (
<Component
ref={forwardedRef}
className={root({ class: className })}
{...rest}
>
{extendedChildren}
</Component>
);
},
);
BadgeRoot.displayName = BADGE_ROOT_NAME;
function BadgeIcon<T extends React.ElementType>({
className,
size,
variant,
color,
as,
...rest
}: PolymorphicComponentProps<T, BadgeSharedProps>) {
const Component = as || 'div';
const { icon } = badgeVariants({ size, variant, color });
return <Component className={icon({ class: className })} {...rest} />;
}
BadgeIcon.displayName = BADGE_ICON_NAME;
type BadgeDotProps = BadgeSharedProps &
Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>;
function BadgeDot({ size, variant, color, className, ...rest }: BadgeDotProps) {
const { dot } = badgeVariants({ size, variant, color });
return <div className={dot({ class: className })} {...rest} />;
}
BadgeDot.displayName = BADGE_DOT_NAME;
export { BadgeRoot as Root, BadgeIcon as Icon, BadgeDot as Dot };
Update the import paths to match your project setup.
Examples
Filled (Default)
Badge
Badge
Badge
Badge
Badge
Light
Badge
Badge
Badge
Badge
Badge
Lighter
Badge
Badge
Badge
Badge
Badge
Stroke
Badge
Badge
Badge
Badge
Badge
Colors
Badge
Badge
Badge
Badge
Badge
Badge
Badge
Badge
Badge
Badge
Size
Badge
Badge
Badge
Badge
Square
Suitable to display numbers.
2
5
66
789
2
5
66
789
With Icon
Badge
Badge
With Dot
Badge
Badge
Disabled
Badge
Badge
Badge
Badge
asChild
API Reference
Badge.Root
This component is based on the <div>
element and supports all of its props. And adds:
Prop | Type | Default |
---|---|---|
variant | "filled" |"light" |"lighter" |"stroke" | "filled" |
size | "small" |"medium" | "small" |
color | "gray" |"blue" |"orange" |"red" |"green" |"yellow" |"purple" |"sky" |"pink" |"teal" | "gray" |
square | boolean | |
asChild | boolean |
Badge.Icon
The Badge.Icon
component is polymorphic, allowing you to change the underlying HTML element using the as
prop.
Prop | Type | Default |
---|---|---|
as | React.ElementType | div |
Badge.Dot
This component is based on the <div>
element and supports all of its props. It provides a styled dot appearance and does not have any extra props.
© 2024 AlignUI Design System. All rights reserved.
ON THIS PAGE