Add Badge atom component
- Create badge component tokens (height, paddingX, fontSize, iconSize, iconGap, borderRadius for sm/md sizes) — 10 tokens - Build Badge component: pill-shaped status indicator label - 6 colours: default, brand, success, warning, error, info - 2 variants: soft (tonal, default) and filled (solid) - 2 sizes: small (22px) and medium (26px) - Optional leading icon prop - All colours use CSS variables from token system (no hardcoded hex) - Create 10 Storybook stories: Default, AllColoursSoft, AllColoursFilled, WithIcons, WithIconsFilled, Sizes, SmallSizes, InPriceCard, ServiceStatus, CompleteMatrix - Regenerate token pipeline outputs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
268
src/components/atoms/Badge/Badge.stories.tsx
Normal file
268
src/components/atoms/Badge/Badge.stories.tsx
Normal file
@@ -0,0 +1,268 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Badge } from './Badge';
|
||||
import { Card } from '../Card';
|
||||
import { Typography } from '../Typography';
|
||||
import { Button } from '../Button';
|
||||
import Box from '@mui/material/Box';
|
||||
import StarIcon from '@mui/icons-material/Star';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
||||
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
|
||||
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
|
||||
import LocalOfferIcon from '@mui/icons-material/LocalOffer';
|
||||
import NewReleasesIcon from '@mui/icons-material/NewReleases';
|
||||
import VerifiedIcon from '@mui/icons-material/Verified';
|
||||
|
||||
const meta: Meta<typeof Badge> = {
|
||||
title: 'Atoms/Badge',
|
||||
component: Badge,
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
argTypes: {
|
||||
color: {
|
||||
control: 'select',
|
||||
options: ['default', 'brand', 'success', 'warning', 'error', 'info'],
|
||||
description: 'Colour intent',
|
||||
table: { defaultValue: { summary: 'default' } },
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['soft', 'filled'],
|
||||
description: 'Visual style variant',
|
||||
table: { defaultValue: { summary: 'soft' } },
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['small', 'medium'],
|
||||
description: 'Size preset',
|
||||
table: { defaultValue: { summary: 'medium' } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Badge>;
|
||||
|
||||
// ─── Default ────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Default badge — soft variant, default colour */
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: 'Status',
|
||||
},
|
||||
};
|
||||
|
||||
// ─── All Colours — Soft ─────────────────────────────────────────────────────
|
||||
|
||||
/** Soft variant across all colour options */
|
||||
export const AllColoursSoft: Story = {
|
||||
name: 'All Colours — Soft',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge color="default">Default</Badge>
|
||||
<Badge color="brand">Brand</Badge>
|
||||
<Badge color="success">Success</Badge>
|
||||
<Badge color="warning">Warning</Badge>
|
||||
<Badge color="error">Error</Badge>
|
||||
<Badge color="info">Info</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── All Colours — Filled ───────────────────────────────────────────────────
|
||||
|
||||
/** Filled variant across all colour options */
|
||||
export const AllColoursFilled: Story = {
|
||||
name: 'All Colours — Filled',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge variant="filled" color="default">Default</Badge>
|
||||
<Badge variant="filled" color="brand">Brand</Badge>
|
||||
<Badge variant="filled" color="success">Success</Badge>
|
||||
<Badge variant="filled" color="warning">Warning</Badge>
|
||||
<Badge variant="filled" color="error">Error</Badge>
|
||||
<Badge variant="filled" color="info">Info</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── With Icons ─────────────────────────────────────────────────────────────
|
||||
|
||||
/** Badges with leading icons */
|
||||
export const WithIcons: Story = {
|
||||
name: 'With Icons',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge color="brand" icon={<StarIcon />}>Popular</Badge>
|
||||
<Badge color="success" icon={<CheckCircleIcon />}>Verified</Badge>
|
||||
<Badge color="warning" icon={<WarningAmberIcon />}>Limited</Badge>
|
||||
<Badge color="error" icon={<ErrorOutlineIcon />}>Sold out</Badge>
|
||||
<Badge color="info" icon={<InfoOutlinedIcon />}>New</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
/** Filled badges with icons */
|
||||
export const WithIconsFilled: Story = {
|
||||
name: 'With Icons — Filled',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge variant="filled" color="brand" icon={<StarIcon />}>Popular</Badge>
|
||||
<Badge variant="filled" color="success" icon={<CheckCircleIcon />}>Included</Badge>
|
||||
<Badge variant="filled" color="warning" icon={<WarningAmberIcon />}>Attention</Badge>
|
||||
<Badge variant="filled" color="error" icon={<ErrorOutlineIcon />}>Unavailable</Badge>
|
||||
<Badge variant="filled" color="info" icon={<InfoOutlinedIcon />}>Updated</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── Sizes ──────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Both sizes side by side */
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Badge size="small" color="brand" icon={<StarIcon />}>Small</Badge>
|
||||
<Badge size="medium" color="brand" icon={<StarIcon />}>Medium</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
/** All colours in small size */
|
||||
export const SmallSizes: Story = {
|
||||
name: 'Small — All Colours',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge size="small" color="default">Default</Badge>
|
||||
<Badge size="small" color="brand" icon={<StarIcon />}>Brand</Badge>
|
||||
<Badge size="small" color="success" icon={<CheckCircleIcon />}>Success</Badge>
|
||||
<Badge size="small" color="warning">Warning</Badge>
|
||||
<Badge size="small" color="error">Error</Badge>
|
||||
<Badge size="small" color="info">Info</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── In Context: Price Card ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Badge used inside a PriceCard-style layout.
|
||||
* Shows how badges label card content in realistic compositions.
|
||||
*/
|
||||
export const InPriceCard: Story = {
|
||||
name: 'In Context — Price Card',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 24, maxWidth: 750 }}>
|
||||
<Card sx={{ flex: 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', mb: 2 }}>
|
||||
<Typography variant="overline" color="text.secondary">
|
||||
Essential
|
||||
</Typography>
|
||||
<Badge size="small" color="default">Standard</Badge>
|
||||
</Box>
|
||||
<Typography variant="display3" color="primary" sx={{ mb: 1 }}>
|
||||
$3,200
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
A simple, respectful service with chapel ceremony.
|
||||
</Typography>
|
||||
<Button fullWidth>Select</Button>
|
||||
</Card>
|
||||
|
||||
<Card sx={{ flex: 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', mb: 2 }}>
|
||||
<Typography variant="overline" color="text.secondary">
|
||||
Premium
|
||||
</Typography>
|
||||
<Badge color="brand" icon={<StarIcon />}>Most popular</Badge>
|
||||
</Box>
|
||||
<Typography variant="display3" color="primary" sx={{ mb: 1 }}>
|
||||
$5,800
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
Comprehensive service with premium inclusions.
|
||||
</Typography>
|
||||
<Button fullWidth>Select</Button>
|
||||
</Card>
|
||||
|
||||
<Card sx={{ flex: 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', mb: 2 }}>
|
||||
<Typography variant="overline" color="text.secondary">
|
||||
Bespoke
|
||||
</Typography>
|
||||
<Badge color="info" icon={<LocalOfferIcon />}>Best value</Badge>
|
||||
</Box>
|
||||
<Typography variant="display3" color="primary" sx={{ mb: 1 }}>
|
||||
$8,500
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
Fully customised farewell with dedicated coordinator.
|
||||
</Typography>
|
||||
<Button fullWidth>Select</Button>
|
||||
</Card>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── In Context: Service Status ─────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Badges used as status indicators in a service listing.
|
||||
*/
|
||||
export const ServiceStatus: Story = {
|
||||
name: 'In Context — Service Status',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 500 }}>
|
||||
{[
|
||||
{ service: 'Chapel ceremony', badge: <Badge color="success" icon={<CheckCircleIcon />}>Confirmed</Badge> },
|
||||
{ service: 'Floral arrangements', badge: <Badge color="warning" icon={<WarningAmberIcon />}>Pending</Badge> },
|
||||
{ service: 'Catering', badge: <Badge color="error" icon={<ErrorOutlineIcon />}>Unavailable</Badge> },
|
||||
{ service: 'Memorial printing', badge: <Badge color="info" icon={<NewReleasesIcon />}>New option</Badge> },
|
||||
{ service: 'Premium casket', badge: <Badge variant="filled" color="brand" icon={<VerifiedIcon />}>Included</Badge> },
|
||||
].map((item) => (
|
||||
<Card key={item.service} variant="outlined" padding="compact">
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="labelLg">{item.service}</Typography>
|
||||
{item.badge}
|
||||
</Box>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── Complete Matrix ────────────────────────────────────────────────────────
|
||||
|
||||
/** Full variant × colour × size matrix for visual QA */
|
||||
export const CompleteMatrix: Story = {
|
||||
name: 'Complete Matrix',
|
||||
render: () => {
|
||||
const colors = ['default', 'brand', 'success', 'warning', 'error', 'info'] as const;
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
{(['soft', 'filled'] as const).map((variant) => (
|
||||
<div key={variant}>
|
||||
<div style={{ marginBottom: 8, fontWeight: 600, fontSize: 14, textTransform: 'capitalize' }}>
|
||||
{variant}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{(['medium', 'small'] as const).map((size) => (
|
||||
<div key={size} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||
<span style={{ width: 60, fontSize: 12, color: '#737373' }}>{size}</span>
|
||||
{colors.map((color) => (
|
||||
<Badge key={color} variant={variant} color={color} size={size} icon={<StarIcon />}>
|
||||
{color}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
141
src/components/atoms/Badge/Badge.tsx
Normal file
141
src/components/atoms/Badge/Badge.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import type { BoxProps } from '@mui/material/Box';
|
||||
import type { Theme } from '@mui/material/styles';
|
||||
|
||||
// ─── Types ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Colour intent for the badge */
|
||||
export type BadgeColor = 'default' | 'brand' | 'success' | 'warning' | 'error' | 'info';
|
||||
|
||||
/** Props for the FA Badge component */
|
||||
export interface BadgeProps extends Omit<BoxProps, 'color'> {
|
||||
/** Colour intent */
|
||||
color?: BadgeColor;
|
||||
/** Visual style: "filled" (solid background) or "soft" (tonal/subtle background) */
|
||||
variant?: 'filled' | 'soft';
|
||||
/** Size preset */
|
||||
size?: 'small' | 'medium';
|
||||
/** Optional leading icon */
|
||||
icon?: React.ReactNode;
|
||||
/** Label text */
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
// ─── Colour maps ─────────────────────────────────────────────────────────────
|
||||
|
||||
const filledColors: Record<BadgeColor, (t: Theme) => { bg: string; text: string }> = {
|
||||
default: (t) => ({ bg: t.palette.grey[700], text: t.palette.common.white }),
|
||||
brand: (t) => ({ bg: t.palette.primary.main, text: t.palette.common.white }),
|
||||
success: (t) => ({ bg: t.palette.success.main, text: t.palette.common.white }),
|
||||
warning: (t) => ({ bg: t.palette.warning.main, text: t.palette.common.white }),
|
||||
error: (t) => ({ bg: t.palette.error.main, text: t.palette.common.white }),
|
||||
info: (t) => ({ bg: t.palette.info.main, text: t.palette.common.white }),
|
||||
};
|
||||
|
||||
const softColors: Record<BadgeColor, (t: Theme) => { bg: string; text: string }> = {
|
||||
default: (t) => ({ bg: t.palette.grey[200], text: t.palette.grey[700] }),
|
||||
brand: () => ({
|
||||
bg: 'var(--fa-color-brand-200)',
|
||||
text: 'var(--fa-color-brand-700)',
|
||||
}),
|
||||
success: () => ({
|
||||
bg: 'var(--fa-color-feedback-success-subtle)',
|
||||
text: 'var(--fa-color-feedback-success)',
|
||||
}),
|
||||
warning: () => ({
|
||||
bg: 'var(--fa-color-feedback-warning-subtle)',
|
||||
text: 'var(--fa-color-text-warning)',
|
||||
}),
|
||||
error: () => ({
|
||||
bg: 'var(--fa-color-feedback-error-subtle)',
|
||||
text: 'var(--fa-color-feedback-error)',
|
||||
}),
|
||||
info: () => ({
|
||||
bg: 'var(--fa-color-feedback-info-subtle)',
|
||||
text: 'var(--fa-color-feedback-info)',
|
||||
}),
|
||||
};
|
||||
|
||||
// ─── Component ───────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Status indicator label for the FA design system.
|
||||
*
|
||||
* Pill-shaped, display-only badge for communicating status, category,
|
||||
* or emphasis. Used in PriceCard ("Popular"), ServiceOption ("Included"),
|
||||
* and other contexts.
|
||||
*
|
||||
* Colour options:
|
||||
* - `default` — neutral grey (general labels)
|
||||
* - `brand` — warm gold/copper (promoted, featured)
|
||||
* - `success` — green (confirmed, included, available)
|
||||
* - `warning` — amber (limited, expiring, attention)
|
||||
* - `error` — red (sold out, unavailable, urgent)
|
||||
* - `info` — blue (new, updated, informational)
|
||||
*
|
||||
* Variant options:
|
||||
* - `soft` (default) — tonal background, coloured text. Calmer, preferred for FA.
|
||||
* - `filled` — solid background, white text. For high-priority emphasis.
|
||||
*/
|
||||
export const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
||||
(
|
||||
{
|
||||
color = 'default',
|
||||
variant = 'soft',
|
||||
size = 'medium',
|
||||
icon,
|
||||
children,
|
||||
sx,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const isSmall = size === 'small';
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={ref}
|
||||
component="span"
|
||||
sx={[
|
||||
(theme: Theme) => {
|
||||
const colors = variant === 'filled'
|
||||
? filledColors[color](theme)
|
||||
: softColors[color](theme);
|
||||
|
||||
return {
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 'var(--fa-badge-icon-gap-default)',
|
||||
minHeight: isSmall ? 'var(--fa-badge-height-sm)' : 'var(--fa-badge-height-md)',
|
||||
px: isSmall ? 'var(--fa-badge-padding-x-sm)' : 'var(--fa-badge-padding-x-md)',
|
||||
borderRadius: 'var(--fa-badge-border-radius-default)',
|
||||
backgroundColor: colors.bg,
|
||||
color: colors.text,
|
||||
fontSize: isSmall ? 'var(--fa-badge-font-size-sm)' : 'var(--fa-badge-font-size-md)',
|
||||
fontWeight: 600,
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
lineHeight: 1,
|
||||
letterSpacing: '0.02em',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'none',
|
||||
// Icon sizing
|
||||
'& > .MuiSvgIcon-root, & > svg': {
|
||||
fontSize: isSmall ? 'var(--fa-badge-icon-size-sm)' : 'var(--fa-badge-icon-size-md)',
|
||||
flexShrink: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
...(Array.isArray(sx) ? sx : [sx]),
|
||||
]}
|
||||
{...props}
|
||||
>
|
||||
{icon}
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Badge.displayName = 'Badge';
|
||||
export default Badge;
|
||||
2
src/components/atoms/Badge/index.ts
Normal file
2
src/components/atoms/Badge/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { Badge, type BadgeProps, type BadgeColor } from './Badge';
|
||||
export { default } from './Badge';
|
||||
@@ -3,6 +3,10 @@
|
||||
*/
|
||||
|
||||
:root {
|
||||
--fa-badge-height-sm: 22px; /** Small — compact inline status indicators */
|
||||
--fa-badge-height-md: 26px; /** Medium — default status badges, card labels */
|
||||
--fa-badge-icon-size-sm: 12px; /** 12px icons in small badges */
|
||||
--fa-badge-icon-size-md: 14px; /** 14px icons in medium badges */
|
||||
--fa-button-height-xs: 28px; /** Extra-small — compact text buttons, inline actions */
|
||||
--fa-button-height-sm: 32px; /** Small — secondary actions, toolbar buttons */
|
||||
--fa-button-height-md: 40px; /** Medium — default size, form submissions */
|
||||
@@ -206,6 +210,12 @@
|
||||
--fa-typography-overline-letter-spacing: 1.5px;
|
||||
--fa-typography-overline-sm-line-height: 1.273;
|
||||
--fa-typography-overline-sm-letter-spacing: 1.5px;
|
||||
--fa-badge-padding-x-sm: var(--fa-spacing-2); /** 8px — compact horizontal padding */
|
||||
--fa-badge-padding-x-md: var(--fa-spacing-3); /** 12px — default horizontal padding */
|
||||
--fa-badge-font-size-sm: var(--fa-font-size-2xs); /** 11px — small badge text */
|
||||
--fa-badge-font-size-md: var(--fa-font-size-xs); /** 12px — default badge text */
|
||||
--fa-badge-icon-gap-default: var(--fa-spacing-1); /** 4px icon-text gap */
|
||||
--fa-badge-border-radius-default: var(--fa-border-radius-full); /** Pill shape — fully rounded */
|
||||
--fa-button-padding-x-xs: var(--fa-spacing-2); /** 8px — compact horizontal padding */
|
||||
--fa-button-padding-x-sm: var(--fa-spacing-3); /** 12px — small horizontal padding */
|
||||
--fa-button-padding-x-md: var(--fa-spacing-4); /** 16px — default horizontal padding */
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
* Do not edit directly, this file was auto-generated.
|
||||
*/
|
||||
|
||||
export const BadgeHeightSm = "22px"; // Small — compact inline status indicators
|
||||
export const BadgeHeightMd = "26px"; // Medium — default status badges, card labels
|
||||
export const BadgePaddingXSm = "8px"; // 8px — compact horizontal padding
|
||||
export const BadgePaddingXMd = "12px"; // 12px — default horizontal padding
|
||||
export const BadgeFontSizeSm = "0.6875rem"; // 11px — small badge text
|
||||
export const BadgeFontSizeMd = "0.75rem"; // 12px — default badge text
|
||||
export const BadgeIconSizeSm = "12px"; // 12px icons in small badges
|
||||
export const BadgeIconSizeMd = "14px"; // 14px icons in medium badges
|
||||
export const BadgeIconGapDefault = "4px"; // 4px icon-text gap
|
||||
export const BadgeBorderRadiusDefault = "9999px"; // Pill shape — fully rounded
|
||||
export const ButtonHeightXs = "28px"; // Extra-small — compact text buttons, inline actions
|
||||
export const ButtonHeightSm = "32px"; // Small — secondary actions, toolbar buttons
|
||||
export const ButtonHeightMd = "40px"; // Medium — default size, form submissions
|
||||
|
||||
39
tokens/component/badge.json
Normal file
39
tokens/component/badge.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"badge": {
|
||||
"$description": "Badge component tokens — status indicator labels used in PriceCard, ServiceOption, and other contexts. Display-only, not interactive.",
|
||||
"height": {
|
||||
"$type": "dimension",
|
||||
"$description": "Badge heights per size.",
|
||||
"sm": { "$value": "22px", "$description": "Small — compact inline status indicators" },
|
||||
"md": { "$value": "26px", "$description": "Medium — default status badges, card labels" }
|
||||
},
|
||||
"paddingX": {
|
||||
"$type": "dimension",
|
||||
"$description": "Horizontal padding per size.",
|
||||
"sm": { "$value": "{spacing.2}", "$description": "8px — compact horizontal padding" },
|
||||
"md": { "$value": "{spacing.3}", "$description": "12px — default horizontal padding" }
|
||||
},
|
||||
"fontSize": {
|
||||
"$type": "dimension",
|
||||
"$description": "Font size per badge size.",
|
||||
"sm": { "$value": "{fontSize.2xs}", "$description": "11px — small badge text" },
|
||||
"md": { "$value": "{fontSize.xs}", "$description": "12px — default badge text" }
|
||||
},
|
||||
"iconSize": {
|
||||
"$type": "dimension",
|
||||
"$description": "Icon dimensions per badge size.",
|
||||
"sm": { "$value": "12px", "$description": "12px icons in small badges" },
|
||||
"md": { "$value": "14px", "$description": "14px icons in medium badges" }
|
||||
},
|
||||
"iconGap": {
|
||||
"$type": "dimension",
|
||||
"$description": "Gap between icon and label text.",
|
||||
"default": { "$value": "{spacing.1}", "$description": "4px icon-text gap" }
|
||||
},
|
||||
"borderRadius": {
|
||||
"$type": "dimension",
|
||||
"$description": "Badge corner radius.",
|
||||
"default": { "$value": "{borderRadius.full}", "$description": "Pill shape — fully rounded" }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user