shadimage
moduix-react
itembadgesource: ok · hit

Badge

moduix React Badge component.

npx shadcn add "https://moduix.dev/r/react/badge.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
ui-primitives@ark-ui/react

Registry dependencies

@moduix-react/foundation

Files3 · source cached 10/14/2026, 1:27:12 PM

src/components/badge/Badge.tsx
import type { HTMLArkProps } from '@ark-ui/react/factory';
import { ark } from '@ark-ui/react/factory';
import { clsx } from 'clsx';
import { forwardRef, type ComponentRef } from 'react';
import styles from './Badge.module.css';

type BadgeRootProps = HTMLArkProps<'span'> & {
  variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost' | 'link';
};

const BadgeLabel = forwardRef<ComponentRef<typeof ark.span>, HTMLArkProps<'span'>>(
  function BadgeLabel({ className, ...props }, ref) {
    return (
      <ark.span
        ref={ref}
        {...props}
        data-scope="badge"
        data-part="label"
        data-slot="badge-label"
        className={clsx(styles.label, className)}
      />
    );
  },
);

const Badge = forwardRef<ComponentRef<typeof ark.span>, BadgeRootProps>(function Badge(
  { className, variant = 'default', ...props },
  ref,
) {
  return (
    <ark.span
      ref={ref}
      {...props}
      data-scope="badge"
      data-part="root"
      data-slot="badge-root"
      data-variant={variant}
      className={clsx(styles.root, className)}
    />
  );
});

const BadgeDot = forwardRef<ComponentRef<typeof ark.span>, HTMLArkProps<'span'>>(function BadgeDot(
  { className, ...props },
  ref,
) {
  return (
    <ark.span
      ref={ref}
      {...props}
      data-scope="badge"
      data-part="dot"
      data-slot="badge-dot"
      aria-hidden="true"
      className={clsx(styles.dot, className)}
    />
  );
});

export { Badge, BadgeDot, BadgeLabel };
src/components/badge/Badge.module.css
@layer moduix.components {
  .root {
    display: inline-flex;
    width: fit-content;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
    align-items: center;
    justify-content: center;
    gap: var(--moduix-badge-gap, var(--moduix-spacing-1-5));
    overflow: hidden;
    border: var(--moduix-badge-border-width, var(--moduix-border-width-sm)) solid
      var(--moduix-badge-border-color, var(--moduix-badge-default-border-color, transparent));
    border-radius: var(--moduix-badge-radius, var(--moduix-radius-full));
    padding-block: var(--moduix-badge-padding-y, 0);
    padding-inline: var(--moduix-badge-padding-x, var(--moduix-spacing-2-5));
    background-color: var(
      --moduix-badge-bg,
      var(--moduix-badge-default-bg, var(--moduix-color-primary))
    );
    color: var(
      --moduix-badge-color,
      var(--moduix-badge-default-color, var(--moduix-color-primary-foreground))
    );
    font-size: var(--moduix-badge-font-size, var(--moduix-text-xs));
    font-weight: var(--moduix-badge-font-weight, var(--moduix-weight-medium));
    font-variant-numeric: tabular-nums;
    line-height: var(--moduix-badge-line-height, var(--moduix-line-height-text-xs));
    min-height: var(--moduix-badge-height, 1.25rem);
    user-select: none;
    white-space: nowrap;
    text-decoration: none;
    vertical-align: middle;
    transition:
      color var(--moduix-badge-transition, var(--moduix-transition-default)),
      background-color var(--moduix-badge-transition, var(--moduix-transition-default)),
      border-color var(--moduix-badge-transition, var(--moduix-transition-default)),
      opacity var(--moduix-badge-transition, var(--moduix-transition-default));

    &:focus-visible {
      outline: var(
          --moduix-badge-focus-ring-width,
          var(--moduix-focus-ring-width, var(--moduix-border-width-md))
        )
        solid var(--moduix-badge-focus-ring-color, var(--moduix-color-ring));
      outline-offset: var(--moduix-badge-focus-ring-offset, var(--moduix-focus-ring-offset));
    }

    &:is(a) {
      cursor: pointer;

      &:is(
          [data-variant='default'],
          [data-variant='secondary'],
          [data-variant='destructive']
        ):hover {
        opacity: var(--moduix-badge-opacity-hover, var(--moduix-opacity-hover));
      }

      &[data-variant='outline']:hover {
        border-color: var(
          --moduix-badge-border-color,
          var(--moduix-badge-outline-border-color-hover, var(--moduix-color-primary))
        );
        color: var(
          --moduix-badge-color,
          var(--moduix-badge-outline-color-hover, var(--moduix-color-primary))
        );
      }

      &[data-variant='ghost']:hover {
        border-color: var(
          --moduix-badge-border-color,
          var(--moduix-badge-ghost-border-color-hover, var(--moduix-color-primary))
        );
        color: var(
          --moduix-badge-color,
          var(--moduix-badge-ghost-color-hover, var(--moduix-color-primary))
        );
      }

      &[data-variant='link']:hover {
        text-decoration-color: currentColor;
      }
    }

    & > svg {
      width: var(--moduix-badge-icon-size, var(--moduix-spacing-3));
      height: var(--moduix-badge-icon-size, var(--moduix-spacing-3));
      flex-shrink: 0;
      pointer-events: none;
    }

    &[data-variant='secondary'] {
      border-color: var(
        --moduix-badge-border-color,
        var(--moduix-badge-secondary-border-color, transparent)
      );
      background-color: var(
        --moduix-badge-bg,
        var(--moduix-badge-secondary-bg, var(--moduix-color-secondary))
      );
      color: var(
        --moduix-badge-color,
        var(--moduix-badge-secondary-color, var(--moduix-color-secondary-foreground))
      );
    }

    &[data-variant='destructive'] {
      border-color: var(
        --moduix-badge-border-color,
        var(--moduix-badge-destructive-border-color, transparent)
      );
      background-color: var(
        --moduix-badge-bg,
        var(--moduix-badge-destructive-bg, var(--moduix-color-destructive))
      );
      color: var(
        --moduix-badge-color,
        var(--moduix-badge-destructive-color, var(--moduix-color-destructive-foreground))
      );
    }

    &[data-variant='outline'] {
      border-color: var(
        --moduix-badge-border-color,
        var(--moduix-badge-outline-border-color, var(--moduix-color-border))
      );
      background-color: var(--moduix-badge-bg, var(--moduix-badge-outline-bg, transparent));
      color: var(
        --moduix-badge-color,
        var(--moduix-badge-outline-color, var(--moduix-color-foreground))
      );
    }

    &[data-variant='ghost'] {
      border-color: var(
        --moduix-badge-border-color,
        var(--moduix-badge-ghost-border-color, transparent)
      );
      background-color: var(--moduix-badge-bg, var(--moduix-badge-ghost-bg, transparent));
      color: var(
        --moduix-badge-color,
        var(--moduix-badge-ghost-color, var(--moduix-color-foreground))
      );
    }

    &[data-variant='link'] {
      min-height: auto;
      border-radius: 0;
      padding-block: 0.125em;
      padding-inline: 0;
      border-color: var(
        --moduix-badge-border-color,
        var(--moduix-badge-link-border-color, transparent)
      );
      background-color: var(--moduix-badge-bg, var(--moduix-badge-link-bg, transparent));
      color: var(--moduix-badge-color, var(--moduix-badge-link-color, var(--moduix-color-primary)));
      text-decoration: underline;
      text-decoration-color: color-mix(in oklab, currentColor 55%, transparent);
      text-underline-offset: var(--moduix-badge-link-underline-offset, 0.15em);
      text-decoration-thickness: from-font;
    }
  }

  .dot {
    width: var(--moduix-badge-dot-size, var(--moduix-spacing-1-5));
    height: var(--moduix-badge-dot-size, var(--moduix-spacing-1-5));
    flex-shrink: 0;
    border-radius: 50%;
    background-color: currentColor;
  }

  .label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}
src/components/badge/index.ts
export * from './Badge.js';

More from moduix-react

Browse all components in this library →