diff --git a/packages/react/src/BranchName/BranchName.features.stories.tsx b/packages/react/src/BranchName/BranchName.features.stories.tsx
index 85db4bb601c..1c3f95c2a29 100644
--- a/packages/react/src/BranchName/BranchName.features.stories.tsx
+++ b/packages/react/src/BranchName/BranchName.features.stories.tsx
@@ -1,8 +1,16 @@
import type {Meta} from '@storybook/react-vite'
+import React from 'react'
import BranchName from './BranchName'
import {Stack} from '../Stack'
import Octicon from '../Octicon'
-import {GitBranchIcon} from '@primer/octicons-react'
+import {GitBranchIcon, CopyIcon, CheckIcon} from '@primer/octicons-react'
+import {IconButton} from '../Button'
+import {Tooltip} from '../TooltipV2'
+import {VisuallyHidden} from '../VisuallyHidden'
+import {Announce} from '../live-region'
+import {clsx} from 'clsx'
+
+import styles from './BranchName.stories.module.css'
export default {
title: 'Components/BranchName/Features',
@@ -28,3 +36,47 @@ export const LinkWithoutHref = () => (
)
export const NoProps = () => branch_name_no_props
+
+export const WithTrailingAction = ({
+ branchName = 'fix/anchored-overlay-outside-top-autogrow',
+ repo = 'primer/react',
+}: {
+ branchName: string
+ repo: string
+}) => {
+ const [copied, setCopied] = React.useState(false)
+
+ const handleCopy = () => {
+ setCopied(true)
+ void navigator.clipboard.writeText(branchName)
+ setTimeout(() => setCopied(false), 2000)
+ }
+
+ const tooltipText = copied ? 'Copied!' : 'Copy branch name to clipboard'
+
+ return (
+
+
+
+ {branchName}
+
+
+ {/* Screen reader announcement for copy success */}
+ {copied && (
+
+ Copied!
+
+ )}
+
+
+
+
+ )
+}
diff --git a/packages/react/src/BranchName/BranchName.stories.module.css b/packages/react/src/BranchName/BranchName.stories.module.css
new file mode 100644
index 00000000000..2754c8e563e
--- /dev/null
+++ b/packages/react/src/BranchName/BranchName.stories.module.css
@@ -0,0 +1,57 @@
+/* Story-only styles for demonstrating trailing action pattern */
+
+.BranchNameWithTrailingAction {
+ display: inline-flex;
+ /* Let items stretch to fill container height */
+ align-items: stretch;
+ /* Create room for pseudo-border divider */
+ gap: var(--borderWidth-default);
+ flex-wrap: nowrap;
+ /* Moving the background to the container allows stacking the button hover background over it */
+ background-color: var(--bgColor-accent-muted);
+ border-radius: var(--borderRadius-medium);
+ overflow: hidden;
+}
+
+.BranchNameTransparent {
+ background-color: transparent;
+}
+
+.TrailingActionButton {
+ /* Make the size auto calculated based on icon size and padding */
+ height: auto;
+ width: auto;
+ padding: var(--base-size-2) var(--base-size-6);
+ position: relative;
+ /* Center content when button stretches to fill container */
+ align-items: center;
+
+ &:not(:disabled, [aria-disabled='true']) {
+ --button-invisible-iconColor-rest: var(--fgColor-link);
+ }
+
+ &.TrailingActionButtonCopied:not(:disabled, [aria-disabled='true']) {
+ --button-invisible-iconColor-rest: var(--fgColor-success);
+ }
+
+ [data-component='Octicon'] {
+ /* Set icon size to text size to exactly match the branch name component */
+ height: var(--text-body-size-small);
+ width: var(--text-body-size-small);
+ }
+
+ /* Dividing line between button and name. Using ::before on button allows for multiple buttons (with a line between each) */
+ &::before {
+ content: '';
+ position: absolute;
+ width: var(--borderWidth-default);
+ /* Position _between_ items (offsetting by width) to avoid overlapping with the real button border on hover/focus */
+ inset: var(--base-size-4) auto var(--base-size-4) calc(-1 * var(--borderWidth-default));
+ /* stylelint-disable-next-line primer/colors */
+ background-color: var(--borderColor-accent-muted);
+ }
+}
+
+.TrailingActionButtonCopied {
+ /* Class exists for JS toggling - styles handled via compound selector above */
+}