feat(ui): add Badge component

Adds a new `Badge` component to the Backstage UI library. Badge shares the same visual appearance as `Tag` (size tokens, colors, border radius, icon slot) but renders as a plain non-interactive `<span>` with no React Aria plumbing.

Key characteristics:
- Plain DOM element — accessible text content exposed to screen readers without any role override
- Background consumer — participates in the bg context system and steps up neutral background levels (`neutral-2` → `neutral-3` → `neutral-4`) when placed inside colored containers
- Supports `icon`, `size` (`small` | `medium`, defaults to `small`), `children`, and `className` props
- Fully themeable via `BadgeDefinition`

Also includes Storybook stories and full docs-ui documentation (props table, examples, theming section, changelog).

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Made-with: Cursor
This commit is contained in:
Charles de Dreuille
2026-04-03 17:19:02 +01:00
parent 8f9c1d64b8
commit 4032ad7fc4
15 changed files with 413 additions and 0 deletions
@@ -0,0 +1,16 @@
'use client';
import { Badge } from '../../../../../packages/ui/src/components/Badge/Badge';
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
import { RiBugLine } from '@remixicon/react';
export const Default = () => <Badge>Banana</Badge>;
export const WithIcon = () => <Badge icon={<RiBugLine />}>Banana</Badge>;
export const Sizes = () => (
<Flex direction="row" gap="2">
<Badge size="small">Banana</Badge>
<Badge size="medium">Banana</Badge>
</Flex>
);
+41
View File
@@ -0,0 +1,41 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { Default, WithIcon, Sizes } from './components';
import { badgePropDefs } from './props-definition';
import { usage, preview, withIcons, sizes } from './snippets';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { BadgeDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
title="Badge"
description="A non-interactive label for annotating, categorizing, or highlighting content."
/>
<Snippet align="center" py={4} preview={<Default />} code={preview} />
## Usage
<CodeBlock code={usage} />
## API reference
### Badge
<PropsTable data={badgePropDefs} />
## Examples
### With icons
<Snippet align="center" py={4} open preview={<WithIcon />} code={withIcons} />
### Sizes
<Snippet align="center" py={4} open preview={<Sizes />} code={sizes} />
<Theming definition={BadgeDefinition} />
<ChangelogComponent component={['badge']} />
@@ -0,0 +1,27 @@
import {
classNamePropDefs,
childrenPropDefs,
type PropDef,
} from '@/utils/propDefs';
import { Chip } from '@/components/Chip';
export const badgePropDefs: Record<string, PropDef> = {
icon: {
type: 'enum',
values: ['ReactNode'],
description: 'Icon displayed before the badge text.',
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'small',
description: (
<>
Visual size of the badge. Use <Chip>small</Chip> for inline or dense
layouts, <Chip>medium</Chip> for standalone badges.
</>
),
},
...childrenPropDefs,
...classNamePropDefs,
};
@@ -0,0 +1,12 @@
export const usage = `import { Badge } from '@backstage/ui';
<Badge>Badge</Badge>`;
export const preview = `<Badge>Banana</Badge>`;
export const withIcons = `<Badge icon={<RiBugLine />}>Banana</Badge>`;
export const sizes = `<Flex direction="row" gap="2">
<Badge size="small">Banana</Badge>
<Badge size="medium">Banana</Badge>
</Flex>`;
+4
View File
@@ -17,6 +17,10 @@ export const components: Page[] = [
title: 'Avatar',
slug: 'avatar',
},
{
title: 'Badge',
slug: 'badge',
},
{
title: 'Box',
slug: 'box',