Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c167bd9
fix(accessibility): Add underline styling to MDX links for WCAG compl…
NikhilChowdhury27 Nov 24, 2025
e09b934
test(accessibility): Add unit tests for underlined links in MDX/docs
NikhilChowdhury27 Nov 24, 2025
7dcec06
test(accessibility): Add unit tests for A component underlined links
NikhilChowdhury27 Nov 24, 2025
196a9a2
fix: Exclude anchor position markers from underline styling
NikhilChowdhury27 Nov 24, 2025
5de4197
fix: Add refined underline styling to all MDX and UI links for WCAG 2…
NikhilChowdhury27 Nov 25, 2025
79dcfcc
fix: Add textDecoration override to isButton variant in Link component
NikhilChowdhury27 Nov 25, 2025
5ddf417
Update code/addons/docs/src/blocks/components/DocsPage.tsx
NikhilChowdhury27 Dec 1, 2025
bb44dce
Update code/core/src/components/components/typography/elements/A.tsx
NikhilChowdhury27 Dec 1, 2025
b773fb7
Update code/addons/docs/src/blocks/components/DocsPage.tsx
NikhilChowdhury27 Dec 1, 2025
5aefb3e
Update code/core/src/components/components/Button/Button.tsx
NikhilChowdhury27 Dec 1, 2025
eec47dc
Update code/addons/docs/src/blocks/components/DocsPage.test.tsx
NikhilChowdhury27 Dec 1, 2025
66e76bb
Update code/core/src/components/components/typography/link/link.tsx
NikhilChowdhury27 Dec 1, 2025
095348e
Update code/core/src/components/components/typography/link/link.tsx
NikhilChowdhury27 Dec 1, 2025
3676017
Update code/core/src/components/components/typography/DocumentWrapper…
NikhilChowdhury27 Dec 1, 2025
312a9ed
Update code/core/src/components/components/typography/DocumentWrapper…
NikhilChowdhury27 Dec 1, 2025
2ac469c
Adjust anchor underline behavior and tests
NikhilChowdhury27 Dec 1, 2025
f671901
Update code/core/src/components/components/typography/DocumentWrapper…
NikhilChowdhury27 Dec 2, 2025
6b437ef
Update code/core/src/components/components/typography/elements/A.tsx
NikhilChowdhury27 Dec 2, 2025
74e4e20
Update code/core/src/components/components/typography/link/link.tsx
NikhilChowdhury27 Dec 2, 2025
c0fdc05
Update code/addons/docs/src/blocks/components/DocsPage.tsx
NikhilChowdhury27 Dec 2, 2025
fe3bfc1
UI: Adjust link decorations to design reqs
Sidnioulz Mar 6, 2026
fb5c625
Exclude Configure.mdx template from changes
Sidnioulz Mar 6, 2026
20ea036
Merge branch 'next' into fix-issue-33017-mdx-link-underlines
Sidnioulz Mar 6, 2026
d62b362
Merge branch 'next' into fix-issue-33017-mdx-link-underlines
Sidnioulz Mar 9, 2026
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
101 changes: 101 additions & 0 deletions code/addons/docs/src/blocks/components/DocsPage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// @vitest-environment happy-dom
import { cleanup, render } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import React from 'react';

import { ThemeProvider, convert, themes } from 'storybook/theming';

import { DocsContent } from './DocsPage';

function ThemedDocsContent({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider theme={convert(themes.light)}>
<DocsContent>{children}</DocsContent>
</ThemeProvider>
);
}

describe('DocsContent', () => {
afterEach(() => {
cleanup();
});

describe('accessibility', () => {
it('should render links with underline text decoration for accessibility', () => {
const { container } = render(
<ThemedDocsContent>
<p>
This is a paragraph with a <a href="https://example.com">link</a> inside.
</p>
</ThemedDocsContent>
);

const link = container.querySelector('a');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
});

it('should render links with underline in dark theme', () => {
const { container } = render(
<ThemeProvider theme={convert(themes.dark)}>
<DocsContent>
<p>
This is a paragraph with a <a href="https://example.com">link</a> inside.
</p>
</DocsContent>
</ThemeProvider>
);

const link = container.querySelector('a');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
});

it('should render multiple links with underlines in text blocks', () => {
const { container } = render(
<ThemedDocsContent>
<div>
<p>
Check out <a href="https://example.com">this link</a> and also{' '}
<a href="https://another.com">this other link</a>.
</p>
<p>
Here is <a href="https://third.com">a third link</a> in another paragraph.
</p>
</div>
</ThemedDocsContent>
);

const links = container.querySelectorAll('a');
expect(links).toHaveLength(3);

links.forEach((link) => {
const styles = window.getComputedStyle(link);
expect(styles.textDecoration).toContain('underline');
});
});

it('should not underline anchor position markers (a.anchor)', () => {
const { container } = render(
<ThemedDocsContent>
<h2>
<a className="anchor" href="#heading">
Heading
</a>
</h2>
</ThemedDocsContent>
);

const anchor = container.querySelector('a.anchor');
expect(anchor).toBeTruthy();

const styles = window.getComputedStyle(anchor!);
expect(styles.textDecoration).not.toContain('underline');
});
});
});
9 changes: 8 additions & 1 deletion code/addons/docs/src/blocks/components/DocsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ export const DocsContent = styled.div(({ theme }) => {
lineHeight: '24px',

color: theme.color.secondary,
textDecoration: 'none',
// Ensure WCAG Level A compliance (SC 1.4.1), see https://www.w3.org/WAI/WCAG22/Techniques/failures/F73
textDecoration: 'underline',
textDecorationThickness: '0.03125rem',
textUnderlineOffset: '0.11em',
'&.absent': {
color: '#cc0000',
},
Expand All @@ -127,6 +130,10 @@ export const DocsContent = styled.div(({ theme }) => {
top: 0,
left: 0,
bottom: 0,
textDecoration: 'none',
},
'&.anchor:hover, &.anchor:focus': {
textDecoration: 'underline',
},
},
[toGlobalSelector('blockquote')]: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// @vitest-environment happy-dom
import { cleanup, render } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import React from 'react';

import { ThemeProvider, convert, themes } from 'storybook/theming';

import { DocumentWrapper } from './DocumentWrapper';

function ThemedDocumentWrapper({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider theme={convert(themes.light)}>
<DocumentWrapper>{children}</DocumentWrapper>
</ThemeProvider>
);
}

describe('DocumentWrapper', () => {
afterEach(() => {
cleanup();
});

describe('accessibility', () => {
it('should render links with underline text decoration for accessibility', () => {
const { container } = render(
<ThemedDocumentWrapper>
<p>
This is a paragraph with a <a href="https://example.com">link</a> inside.
</p>
</ThemedDocumentWrapper>
);

const link = container.querySelector('a');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
});

it('should render links with underline in dark theme', () => {
const { container } = render(
<ThemeProvider theme={convert(themes.dark)}>
<DocumentWrapper>
<p>
This is a paragraph with a <a href="https://example.com">link</a> inside.
</p>
</DocumentWrapper>
</ThemeProvider>
);

const link = container.querySelector('a');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
});

it('should render multiple links with underlines in text blocks', () => {
const { container } = render(
<ThemedDocumentWrapper>
<div>
<p>
Check out <a href="https://example.com">this link</a> and also{' '}
<a href="https://another.com">this other link</a>.
</p>
<p>
Here is <a href="https://third.com">a third link</a> in another paragraph.
</p>
</div>
</ThemedDocumentWrapper>
);

const links = container.querySelectorAll('a');
expect(links).toHaveLength(3);

links.forEach((link) => {
const styles = window.getComputedStyle(link);
expect(styles.textDecoration).toContain('underline');
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export const DocumentWrapper = styled.div(({ theme }) => ({
},
a: {
color: theme.color.secondary,
textDecoration: 'none',
// Ensure WCAG Level A compliance (SC 1.4.1), see https://www.w3.org/WAI/WCAG22/Techniques/failures/F73
textDecoration: 'underline',
textDecorationThickness: '0.03125rem',
textUnderlineOffset: '0.11em',
},
'a.absent': {
color: '#cc0000',
Expand All @@ -78,6 +81,10 @@ export const DocumentWrapper = styled.div(({ theme }) => ({
top: 0,
left: 0,
bottom: 0,
textDecoration: 'none',
},
'&.anchor:hover, &.anchor:focus': {
textDecoration: 'underline',
},
'h1, h2, h3, h4, h5, h6': {
margin: '20px 0 10px',
Expand Down
75 changes: 75 additions & 0 deletions code/core/src/components/components/typography/elements/A.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// @vitest-environment happy-dom
import { cleanup, render } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import React from 'react';

import { ThemeProvider, convert, themes } from 'storybook/theming';

import { A } from './A';

function ThemedA({ children, ...props }: React.ComponentProps<typeof A>) {
return (
<ThemeProvider theme={convert(themes.light)}>
<A {...props}>{children}</A>
</ThemeProvider>
);
}

describe('A', () => {
afterEach(() => {
cleanup();
});

describe('accessibility', () => {
it('should render with underline text decoration for accessibility', () => {
const { container } = render(<ThemedA href="https://example.com">Test Link</ThemedA>);

const link = container.querySelector('a');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
});

it('should render with underline in dark theme', () => {
const { container } = render(
<ThemeProvider theme={convert(themes.dark)}>
<A href="https://example.com">Test Link</A>
</ThemeProvider>
);

const link = container.querySelector('a');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
});

it('should not underline anchor position markers (a.anchor)', () => {
const { container } = render(
<ThemedA href="#heading" className="anchor">
Anchor Link
</ThemedA>
);

const link = container.querySelector('a.anchor');
expect(link).toBeTruthy();

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).not.toContain('underline');
});

it('should render with correct color and styling', () => {
const { container } = render(<ThemedA href="https://example.com">Link Text</ThemedA>);

const link = container.querySelector('a');
expect(link).toBeTruthy();
expect(link?.textContent).toBe('Link Text');

const styles = window.getComputedStyle(link!);
expect(styles.textDecoration).toContain('underline');
expect(styles.fontSize).toBe('inherit');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ export const A = styled(Link)(withReset, ({ theme }) => ({
lineHeight: '24px',

color: theme.color.secondary,
textDecoration: 'none',
// Ensure WCAG Level A compliance (SC 1.4.1), see https://www.w3.org/WAI/WCAG22/Techniques/failures/F73
textDecoration: 'underline',
textDecorationThickness: '0.03125rem',
textUnderlineOffset: '0.11em',
'&.absent': {
color: '#cc0000',
},
Expand All @@ -21,5 +24,9 @@ export const A = styled(Link)(withReset, ({ theme }) => ({
top: 0,
left: 0,
bottom: 0,
textDecoration: 'none',
},
'&.anchor:hover, &.anchor:focus': {
textDecoration: 'underline',
},
}));
Loading
Loading