Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
4ea9fa0
Spike for breadcrumbs overflow
pksjce Aug 11, 2025
42cac37
Add changeset for breadcrumbs overflow
pksjce Aug 11, 2025
5e7a4b8
Add review comments and change behavior
pksjce Aug 12, 2025
fd6a3fe
Fix up some issues.
pksjce Aug 13, 2025
66908bb
check for zero children
pksjce Aug 13, 2025
73e7941
Fix bug with hideRoot
pksjce Aug 13, 2025
2dfe917
Add test cases
pksjce Aug 13, 2025
3afee5d
Add docs
pksjce Aug 13, 2025
d9b9228
Breadcrumbs can have more stories
pksjce Aug 13, 2025
af2997f
Add features stories for breadcrumbs
pksjce Aug 13, 2025
d8547a0
Add wrapped breadcrumbs in story
pksjce Aug 14, 2025
8a25c92
chore(release): enter pre-release mode for v38 (#6600)
joshblack Aug 14, 2025
565a925
ci(migration-status): update styled-components migration status repor…
joshblack Aug 14, 2025
4379be7
chore(mcp): update package to be private (#6606)
joshblack Aug 14, 2025
71de059
fix: add mini-throttle as dep to packages/react (#6604)
joshblack Aug 14, 2025
540181b
refactor(SubNav): Remove sx support from SubNav component (#6602)
jonrohan Aug 14, 2025
7577876
Add support for high contrast themes to `ProgressBar` (#6431)
langermank Aug 14, 2025
0ec1a35
Only show focus outline for `Radio` if `focus-visible` (#6603)
langermank Aug 14, 2025
2d8d59b
Fix for ssr and child key
pksjce Aug 15, 2025
c3ef7ad
Merge branch 'main' into pk/breadcrumbs
pksjce Aug 15, 2025
36adfc8
Add IconButton
pksjce Aug 15, 2025
481f83c
Merge branch 'main' into pk/breadcrumbs
pksjce Aug 18, 2025
9742aaa
Fix for SSR
pksjce Aug 18, 2025
43c141b
Fix for SSR
pksjce Aug 18, 2025
e56256f
Final changes for SSR
pksjce Aug 18, 2025
a096e08
Rework calculations
pksjce Aug 19, 2025
b071e70
Tests are passing
pksjce Aug 19, 2025
bfc5260
test(vrt): update snapshots
pksjce Aug 19, 2025
ca57590
Small fixes to menu button
pksjce Aug 21, 2025
4697c77
Fix styling issues with button
pksjce Aug 21, 2025
832b43a
Merge branch 'main' into pk/breadcrumbs
pksjce Aug 21, 2025
30d6670
Remove rogue snapshots
pksjce Aug 21, 2025
de35ab7
test(vrt): update snapshots
pksjce Aug 21, 2025
cb1cd25
Update paths in package.json exports (#6635)
joshblack Aug 21, 2025
34df59e
Release tracking (rc) (#6589)
primer[bot] Aug 21, 2025
b0c278c
chore(deps): bump the babel group with 6 updates (#6617)
dependabot[bot] Aug 21, 2025
9bb5ed7
chore(VisuallyHidden): Remove sx prop support and refactor VisuallyHi…
jonrohan Aug 21, 2025
23a2ff9
refactor(Stack): Remove sx prop support from the Stack component (#6610)
jonrohan Aug 21, 2025
688354b
chore(deps): bump actions/create-github-app-token from 2.1.0 to 2.1.1…
dependabot[bot] Aug 21, 2025
8281004
chore(deps): bump actions/upload-pages-artifact from 3.0.1 to 4.0.0 (…
dependabot[bot] Aug 21, 2025
25ba968
chore(deps): bump actions/checkout from 4.2.2 to 5.0.0 (#6616)
dependabot[bot] Aug 21, 2025
a3434a5
chore(deps-dev): bump @eslint-react/eslint-plugin from 1.50.0 to 1.52…
dependabot[bot] Aug 21, 2025
db0a421
chore(migration): update styled-components migration script (#6640)
joshblack Aug 21, 2025
faff5ae
feat(styled-react): add all components as exports (#6612)
joshblack Aug 21, 2025
690cf75
chore(deps-dev): bump the storybook group with 5 updates (#6621)
dependabot[bot] Aug 21, 2025
9f7655d
Fix flaky test for SelectPanel vrt (#6623)
pksjce Aug 22, 2025
e99fb5d
Remove deprecated AvatarPair component (#6595)
Copilot Aug 22, 2025
68ec9a9
Migrate SelectPanel.features.stories.tsx from styled-components to CS…
Copilot Aug 22, 2025
8f4f149
Fix npm audit issues (#6648)
hectahertz Aug 22, 2025
32532ca
chore(deps): update playwright to 1.55.0 (#6638)
joshblack Aug 22, 2025
9b4c7a1
fix(styled-react): update types output (#6649)
joshblack Aug 22, 2025
8f08bf5
Remove TypeScript ESLint plugins from package.json (#6472)
joshblack Aug 22, 2025
5520ce8
Remove support for `sx` from `ActionMenu` component (#6626)
llastflowers Aug 22, 2025
bf13548
Remove sx support from SplitPageLayout component (#6613)
llastflowers Aug 22, 2025
65f0e8b
Remove support for `sx` from `AnchoredOverlay` component (#6628)
llastflowers Aug 22, 2025
dd41923
Remove support for `sx` from `Autocomplete` component (#6629)
llastflowers Aug 22, 2025
e5e5f1a
Update Overlay.stories.tsx to no longer use styled-components (#6494)
Copilot Aug 22, 2025
31fac51
test(e2e): skip flaky test (#6650)
joshblack Aug 22, 2025
129a753
chore(deps): update typescript to 5.9.2 (#6637)
joshblack Aug 22, 2025
3b11ca6
chore(deps-dev): bump the eslint group with 7 updates (#6619)
dependabot[bot] Aug 22, 2025
87488bb
Fix focus states
pksjce Aug 22, 2025
0d61e88
Fix focus states
pksjce Aug 22, 2025
660dd11
Merge branch 'main' into pk/breadcrumbs
pksjce Aug 25, 2025
a34f53f
test(vrt): update snapshots
pksjce Aug 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/good-cougars-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

Spike for breadcrumbs overflow
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.
35 changes: 31 additions & 4 deletions packages/react/src/Breadcrumbs/Breadcrumbs.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,34 @@
"name": "className",
"type": "string",
"required": false,
"description": "",
"description": "Additional CSS class names to apply to the breadcrumbs container",
"defaultValue": ""
},
{
"name": "children",
"type": "Breadcrumbs.Item[]",
"defaultValue": "",
"description": ""
"description": "Breadcrumb items to render. Each item should be a Breadcrumbs.Item component."
},
{
"name": "overflow",
"type": "'wrap' | 'menu'",
"required": false,
"description": "How to handle overflow when breadcrumbs don't fit in the container. 'wrap' allows items to wrap to new lines, 'menu' collapses items into an overflow menu.",
"defaultValue": "'wrap'"
},
{
"name": "hideRoot",
"type": "boolean",
"required": false,
"description": "When using overflow='menu', whether to prioritize hiding the root (first) item when space is limited. If false, the root item will be kept visible when possible.",
"defaultValue": "true"
},
{
"name": "sx",
"type": "SystemStyleObject",
"deprecated": true
"deprecated": true,
"description": "System styles (deprecated, use CSS classes instead)"
}
],
"subcomponents": [
Expand All @@ -37,7 +52,7 @@
"name": "selected",
"type": "boolean",
"defaultValue": "false",
"description": "Whether this item represents the current page"
"description": "Whether this item represents the current page. Sets aria-current='page' for accessibility."
},
{
"name": "to",
Expand All @@ -46,6 +61,18 @@
"description": "Used when the item is rendered using a component like React Router's `Link`. The path to navigate to.",
"defaultValue": ""
},
{
"name": "href",
"type": "string",
"required": false,
"description": "The URL that the breadcrumb item links to. Used with regular anchor elements."
},
{
"name": "children",
"type": "React.ReactNode",
"required": true,
"description": "The content to display inside the breadcrumb item, typically text."
},
{
"name": "ref",
"type": "React.RefObject<HTMLAnchorElement>"
Expand Down
105 changes: 105 additions & 0 deletions packages/react/src/Breadcrumbs/Breadcrumbs.features.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type {Meta} from '@storybook/react-vite'
import type React from 'react'
import type {ComponentProps} from '../utils/types'
import Breadcrumbs from './Breadcrumbs'

export default {
title: 'Components/Breadcrumbs/Features',
component: Breadcrumbs,
} as Meta<ComponentProps<typeof Breadcrumbs>>

export const OverflowWrap = () => (
<Breadcrumbs overflow="wrap">
<Breadcrumbs.Item href="#">Home</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Products</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Category</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Subcategory</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Item</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Details</Breadcrumbs.Item>
<Breadcrumbs.Item href="#" selected>
Current Page
</Breadcrumbs.Item>
</Breadcrumbs>
)

export const OverflowMenu = () => (
<Breadcrumbs overflow="menu">
<Breadcrumbs.Item href="#">Home</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Products</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Category</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">SubcategorySubcategorySubcategorySubcategory</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">ItemItemItemItemItemItemItem</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">DetailsDetailsDetailsDetailsDetailsDetailsDetails</Breadcrumbs.Item>
<Breadcrumbs.Item href="#" selected>
Current Page
</Breadcrumbs.Item>
</Breadcrumbs>
)

export const OverflowMenuShowRoot = () => (
<Breadcrumbs overflow="menu" hideRoot={false}>
<Breadcrumbs.Item href="#">github</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Teams</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Engineering</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">core-productivity</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">collaboration-workflows-flex</Breadcrumbs.Item>
<Breadcrumbs.Item href="#" selected>
global-navigation-reviewers
</Breadcrumbs.Item>
</Breadcrumbs>
)

export const OverflowMenuNarrowContainer = () => (
<div style={{width: '350px', border: '1px solid #ccc', padding: '8px'}}>
<Breadcrumbs overflow="menu">
<Breadcrumbs.Item href="#">Home</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Products</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Category</Breadcrumbs.Item>
<Breadcrumbs.Item href="#">Subcategory</Breadcrumbs.Item>
<Breadcrumbs.Item href="#" selected>
Current Page
</Breadcrumbs.Item>
</Breadcrumbs>
</div>
)

// Wrapper components to test that BreadcrumbsItem works when wrapped
const StyledWrapper = ({children}: {children: React.ReactNode}) => (
<span style={{padding: '2px', border: '1px dotted #999'}}>{children}</span>
)

const ConditionalWrapper = ({children, condition}: {children: React.ReactNode; condition: boolean}) => {
return condition ? <strong>{children}</strong> : <>{children}</>
}

const DataAttributeWrapper = ({children}: {children: React.ReactNode}) => (
<span data-testid="wrapper" className="custom-wrapper">
{children}
</span>
)

export const WrappedBreadcrumbItemsWithOverflow = () => (
<Breadcrumbs overflow="menu">
<StyledWrapper>
<Breadcrumbs.Item href="#">Wrapped Home</Breadcrumbs.Item>
</StyledWrapper>
<ConditionalWrapper condition={false}>
<Breadcrumbs.Item href="#">Products</Breadcrumbs.Item>
</ConditionalWrapper>
<DataAttributeWrapper>
<Breadcrumbs.Item href="#">Category</Breadcrumbs.Item>
</DataAttributeWrapper>
<StyledWrapper>
<Breadcrumbs.Item href="#">Subcategory</Breadcrumbs.Item>
</StyledWrapper>
<ConditionalWrapper condition={true}>
<Breadcrumbs.Item href="#">Item</Breadcrumbs.Item>
</ConditionalWrapper>
<DataAttributeWrapper>
<Breadcrumbs.Item href="#">Details</Breadcrumbs.Item>
</DataAttributeWrapper>
<Breadcrumbs.Item href="#" selected>
Current Page
</Breadcrumbs.Item>
</Breadcrumbs>
)
60 changes: 36 additions & 24 deletions packages/react/src/Breadcrumbs/Breadcrumbs.module.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.BreadcrumbsBase {
display: flex;
justify-content: space-between;
width: 100%;
}

.BreadcrumbsList {
Expand All @@ -9,31 +10,31 @@
margin-bottom: 0;
}

.ItemWrapper {
display: inline-block;
/* Prevent wrapping when using menu overflow */
[data-overflow='menu'] .BreadcrumbsList {
white-space: nowrap;
overflow: hidden;
display: flex;
flex-direction: row;
}

.BreadcrumbsItem {
display: inline-grid;
grid-auto-flow: column;
align-items: center;
flex: 0 99999 auto;
min-width: auto;
font-size: var(--text-body-size-medium);
white-space: nowrap;
list-style: none;

&::after {
display: inline-block;
height: 0.8em;
/* stylelint-disable-next-line primer/spacing */
margin: 0 0.5em;
font-size: var(--text-body-size-medium);
content: '';
/* stylelint-disable-next-line primer/borders, primer/colors */
border-right: 0.1em solid var(--fgColor-muted);
transform: rotate(15deg) translateY(0.0625em);
}

&:first-child {
margin-left: 0;
}

&:last-child {
&::after {
content: none;
.ItemSeparator {
display: none;
}
}
}
Expand All @@ -43,18 +44,29 @@
font-size: var(--text-body-size-medium);
color: var(--fgColor-link);
text-decoration: none;
padding-inline: var(--base-size-6);
padding-block: var(--base-size-4);
border-radius: var(--borderRadius-medium);

&:hover,
&:hover {
background: var(--control-transparent-bgColor-hover);
}
&:focus {
text-decoration: underline;
box-shadow: none;
outline: 2px solid var(--focus-outlineColor, var(--color-accent-fg));
outline-offset: -2px;
}
}

.ItemSelected {
color: var(--fgColor-default);
pointer-events: none;
.MenuButton {
color: var(--fgColor-link);
}

&:focus {
text-decoration: none;
}
.ItemSeparator {
color: var(--fgColor-muted);
display: flex;
align-self: center;
justify-content: center;
white-space: nowrap;
user-select: none;
}
Loading
Loading