ButtonGroup does not treat Badge-wrapped buttons as group items
What version of rsuite are you using?
6.2.4
What version of React are you using?
19.3.0
What version of TypeScript are you using (if any)?
6.0.3
What browser are you using?
Chrome
Describe the Bug
Badge is documented as a wrapper around another element (for example a Button). That pattern does not work inside ButtonGroup.
ButtonGroup styles only target direct .rs-btn children:
.rs-btn-group:not([data-vertical='true']) {
> .rs-btn { /* radius, float */ }
> .rs-btn[data-appearance='ghost'] + .rs-btn[data-appearance='ghost'] { /* collapse border */ }
}
.rs-btn-group[data-justified='true'] {
> .rs-btn {
flex: 1 1 1%;
}
}Wrapping a button with Badge changes the group’s direct child from .rs-btn to .rs-badge. The inner button then drops out of:
- Justified layout — the item does not get
flex: 1 1 1%, so it no longer shares width with sibling buttons. - Connected border-radius —
:first-child/:last-childapply to theBadge, not the inner button. The wrapped button keeps all four corners rounded; neighbors can also get the wrong corners. - Ghost border collapse — adjacent-sibling rules only match
.rs-btn + .rs-btn, so aBadgein the middle leaves a double border. - Focus / active stacking —
z-indexis only set on> .rs-btn.
The same gap exists for vertical and divided groups.
Putting the Badge inside the button still works for an inline count. Wrapping the button is the documented overlay usage, and that is what breaks.
Expected Behavior
A ButtonGroup item that is <Badge><Button /></Badge> should participate in the same group layout as a bare Button:
- equal width when
justified - shared / collapsed corners
- collapsed ghost borders (
margin-inline-start: -1px) - the inner button stretching to fill the item
To Reproduce
import { Badge, Button, ButtonGroup } from 'rsuite';
import 'rsuite/dist/rsuite.min.css';
export default function App() {
return (
<div style={{ width: 360, padding: 16 }}>
<ButtonGroup justified>
<Button appearance="ghost">Doc</Button>
<Button appearance="ghost">Folder</Button>
<Badge color="orange" content="3/3">
<Button appearance="ghost">Knowledge</Button>
</Badge>
<Button appearance="ghost">Template</Button>
</ButtonGroup>
</div>
);
}Compare with the same group without the Badge wrapper: four equal, connected ghost buttons.
Possible fix
Treat a direct-child .rs-badge that wraps a .rs-btn as a group item, mirroring the existing > .rs-btn rules. Something like:
.rs-btn-group > .rs-badge {
position: relative;
z-index: 3;
}
.rs-btn-group:not([data-vertical='true']) > .rs-badge {
float: inline-start;
}
.rs-btn-group:not([data-vertical='true']) > .rs-badge:not(:last-child) > .rs-btn {
border-end-end-radius: 0;
border-start-end-radius: 0;
}
.rs-btn-group:not([data-vertical='true']) > .rs-badge:not(:first-child) > .rs-btn {
border-end-start-radius: 0;
border-start-start-radius: 0;
}
.rs-btn-group:not([data-vertical='true']) > .rs-btn[data-appearance='ghost'] + .rs-badge,
.rs-btn-group:not([data-vertical='true']) > .rs-badge + .rs-badge {
margin-inline-start: -1px;
}
.rs-btn-group:not([data-vertical='true']) > .rs-badge + .rs-btn[data-appearance='ghost'] {
margin-inline-start: -1px;
}
.rs-btn-group[data-justified='true'] > .rs-badge {
display: flex;
flex: 1 1 1%;
}
.rs-btn-group[data-justified='true'] > .rs-badge > .rs-btn {
width: 100%;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}Vertical / divided groups would need the matching > .rs-btn counterparts.
Source: rsuite/rsuite