diff --git a/.changeset/lucky-socks-carry.md b/.changeset/lucky-socks-carry.md
new file mode 100644
index 0000000000..6a0e8fdc67
--- /dev/null
+++ b/.changeset/lucky-socks-carry.md
@@ -0,0 +1,5 @@
+---
+'@primer/react-brand': patch
+---
+
+Added descriptive labels to `Footnote` return links for an improved screen-reader experience. Return links will now be read aloud as "Back to content {Link label}"
diff --git a/package-lock.json b/package-lock.json
index 0cadee09ab..0bf979e58a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -83,7 +83,7 @@
},
"apps/storybook": {
"name": "@primer/brand-storybook",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "7.26.9",
@@ -23689,7 +23689,7 @@
},
"packages/css": {
"name": "@primer/brand-css",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "UNLICENSED",
"devDependencies": {
"@types/node": "22.18.1",
@@ -23705,7 +23705,7 @@
},
"packages/design-tokens": {
"name": "@primer/brand-primitives",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "MIT",
"devDependencies": {
"@primer/primitives": "9.1.1",
@@ -23719,7 +23719,7 @@
},
"packages/e2e": {
"name": "@primer/brand-e2e",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "MIT",
"devDependencies": {
"@github/axe-github": "^0.5.0",
@@ -23735,7 +23735,7 @@
},
"packages/fonts": {
"name": "@primer/brand-fonts",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "MIT",
"engines": {
"node": ">=22.0.0",
@@ -23744,7 +23744,7 @@
},
"packages/react": {
"name": "@primer/react-brand",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "MIT",
"dependencies": {
"@oddbird/popover-polyfill": "0.5.2",
@@ -23901,7 +23901,7 @@
},
"packages/repo-configs": {
"name": "@primer/brand-config",
- "version": "0.60.1",
+ "version": "0.61.0",
"license": "MIT"
}
}
diff --git a/packages/react/src/Footnotes/Footnotes.test.tsx b/packages/react/src/Footnotes/Footnotes.test.tsx
index fbaa32118e..b0f3cb06b8 100644
--- a/packages/react/src/Footnotes/Footnotes.test.tsx
+++ b/packages/react/src/Footnotes/Footnotes.test.tsx
@@ -4,6 +4,7 @@ import '@testing-library/jest-dom'
import {axe, toHaveNoViolations} from 'jest-axe'
import {Footnotes} from './Footnotes'
+import {InlineLink} from '../InlineLink'
import '../test-utils/mocks/match-media-mock'
expect.extend(toHaveNoViolations)
@@ -89,18 +90,35 @@ describe('Footnotes', () => {
const mockHref = 'https://github.com'
const {getByText, getByRole} = render(
- {mockItemText}
+ {mockItemText}
,
)
const itemEl = getByText(mockItemText)
expect(itemEl).toBeInTheDocument()
- const backLink = getByRole('link', {name: 'Back to content'})
+ const backLink = getByRole('link', {name: `Back to content ${mockItemText}`})
expect(backLink).toBeInTheDocument()
expect(backLink).toHaveAttribute('href', mockHref)
const iconEl = backLink.querySelector('svg')
expect(iconEl).toBeInTheDocument()
})
+
+ it('extracts text content from nested children for aria-label', async () => {
+ const mockHref = 'https://github.com'
+ const {getByRole} = render(
+
+
+ This factor is based on data from the industry's{' '}
+ longest running analysis by Acme Corp.
+
+ ,
+ )
+
+ const backLink = getByRole('link', {
+ name: "Back to content This factor is based on data from the industry's longest running analysis by Acme Corp.",
+ })
+ expect(backLink).toBeInTheDocument()
+ })
})
diff --git a/packages/react/src/Footnotes/Footnotes.tsx b/packages/react/src/Footnotes/Footnotes.tsx
index 2fe3834b13..01101ca837 100644
--- a/packages/react/src/Footnotes/Footnotes.tsx
+++ b/packages/react/src/Footnotes/Footnotes.tsx
@@ -7,6 +7,7 @@ import type {BaseProps} from '../component-helpers'
/** * Main Stylesheet (as a CSS Module) */
import styles from './Footnotes.module.css'
+import {getTextContent} from '../utils/getTextContent'
export const FootnotesTags = ['div', 'ol'] as const
@@ -92,7 +93,7 @@ function Item({className, children, _variant = 'citation', ...rest}: FootnotesIt
{children}
{href && (
-
+
)}
diff --git a/packages/react/src/utils/getTextContent.ts b/packages/react/src/utils/getTextContent.ts
new file mode 100644
index 0000000000..63a45051c7
--- /dev/null
+++ b/packages/react/src/utils/getTextContent.ts
@@ -0,0 +1,20 @@
+import React from 'react'
+
+export function getTextContent(node: React.ReactNode): string {
+ if (!node) return ''
+
+ if (typeof node === 'string' || typeof node === 'number') {
+ return String(node)
+ }
+
+ if (Array.isArray(node)) {
+ return node.map(getTextContent).join('')
+ }
+
+ if (React.isValidElement(node)) {
+ const props = node.props as {children?: React.ReactNode}
+ return getTextContent(props.children)
+ }
+
+ return ''
+}