feat(Badge): rewrite Badge component to new design

This commit is contained in:
Ilia Mashkov
2026-02-27 18:43:56 +03:00
parent db518a6469
commit f7fe71f8e3
2 changed files with 106 additions and 73 deletions

View File

@@ -9,8 +9,7 @@ const { Story } = defineMeta({
parameters: { parameters: {
docs: { docs: {
description: { description: {
component: component: 'Pill badge with border and optional status dot.',
'Small status indicator with color variants and size options. Use for displaying status, categories, or labels.',
}, },
story: { inline: false }, story: { inline: false },
}, },
@@ -19,58 +18,76 @@ const { Story } = defineMeta({
argTypes: { argTypes: {
variant: { variant: {
control: 'select', control: 'select',
options: ['default', 'success', 'warning', 'error', 'info'], options: ['default', 'accent', 'success', 'warning', 'info'],
description: 'Color variant of the badge', description: 'Badge variant',
defaultValue: 'default',
}, },
size: { size: {
control: 'select', control: 'select',
options: ['sm', 'md'], options: ['xs', 'sm', 'md'],
description: 'Size of the badge', description: 'Badge size',
defaultValue: 'xs',
},
dot: {
control: 'boolean',
description: 'Show status dot',
defaultValue: false,
}, },
}, },
}); });
</script> </script>
<script lang="ts"> <Story
import type { Snippet } from 'svelte'; name="Default variant"
</script> args={{ variant: 'default', size: 'xs' }}
>
<Story name="Default"> {#snippet template()}
{#snippet template(args)} <Badge variant="default" size="xs">Default</Badge>
<Badge variant="default" {...args}>Default</Badge>
{/snippet} {/snippet}
</Story> </Story>
<Story name="Variants"> <Story
{#snippet template(args)} name="Accent variant"
<div class="flex gap-2"> args={{ variant: 'accent', size: 'xs' }}
<Badge variant="default" {...args}>Default</Badge> >
<Badge variant="success" {...args}>Success</Badge> {#snippet template()}
<Badge variant="warning" {...args}>Warning</Badge> <Badge variant="accent" size="xs">Accent</Badge>
<Badge variant="error" {...args}>Error</Badge>
<Badge variant="info" {...args}>Info</Badge>
</div>
{/snippet} {/snippet}
</Story> </Story>
<Story name="Sizes"> <Story
{#snippet template(args)} name="Success variant"
<div class="flex items-center gap-2"> args={{ variant: 'success', size: 'xs' }}
<Badge size="sm" {...args}>Small</Badge> >
<Badge size="md" {...args}>Medium</Badge> {#snippet template()}
</div> <Badge variant="success" size="xs">Success</Badge>
{/snippet} {/snippet}
</Story> </Story>
<Story name="All Combinations"> <Story
{#snippet template(args)} name="Warning variant"
<div class="space-y-2"> args={{ variant: 'warning', size: 'xs' }}
{#each ['default', 'success', 'warning', 'error', 'info'] as variant} >
<div class="flex gap-2"> {#snippet template()}
<Badge {variant} size="sm" {...args}>{variant} sm</Badge> <Badge variant="warning" size="xs">Warning</Badge>
<Badge {variant} size="md" {...args}>{variant} md</Badge> {/snippet}
</div> </Story>
{/each}
<Story
name="Info variant"
args={{ variant: 'info', size: 'xs' }}
>
{#snippet template()}
<Badge variant="info" size="xs">Info</Badge>
{/snippet}
</Story>
<Story name="All sizes">
{#snippet template()}
<div class="flex gap-3 items-center">
<Badge variant="default" size="xs">XS</Badge>
<Badge variant="default" size="sm">SM</Badge>
<Badge variant="default" size="md">MD</Badge>
</div> </div>
{/snippet} {/snippet}
</Story> </Story>

View File

@@ -1,40 +1,56 @@
<!-- <!--
Component: Badge Component: Badge
Small status indicator with color variants and size options Pill badge with border and optional status dot.
--> -->
<script lang="ts"> <script lang="ts">
import { cn } from '$shared/shadcn/utils/shadcn-utils';
import {
type LabelSize,
labelSizeConfig,
} from '$shared/ui/Label/config';
import type { Snippet } from 'svelte';
type BadgeVariant = 'default' | 'accent' | 'success' | 'warning' | 'info';
const badgeVariantConfig: Record<BadgeVariant, string> = {
default: 'border-black/10 dark:border-white/10 text-neutral-500',
accent: 'bg-brand/10 border-brand/20 text-brand',
success: 'bg-green-500/10 border-green-500/20 text-green-600 dark:text-green-400',
warning: 'bg-yellow-500/10 border-yellow-500/20 text-yellow-600 dark:text-yellow-400',
info: 'bg-blue-500/10 border-blue-500/20 text-blue-600 dark:text-blue-400',
};
interface Props { interface Props {
/** variant?: BadgeVariant;
* Color variant of the badge size?: LabelSize;
* @default default /** Renders a small filled circle before the text. */
*/ dot?: boolean;
variant?: 'default' | 'success' | 'warning' | 'error' | 'info'; children?: Snippet;
/** class?: string;
* Size of the badge
* @default md
*/
size?: 'sm' | 'md';
} }
const { variant = 'default', size = 'md' }: Props = $props(); let {
variant = 'default',
const baseClasses = 'inline-flex items-center justify-center rounded-full font-medium'; size = 'xs',
dot = false,
const variantClasses = $derived( children,
variant === 'success' class: className,
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' }: Props = $props();
: variant === 'warning'
? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400'
: variant === 'error'
? 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
: variant === 'info'
? 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'
: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300',
);
const sizeClasses = $derived(size === 'sm' ? 'px-2 py-0.5 text-xs' : 'px-2.5 py-0.5 text-sm');
</script> </script>
<span class="{baseClasses} {variantClasses} {sizeClasses}"> <span
<slot /> class={cn(
'inline-flex items-center gap-1 px-2 py-0.5 border rounded-full',
'font-mono uppercase tracking-wide',
labelSizeConfig[size],
badgeVariantConfig[variant],
className,
)}
>
{#if dot}
<span class="w-1 h-1 rounded-full bg-current"></span>
{/if}
{#if children}
{@render children()}
{/if}
</span> </span>