import { cva, type VariantProps } from 'class-variance-authority'; import { TRANSITION_COLORS } from '../../styles/constants'; export const badgeRootVariants = cva(['relative self-start'], { variants: { standalone: { true: 'inline-flex', false: '', }, }, defaultVariants: { standalone: false }, }); export const badgeIndicatorVariants = cva( [ 'absolute items-center justify-center overflow-hidden', 'rounded-[var(--border-radius-round)]', 'border border-surface-primary', TRANSITION_COLORS, ], { variants: { variant: { default: 'h-5 min-w-5 px-1', dot: 'h-2 w-2 min-w-0 px-0', }, color: { action: 'bg-surface-action-strong', danger: 'bg-surface-danger-strong', warning: 'bg-surface-warning-strong', success: 'bg-surface-success-strong', info: 'bg-surface-info-strong', }, placement: { 'top-start': 'top-0 ltr:left-0 rtl:right-0', 'top-end': 'top-0 ltr:right-0 rtl:left-0', 'bottom-start': 'bottom-0 ltr:left-0 rtl:right-0', 'bottom-end': 'bottom-0 ltr:right-0 rtl:left-0', }, overlap: { circular: '', rectangular: '' }, }, compoundVariants: [ // Positioning model (mirrors MUI Badge): the indicator is always centered on an // anchor point via a 50% self-translate, so it straddles that point regardless of // its own size (dot vs default). `overlap` only moves the anchor point. // // Translate uses arbitrary `[50%]` (not the `1/2` fraction utility) because the // fraction sugar is web-only in Uniwind — Avatar's badge uses `[N%]` translate on // native, Toast gates `-translate-x-1/2` behind `web:`. // // rectangular: anchor at the host's true bounding-box corner. { placement: 'top-end', overlap: 'rectangular', className: 'ltr:translate-x-[50%] rtl:-translate-x-[50%] -translate-y-[50%]', }, { placement: 'top-start', overlap: 'rectangular', className: 'ltr:-translate-x-[50%] rtl:translate-x-[50%] -translate-y-[50%]', }, { placement: 'bottom-end', overlap: 'rectangular', className: 'ltr:translate-x-[50%] rtl:-translate-x-[50%] translate-y-[50%]', }, { placement: 'bottom-start', overlap: 'rectangular', className: 'ltr:-translate-x-[50%] rtl:translate-x-[50%] translate-y-[50%]', }, // circular: anchor inset 14% from each edge — the ~45° point of the inscribed // circle ((1 - √2/2)/2 ≈ 14.6%, rounded to 14% per MUI) — so the indicator sits // on the host's visible arc. The inset is host-relative (percent of the wrapper, // which hugs the host), keeping it independent of the indicator's own size. { placement: 'top-end', overlap: 'circular', className: 'top-[14%] ltr:right-[14%] rtl:left-[14%] ltr:translate-x-[50%] rtl:-translate-x-[50%] -translate-y-[50%]', }, { placement: 'top-start', overlap: 'circular', className: 'top-[14%] ltr:left-[14%] rtl:right-[14%] ltr:-translate-x-[50%] rtl:translate-x-[50%] -translate-y-[50%]', }, { placement: 'bottom-end', overlap: 'circular', className: 'bottom-[14%] ltr:right-[14%] rtl:left-[14%] ltr:translate-x-[50%] rtl:-translate-x-[50%] translate-y-[50%]', }, { placement: 'bottom-start', overlap: 'circular', className: 'bottom-[14%] ltr:left-[14%] rtl:right-[14%] ltr:-translate-x-[50%] rtl:translate-x-[50%] translate-y-[50%]', }, ], defaultVariants: { variant: 'default', color: 'action', placement: 'top-end', overlap: 'rectangular', }, }, ); export const badgeLabelVariants = cva(['body-sm font-medium text-center'], { variants: { color: { action: 'text-content-action-on-strong', danger: 'text-content-danger-on-strong', warning: 'text-content-warning-on-strong', success: 'text-content-success-on-strong', info: 'text-content-info-on-strong', }, }, defaultVariants: { color: 'action' }, }); export const badgeIconVariants = cva(['size-3'], { variants: { color: { action: 'text-content-action-on-strong', danger: 'text-content-danger-on-strong', warning: 'text-content-warning-on-strong', success: 'text-content-success-on-strong', info: 'text-content-info-on-strong', }, }, defaultVariants: { color: 'action' }, }); export type BadgeVariantProps = VariantProps;