Badge
Compact status label.
import { Badge } from "@/components/ui/badge";
export default function Demo() { return ( <div className="flex flex-wrap items-center gap-3"> <Badge>Index</Badge> <Badge variant="secondary">Devnet</Badge> <Badge variant="outline">xStock</Badge> <Badge variant="destructive">Failed</Badge> <Badge variant="ghost">Draft</Badge> <Badge variant="link">Solscan</Badge> <Badge variant="brand">Live</Badge> <Badge variant="success">Settled</Badge> <Badge variant="warning">Drift 6%</Badge> <Badge variant="info">Rebalancing</Badge> <Badge shape="pill" variant="brand"> Pill </Badge> <Badge shape="number">12</Badge> </div> );}<script setup lang="ts">import { Badge } from "@/components/ui/badge";</script>
<template> <div class="flex flex-wrap items-center gap-3"> <Badge>Index</Badge> <Badge variant="secondary">Devnet</Badge> <Badge variant="outline">xStock</Badge> <Badge variant="destructive">Failed</Badge> <Badge variant="ghost">Draft</Badge> <Badge variant="link">Solscan</Badge> <Badge variant="brand">Live</Badge> <Badge variant="success">Settled</Badge> <Badge variant="warning">Drift 6%</Badge> <Badge variant="info">Rebalancing</Badge> <Badge shape="pill" variant="brand">Pill</Badge> <Badge shape="number">12</Badge> </div></template><script lang="ts"> import { Badge } from "$lib/components/ui/badge";</script>
<div class="flex flex-wrap items-center gap-3"> <Badge>Index</Badge> <Badge variant="secondary">Devnet</Badge> <Badge variant="outline">xStock</Badge> <Badge variant="destructive">Failed</Badge> <Badge variant="ghost">Draft</Badge> <Badge variant="link">Solscan</Badge> <Badge variant="brand">Live</Badge> <Badge variant="success">Settled</Badge> <Badge variant="warning">Drift 6%</Badge> <Badge variant="info">Rebalancing</Badge> <Badge shape="pill" variant="brand">Pill</Badge> <Badge shape="number">12</Badge></div>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/badgepnpm dlx shadcn@latest add @edmi-ui/badgeyarn dlx shadcn@latest add @edmi-ui/badgebunx shadcn@latest add @edmi-ui/badgePick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @base-ui/react class-variance-authorityTerminal window pnpm add @base-ui/react class-variance-authorityTerminal window yarn add @base-ui/react class-variance-authorityTerminal window bun add @base-ui/react class-variance-authorityAdd the Edmi components this one builds on:
elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/badgepnpm dlx shadcn-vue@latest add @edmi-ui/badgeyarn dlx shadcn-vue@latest add @edmi-ui/badgebunx shadcn-vue@latest add @edmi-ui/badgePick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install reka-ui class-variance-authority @vueuse/coreTerminal window pnpm add reka-ui class-variance-authority @vueuse/coreTerminal window yarn add reka-ui class-variance-authority @vueuse/coreTerminal window bun add reka-ui class-variance-authority @vueuse/coreAdd the Edmi components this one builds on:
elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/badge.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/badge.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/badge.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/badge.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
import { Badge } from "@/components/ui/badge";
<div className="flex flex-wrap items-center gap-3"> <Badge>Index</Badge> <Badge variant="secondary">Devnet</Badge> <Badge variant="outline">xStock</Badge> <Badge variant="destructive">Failed</Badge> <Badge variant="ghost">Draft</Badge> <Badge variant="link">Solscan</Badge> <Badge variant="brand">Live</Badge> <Badge variant="success">Settled</Badge> <Badge variant="warning">Drift 6%</Badge> <Badge variant="info">Rebalancing</Badge> <Badge shape="pill" variant="brand"> Pill </Badge> <Badge shape="number">12</Badge></div><script setup lang="ts">import { Badge } from "@/components/ui/badge";</script>
<template> <div class="flex flex-wrap items-center gap-3"> <Badge>Index</Badge> <Badge variant="secondary">Devnet</Badge> <Badge variant="outline">xStock</Badge> <Badge variant="destructive">Failed</Badge> <Badge variant="ghost">Draft</Badge> <Badge variant="link">Solscan</Badge> <Badge variant="brand">Live</Badge> <Badge variant="success">Settled</Badge> <Badge variant="warning">Drift 6%</Badge> <Badge variant="info">Rebalancing</Badge> <Badge shape="pill" variant="brand">Pill</Badge> <Badge shape="number">12</Badge> </div></template><script lang="ts"> import { Badge } from "$lib/components/ui/badge";</script>
<div class="flex flex-wrap items-center gap-3"> <Badge>Index</Badge> <Badge variant="secondary">Devnet</Badge> <Badge variant="outline">xStock</Badge> <Badge variant="destructive">Failed</Badge> <Badge variant="ghost">Draft</Badge> <Badge variant="link">Solscan</Badge> <Badge variant="brand">Live</Badge> <Badge variant="success">Settled</Badge> <Badge variant="warning">Drift 6%</Badge> <Badge variant="info">Rebalancing</Badge> <Badge shape="pill" variant="brand">Pill</Badge> <Badge shape="number">12</Badge></div>Component source
Section titled “Component source”"use client";
import { mergeProps } from "@base-ui/react/merge-props";import { useRender } from "@base-ui/react/use-render";import { cva, type VariantProps } from "class-variance-authority";import { cn } from "cn";
import { type Elevation, useElevation } from "@/registry/edmi/ui/elevation";
const badgeVariants = cva( "group/badge inline-flex h-[22px] w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-md border border-transparent px-2 text-xs font-medium whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring aria-invalid:border-destructive [&>svg]:pointer-events-none [&_svg]:size-3", { variants: { variant: { default: "bg-primary text-primary-foreground [a]:hover:bg-[color-mix(in_srgb,var(--primary)_85%,var(--background))]", secondary: "border-border bg-secondary text-secondary-foreground [a]:hover:bg-accent", destructive: "border-[color-mix(in_srgb,var(--destructive)_30%,var(--popover))] bg-destructive-soft text-destructive-text", outline: "border-input text-foreground [a]:hover:bg-accent", ghost: "text-foreground hover:bg-accent", link: "text-foreground underline underline-offset-[3px] hover:opacity-80", // ✦ Edmi additions: soft fill + tinted border, never solid brand: "border-[color-mix(in_srgb,var(--brand)_30%,var(--popover))] bg-brand-soft text-brand-text", success: "border-[color-mix(in_srgb,var(--success)_30%,var(--popover))] bg-success-soft text-success-text", warning: "border-[color-mix(in_srgb,var(--warning)_30%,var(--popover))] bg-warning-soft text-warning-text", info: "border-[color-mix(in_srgb,var(--info)_30%,var(--popover))] bg-info-soft text-info-text", }, // ✦ Edmi addition shape: { default: "", pill: "rounded-full", number: "min-w-[22px] justify-center px-1.5 font-mono text-[11px]", }, // ✦ depth (v4): badges keep their fill and tinted border at every level, only the edge changes elevation: { flat: "", sunken: "shadow-[inset_0_1px_2px_rgb(0_0_0/0.22)]", raised: "shadow-raised", floating: "shadow-[inset_0_1px_0_var(--bv-top),0_0_1.5px_var(--bv-out),0_2px_5px_rgb(0_0_0/0.14)]", }, }, defaultVariants: { variant: "default", shape: "default", elevation: "flat", }, },);
function Badge({ className, variant = "default", shape = "default", elevation, render, ...props}: useRender.ComponentProps<"span"> & Omit<VariantProps<typeof badgeVariants>, "elevation"> & { /** ✦ depth: sunken -1, flat 0, raised +1, floating +2 (fill and tint stay). */ elevation?: Elevation; }) { const level = useElevation(elevation, "control"); return useRender({ defaultTagName: "span", props: mergeProps<"span">( { className: cn( badgeVariants({ variant, shape, elevation: level }), className, ), }, props, ), render, state: { slot: "badge", variant, }, });}
export { Badge, badgeVariants };<script setup lang="ts">import type { PrimitiveProps } from 'reka-ui'import type { HTMLAttributes } from 'vue'import type { BadgeVariants } from '.'import { reactiveOmit } from '@vueuse/core'import { Primitive } from 'reka-ui'import { cn } from '@/registry/edmi/lib/utils'import { type Elevation, useElevation } from '@/registry/edmi/ui/elevation'import { badgeVariants } from '.'
const props = withDefaults(defineProps<PrimitiveProps & { variant?: BadgeVariants['variant'] /** ✦ Edmi addition */ shape?: BadgeVariants['shape'] /** ✦ depth: sunken -1, flat 0, raised +1, floating +2 (fill and tint stay) */ elevation?: Elevation class?: HTMLAttributes['class']}>(), { elevation: undefined })
const delegatedProps = reactiveOmit(props, 'class', 'variant', 'shape', 'elevation')const level = useElevation(() => props.elevation, 'control')</script>
<template> <Primitive data-slot="badge" :data-variant="variant" :class="cn(badgeVariants({ variant, shape, elevation: level }), props.class)" v-bind="delegatedProps" > <slot /> </Primitive></template>import type { VariantProps } from "class-variance-authority";import { cva } from "class-variance-authority";
export { default as Badge } from "./Badge.vue";
export const badgeVariants = cva( "group/badge inline-flex h-[22px] w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-md border border-transparent px-2 text-xs font-medium whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring aria-invalid:border-destructive [&>svg]:pointer-events-none [&_svg]:size-3", { variants: { variant: { default: "bg-primary text-primary-foreground [a]:hover:bg-[color-mix(in_srgb,var(--primary)_85%,var(--background))]", secondary: "border-border bg-secondary text-secondary-foreground [a]:hover:bg-accent", destructive: "border-[color-mix(in_srgb,var(--destructive)_30%,var(--popover))] bg-destructive-soft text-destructive-text", outline: "border-input text-foreground [a]:hover:bg-accent", ghost: "text-foreground hover:bg-accent", link: "text-foreground underline underline-offset-[3px] hover:opacity-80", // ✦ Edmi additions: soft fill + tinted border, never solid brand: "border-[color-mix(in_srgb,var(--brand)_30%,var(--popover))] bg-brand-soft text-brand-text", success: "border-[color-mix(in_srgb,var(--success)_30%,var(--popover))] bg-success-soft text-success-text", warning: "border-[color-mix(in_srgb,var(--warning)_30%,var(--popover))] bg-warning-soft text-warning-text", info: "border-[color-mix(in_srgb,var(--info)_30%,var(--popover))] bg-info-soft text-info-text", }, // ✦ Edmi addition shape: { default: "", pill: "rounded-full", number: "min-w-[22px] justify-center px-1.5 font-mono text-[11px]", }, // ✦ depth (v4): badges keep their fill and tinted border at every level, only the edge changes elevation: { flat: "", sunken: "shadow-[inset_0_1px_2px_rgb(0_0_0/0.22)]", raised: "shadow-raised", floating: "shadow-[inset_0_1px_0_var(--bv-top),0_0_1.5px_var(--bv-out),0_2px_5px_rgb(0_0_0/0.14)]", }, }, defaultVariants: { variant: "default", shape: "default", elevation: "flat", }, },);export type BadgeVariants = VariantProps<typeof badgeVariants>;<script lang="ts" module> import { type VariantProps, tv } from "tailwind-variants";
export const badgeVariants = tv({ base: "group/badge inline-flex h-[22px] w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-md border border-transparent px-2 text-xs font-medium whitespace-nowrap transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring aria-invalid:border-destructive [&>svg]:pointer-events-none [&_svg]:size-3", variants: { variant: { default: "bg-primary text-primary-foreground [a]:hover:bg-[color-mix(in_srgb,var(--primary)_85%,var(--background))]", secondary: "border-border bg-secondary text-secondary-foreground [a]:hover:bg-accent", destructive: "border-[color-mix(in_srgb,var(--destructive)_30%,var(--popover))] bg-destructive-soft text-destructive-text", outline: "border-input text-foreground [a]:hover:bg-accent", ghost: "text-foreground hover:bg-accent", link: "text-foreground underline underline-offset-[3px] hover:opacity-80", // ✦ Edmi additions: soft fill + tinted border, never solid brand: "border-[color-mix(in_srgb,var(--brand)_30%,var(--popover))] bg-brand-soft text-brand-text", success: "border-[color-mix(in_srgb,var(--success)_30%,var(--popover))] bg-success-soft text-success-text", warning: "border-[color-mix(in_srgb,var(--warning)_30%,var(--popover))] bg-warning-soft text-warning-text", info: "border-[color-mix(in_srgb,var(--info)_30%,var(--popover))] bg-info-soft text-info-text", }, // ✦ Edmi addition shape: { default: "", pill: "rounded-full", number: "min-w-[22px] justify-center px-1.5 font-mono text-[11px]", }, // ✦ depth (v4): badges keep their fill and tinted border at every level, only the edge changes elevation: { flat: "", sunken: "shadow-[inset_0_1px_2px_rgb(0_0_0/0.22)]", raised: "shadow-raised", floating: "shadow-[inset_0_1px_0_var(--bv-top),0_0_1.5px_var(--bv-out),0_2px_5px_rgb(0_0_0/0.14)]", }, }, defaultVariants: { variant: "default", shape: "default", elevation: "flat", }, });
export type BadgeShape = VariantProps<typeof badgeVariants>["shape"]; export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"];</script>
<script lang="ts"> import { type Elevation, useElevation } from "$lib/registry/ui/elevation/index.js"; import { cn, type WithElementRef } from "$lib/utils.js"; import type { HTMLAnchorAttributes } from "svelte/elements";
let { ref = $bindable(null), href, class: className, variant = "default", shape = "default", elevation = "auto", children, ...restProps }: WithElementRef<HTMLAnchorAttributes> & { variant?: BadgeVariant; shape?: BadgeShape; /** ✦ depth: sunken -1, flat 0, raised +1, floating +2 (fill and tint stay). */ elevation?: Elevation; } = $props();
const level = useElevation(() => elevation, "control");</script>
<svelte:element this={href ? "a" : "span"} bind:this={ref} data-slot="badge" {href} class={cn(badgeVariants({ variant, shape, elevation: level.current }), className)} {...restProps}> {@render children?.()}</svelte:element>export { type BadgeShape, type BadgeVariant, badgeVariants, default as Badge,} from "./badge.svelte";Elevation ✦
Section titled “Elevation ✦”See the Elevation guide for the levels, roles and layered mode.
Badges keep their fill and tinted border at every level; only the edge changes: sunken (-1) is a soft inset, raised (+1) a bevel, floating (+2) a bevel with a small drop.
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
import { Badge } from "@/components/ui/badge";
const variants = [ "default", "secondary", "brand", "success", "destructive", "outline",] as const;
const levels = [ { value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" },] as const;
export default function Demo() { return ( <div className="flex flex-col gap-5"> {levels.map(({ value, label }) => ( <div key={value} className="flex flex-col gap-2"> <p className="text-xs font-medium text-muted-foreground">{label}</p> <div className="flex flex-wrap items-center gap-2"> {variants.map((variant) => ( <Badge key={variant} variant={variant} elevation={value}> {variant} </Badge> ))} <Badge variant="brand" shape="pill" elevation={value}> pill </Badge> </div> </div> ))} </div> );}<script setup lang="ts">import { Badge } from "@/components/ui/badge";
const variants = ["default", "secondary", "brand", "success", "destructive", "outline"] as const;
const levels = [{ value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" }];</script>
<template> <div class="flex flex-col gap-5"> <div v-for="level in levels" :key="level.value" class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{{ level.label }}</p> <div class="flex flex-wrap items-center gap-2"> <Badge v-for="variant in variants" :key="variant" :variant="variant" :elevation="level.value">{{ variant }}</Badge> <Badge variant="brand" shape="pill" :elevation="level.value">pill</Badge> </div> </div> </div></template><script lang="ts"> import { Badge } from "$lib/components/ui/badge";
const variants = ["default", "secondary", "brand", "success", "destructive", "outline"] as const;
const levels = [{ value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" }] as const;</script>
<div class="flex flex-col gap-5"> {#each levels as level (level.value)} <div class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{level.label}</p> <div class="flex flex-wrap items-center gap-2"> {#each variants as variant (variant)} <Badge {variant} elevation={level.value}>{variant}</Badge> {/each} <Badge variant="brand" shape="pill" elevation={level.value}>pill</Badge> </div> </div> {/each}</div>| Prop | Type | Default |
|---|---|---|
| variant | default | secondary | destructive | outline | ghost | link | brand | success | warning | info |
default |
| shape | default | pill | number |
default |
| render | ReactElement |
none |
elevation |
"auto" | "sunken" | "flat" | "raised" | "floating" |
"auto" |
✦ Edmi additions
Section titled “✦ Edmi additions”brand,success,warning,infovariants: soft fill with a tinted border. Usesuccessfor positive deltas and done states (green in every theme);brandfollows the theme accent.shape:pillandnumber(mono, for counts).