Skip to content
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions packages/eui/changelogs/upcoming/9302.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Updated EuiBadge design to have rounded corners and improved paddings
43 changes: 43 additions & 0 deletions packages/eui/src/components/badge/badge.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
* Side Public License, v 1.
*/

import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { action } from '@storybook/addon-actions';

import { EuiBadge, EuiBadgeProps, COLORS } from './badge';
import { EuiBadgeGroup } from './badge_group';
import { EuiFlexGroup } from '../flex';

const meta: Meta<EuiBadgeProps> = {
title: 'Display/EuiBadge/EuiBadge',
Expand Down Expand Up @@ -50,3 +54,42 @@ export const CustomColors: Story = {
color: '#0000FF',
},
};

const KitchenSinkVariantRow = (props: Pick<EuiBadgeProps, 'color'>) => (
<EuiBadgeGroup>
<EuiBadge {...props}>Badge</EuiBadge>
<EuiBadge {...props} iconType="check">
Badge
</EuiBadge>
<EuiBadge
{...props}
iconType="cross"
iconSide="right"
iconOnClick={action('iconOnClick')}
iconOnClickAriaLabel="A dummy action icon"
>
Badge with iconOnClick
</EuiBadge>
<EuiBadge {...props} href="#">
Badge with href
</EuiBadge>
<EuiBadge {...props} iconType="check" children={undefined} />
</EuiBadgeGroup>
);

export const KitchenSink: Story = {
parameters: {
controls: {
disable: true,
},
},
render() {
return (
<EuiFlexGroup gutterSize="m" direction="column">
{COLORS.map((color, index) => (
<KitchenSinkVariantRow color={color} key={index} />
))}
</EuiFlexGroup>
);
},
};
34 changes: 25 additions & 9 deletions packages/eui/src/components/badge/badge.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,18 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
--euiBadgeBackgroundColor: ${colors.backgroundColor};
`;

const inlinePadding = mathWithUnits(
// Account for the (usually transparent) border so that the visual
// padding is of size s
[euiTheme.size.s, euiTheme.border.width.thin],
(size, borderWidth) => size - borderWidth
);

return {
euiBadge: css`
display: inline-block;
vertical-align: middle;
${logicalShorthandCSS('padding', `0 ${euiTheme.size.s}`)}
${logicalShorthandCSS('padding', `0 ${inlinePadding}`)}
${logicalCSS('max-width', '100%')}
font-size: ${euiFontSize(euiThemeContext, 'xs').fontSize};
line-height: ${mathWithUnits(
Expand All @@ -46,10 +53,11 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
text-decoration: none;
cursor: inherit;
border: ${euiTheme.border.width.thin} solid transparent;
border-radius: ${mathWithUnits(
euiTheme.border.radius.medium,
(x) => x / 2
)};

/* border radius is intentionally larger to protect against external
customizations that might affect badge height */
border-radius: ${euiTheme.size.l};

/* The badge will only ever be as wide as its content
So, make the text left aligned to ensure all badges line up the same */
${logicalTextAlignCSS('left')}
Expand Down Expand Up @@ -81,6 +89,13 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
${logicalCSS('margin-left', euiTheme.size.xs)}
}
`,
iconOnly: css`
padding-inline: ${mathWithUnits(
// Account for the border
[euiTheme.size.xs, euiTheme.border.width.thin],
(size, borderWidth) => size - borderWidth
)};
`,
clickable: css`
&:not(:disabled) {
&:hover,
Expand Down Expand Up @@ -143,6 +158,7 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
text: {
euiBadge__text: css`
${euiTextTruncate()}
padding-inline: ${euiTheme.size.xxs};
cursor: inherit;
`,
clickable: css`
Expand All @@ -155,12 +171,12 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
euiBadge__icon: css``,
right: css`
&:not(:only-child) {
${logicalCSS('margin-left', euiTheme.size.xs)}
${logicalCSS('margin-left', euiTheme.size.xxs)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tkajtoch, thank you so much for the changes 🙏🏻 Looks perfect, but I have only one question/neat: I know in design it is 8px padding from left/right to the icon. But since here in code the border is counted too, it looks just a liiiittle too much, and I think we could go for 6px from both sider. Would that change be possible?
CleanShot 2026-01-09 at 13 57 46@2x
CleanShot 2026-01-09 at 13 58 18@2x
CleanShot 2026-01-09 at 13 58 28@2x

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed in e86fe30

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a token for 6px spacing, so the thing that made the most sense to me was subtracting the border width from the size s used for inline padding. The resulting inline padding is 7px, which I think looks good. Let me know!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with it, works for me!

A bit of a side question, but I wonder if we should actually introduce those tokens. For smaller elements they might make sense to perfectly balance the composition sometimes... cc @JoseLuisGJ
I wonder what do you both think? We we can continue discussion somewhere else of course, just wanted to mention in a context :)
CleanShot 2026-01-12 at 09 01 11@2x

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think it could be a little too much in terms of available options considering there are also values above base, but I'd love to hear others' opinion on this.

Let me merge this PR as-is then and we can discuss the sizing tokens somewhere else - how about a slack thread? :)

}
`,
left: css`
&:not(:only-child) {
${logicalCSS('margin-right', euiTheme.size.xs)}
${logicalCSS('margin-right', euiTheme.size.xxs)}
}
`,
},
Expand All @@ -180,10 +196,10 @@ export const euiBadgeStyles = (euiThemeContext: UseEuiTheme) => {
}
`,
right: css`
${logicalCSS('margin-left', euiTheme.size.xs)}
${logicalCSS('margin-left', euiTheme.size.xxs)}
`,
left: css`
${logicalCSS('margin-right', euiTheme.size.xs)}
${logicalCSS('margin-right', euiTheme.size.xxs)}
`,
},

Expand Down
38 changes: 37 additions & 1 deletion packages/eui/src/components/badge/badge.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import React from 'react';
import { shouldRenderCustomStyles } from '../../test/internal';
import { requiredProps } from '../../test/required_props';
import { render } from '../../test/rtl';
import { render, renderHook } from '../../test/rtl';
import { useEuiTheme } from '../../services';

import { EuiBadge, COLORS, ICON_SIDES } from './badge';
import { mathWithUnits, UseEuiTheme } from '@elastic/eui-theme-common';

describe('EuiBadge', () => {
shouldRenderCustomStyles(
Expand Down Expand Up @@ -215,4 +217,38 @@ describe('EuiBadge', () => {
});
});
});

describe('styles', () => {
let theme: UseEuiTheme;

beforeAll(() => {
theme = renderHook(useEuiTheme).result.current;
});

it('applies correct sizing styles to the main element', () => {
const { container } = render(<EuiBadge>Badge</EuiBadge>);

expect(container.firstChild).toHaveStyleRule('padding-block', '0');
expect(container.firstChild).toHaveStyleRule(
'padding-inline',
mathWithUnits(
[theme.euiTheme.size.s, theme.euiTheme.border.width.thin],
(size, borderWidth) => size - borderWidth
)
);
});

it('applies custom sizing styles to the main element when rendering an icon-only variant', () => {
const { container } = render(<EuiBadge iconType="gear" />);

expect(container.firstChild).toHaveStyleRule('padding-block', '0');
expect(container.firstChild).toHaveStyleRule(
'padding-inline',
mathWithUnits(
[theme.euiTheme.size.xs, theme.euiTheme.border.width.thin],
(size, borderWidth) => size - borderWidth
)
);
});
});
});
4 changes: 3 additions & 1 deletion packages/eui/src/components/badge/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const EuiBadge: FunctionComponent<EuiBadgeProps> = ({
const isHrefValid = !href || validateHref(href);
const isDisabled = _isDisabled || !isHrefValid;
const isNamedColor = COLORS.includes(color as BadgeColor);
const isIconOnly = !children && !!iconType;

const euiTheme = useEuiTheme();
const customColorStyles = useMemo(() => {
Expand Down Expand Up @@ -169,6 +170,7 @@ export const EuiBadge: FunctionComponent<EuiBadgeProps> = ({
const styles = useEuiMemoizedStyles(euiBadgeStyles);
const cssStyles = [
styles.euiBadge,
isIconOnly && styles.iconOnly,
...(isDisabled
? [styles.disabled]
: [
Expand Down Expand Up @@ -244,7 +246,7 @@ export const EuiBadge: FunctionComponent<EuiBadgeProps> = ({
optionalIcon = (
<EuiIcon
type={iconType}
size={children ? 's' : 'm'}
size="s"
className="euiBadge__icon"
css={iconCssStyles}
color="inherit" // forces the icon to inherit its parent color
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ exports[`EuiSelectableListItem props activeOptionIndex 1`] = `

<span
aria-hidden="true"
class="euiBadge euiSelectableListItem__onFocusBadge emotion-euiBadge-hollow"
class="euiBadge euiSelectableListItem__onFocusBadge emotion-euiBadge-iconOnly-hollow"
>
<span
class="euiBadge__content emotion-euiBadge__content"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ exports[`EuiSelectableListItem props isFocused 1`] = `

<span
aria-hidden="true"
class="euiBadge euiSelectableListItem__onFocusBadge emotion-euiBadge-hollow"
class="euiBadge euiSelectableListItem__onFocusBadge emotion-euiBadge-iconOnly-hollow"
>
<span
class="euiBadge__content emotion-euiBadge__content"
Expand Down Expand Up @@ -603,7 +603,7 @@ exports[`EuiSelectableListItem props tooltip behavior when isFocused 1`] = `

<span
aria-hidden="true"
class="euiBadge euiSelectableListItem__onFocusBadge emotion-euiBadge-hollow"
class="euiBadge euiSelectableListItem__onFocusBadge emotion-euiBadge-iconOnly-hollow"
>
<span
class="euiBadge__content emotion-euiBadge__content"
Expand Down