From 6d3da6556c26b40b106e92ffd6b120e66a281deb Mon Sep 17 00:00:00 2001 From: Matthew Costabile Date: Fri, 29 May 2026 14:50:50 +0000 Subject: [PATCH 1/2] perf(SegmentedControl): replace :has(+ [data-selected]) divider rule with adjacent-sibling selector --- .../perf-segmentedcontrol-divider-no-has.md | 5 ++ .../SegmentedControl.module.css | 46 +++++++++++++------ 2 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 .changeset/perf-segmentedcontrol-divider-no-has.md diff --git a/.changeset/perf-segmentedcontrol-divider-no-has.md b/.changeset/perf-segmentedcontrol-divider-no-has.md new file mode 100644 index 00000000000..4321b661fdb --- /dev/null +++ b/.changeset/perf-segmentedcontrol-divider-no-has.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +SegmentedControl: move the inter-item divider from `::after` of each non-last item to `::before` of each non-first item so the "hide divider next to the selected item" rule can be expressed with the adjacent-sibling combinator instead of `:has(+ [data-selected])`. The 1px line sits at the same visual gap between items; no rendered output change. diff --git a/packages/react/src/SegmentedControl/SegmentedControl.module.css b/packages/react/src/SegmentedControl/SegmentedControl.module.css index 05bc0feed8f..4c02ed4f0b0 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.module.css +++ b/packages/react/src/SegmentedControl/SegmentedControl.module.css @@ -169,23 +169,29 @@ &:not(:last-child) { /* stylelint-disable-next-line primer/spacing */ margin-right: 1px; + } - &::after { - position: absolute; - top: var(--base-size-8); - right: calc(-1 * var(--base-size-2)); - bottom: var(--base-size-8); - width: 1px; - content: ''; - /* stylelint-disable-next-line primer/colors */ - background-color: var(--borderColor-default); - } + /* + * Divider is drawn on the LEADING edge of each non-first item (rather than + * the trailing edge of each non-last item) so the "hide divider before the + * selected item" case can be expressed as a plain adjacent-sibling selector + * instead of `:has(+ [data-selected])`. Visual position is unchanged because + * the same 1px line lives at the same gap between two adjacent items. + */ + &:not(:first-child)::before { + position: absolute; + top: var(--base-size-8); + left: calc(-1 * var(--base-size-2)); + bottom: var(--base-size-8); + width: 1px; + content: ''; + /* stylelint-disable-next-line primer/colors */ + background-color: var(--borderColor-default); + } - /* stylelint-disable-next-line selector-pseudo-class-disallowed-list -- scoped to CSS Module, audited (github/github-ui#17224) */ - &:has(+ [data-selected])::after, - &:where([data-selected])::after { - background-color: transparent; - } + /* Hide the divider on the leading edge of the selected item itself. */ + &:where([data-selected])::before { + background-color: transparent; } /* stylelint-disable-next-line selector-pseudo-class-disallowed-list -- scoped to CSS Module, audited (github/github-ui#17224) */ @@ -210,6 +216,16 @@ } } +/* + * Hide the divider on the leading edge of the item that immediately follows + * the selected item. Adjacent-sibling combinator avoids the previous + * `:has(+ [data-selected])::after` which forced a sibling-direction `:has()` + * re-evaluation on every selection change. + */ +.Item[data-selected] + .Item::before { + background-color: transparent; +} + .Button { /* TODO: use primitive `primer.control.medium.paddingInline.normal` when it is available */ --segmented-control-button-inner-padding: 12px; From 83d8e5201915cb32ff3a3480edb8c15c21f29735 Mon Sep 17 00:00:00 2001 From: Matthew Costabile Date: Fri, 29 May 2026 15:47:48 +0000 Subject: [PATCH 2/2] fix(SegmentedControl): align divider to original position (left: 0, not -2px) --- .../src/SegmentedControl/SegmentedControl.module.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react/src/SegmentedControl/SegmentedControl.module.css b/packages/react/src/SegmentedControl/SegmentedControl.module.css index 4c02ed4f0b0..057dd63c53d 100644 --- a/packages/react/src/SegmentedControl/SegmentedControl.module.css +++ b/packages/react/src/SegmentedControl/SegmentedControl.module.css @@ -175,13 +175,15 @@ * Divider is drawn on the LEADING edge of each non-first item (rather than * the trailing edge of each non-last item) so the "hide divider before the * selected item" case can be expressed as a plain adjacent-sibling selector - * instead of `:has(+ [data-selected])`. Visual position is unchanged because - * the same 1px line lives at the same gap between two adjacent items. + * instead of `:has(+ [data-selected])`. Visual position is unchanged: the + * original `::after` at `right: -2px` (1px wide) on item N sat in the first + * 1px of item N+1's column (the previous item has `margin-right: 1px`), so + * the equivalent ::before on item N+1 lives at `left: 0`. */ &:not(:first-child)::before { position: absolute; top: var(--base-size-8); - left: calc(-1 * var(--base-size-2)); + left: 0; bottom: var(--base-size-8); width: 1px; content: '';