Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"npm-run-all": "4.1.5",
"postcss": "8.5.6",
"prettier": "3.8.1",
"react-docgen-typescript": "^2.4.0",
"rollup-plugin-jsx-remove-attributes": "^3.1.2",
"sass": "^1.97.3",
"start-server-and-test": "2.1.3",
Expand Down
20 changes: 20 additions & 0 deletions src/components/SkipNav/SkipNav.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react';
import { SkipNav } from '~/src/index';

const meta: Meta<typeof SkipNav> = {
title: 'Components (Draft)/Skip Navigation',
tags: ['autodocs'],
component: SkipNav,
argTypes: {}
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: properties => <SkipNav {...properties} />,
args: {
href: '#main'
}
};
21 changes: 21 additions & 0 deletions src/components/SkipNav/SkipNav.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import SkipNav from './SkipNav';

describe('<SkipNav />', () => {
it('renders the default link', () => {
render(<SkipNav />);
const link = screen.getByRole('link', { name: /skip to main content/i });
expect(link).toHaveAttribute('href', '#main');
expect(link).toHaveClass('skip-nav__link');
});

it('receives focus when tabbing', async () => {
const user = userEvent.setup();
render(<SkipNav />);
await user.tab();
const link = screen.getByRole('link', { name: /skip to main content/i });
expect(link).toHaveFocus();
});
});
26 changes: 20 additions & 6 deletions src/components/SkipNav/SkipNav.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import type { JSXElement } from '../../types/jsxElement';
import './SkipNav.scss';
import { Link } from '~/src';

export interface SkipNavProperties {
href?: string;
}

/**
* A SkipNav (skip navigation) link is an accessibility feature, usually hidden
* until focused, placed at the top of a webpage to allow keyboard and screen
* reader users to bypass repetitive navigation menus and jump directly to the
* main content. It acts as an anchor link, enhancing user experience and
* efficiency.
*/
export default function SkipNav({
href = '#main',
text = 'Skip to main content'
}): JSXElement {
href = '#main'

@flacoman91 flacoman91 Apr 22, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i tried to implement this skipnav button in CE and noticed some issues and was unable to use it.
GHE/ConsumerResponse/explorer-app/blob/main/frontend-react/src/components/Common/Buttons/SkipNavButton/SkipNavButton.js

When we specify an anchor tag, clicking the button forces a reload of the page.

In the button in CE we do this for the onClick handler:

    onClick={() => {
        // remove focus on button so main gets new focus and user can tab
        document.activeElement.blur();
        const el = document.getElementById('main');
        if (el) {
          el.scrollIntoView();
          el.focus();
        }
      }}
   

This way users won't see a reload, and you can specify where the page should scroll to.

I think if we just specify the ID or tag to scroll to, that would solve the issue that @anselmbradford identified where skip to can be used in multiple places on the page.

}: SkipNavProperties): JSXElement {
return (
<div className='skip-nav'>
<a className='skip-nav__link a-btn' href={href}>
{text}
</a>
<Link
href={href}
label={'Skip to main content'}
className={'skip-nav__link'}
asButton
/>
</div>
);
}
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,7 @@ __metadata:
postcss: "npm:8.5.6"
prettier: "npm:3.8.1"
react: "npm:19.2.4"
react-docgen-typescript: "npm:^2.4.0"
react-dom: "npm:19.2.4"
react-router-dom: "npm:^7.13.0"
react-select: "npm:^5.10.2"
Expand Down Expand Up @@ -9062,7 +9063,7 @@ __metadata:
languageName: node
linkType: hard

"react-docgen-typescript@npm:^2.2.2":
"react-docgen-typescript@npm:^2.2.2, react-docgen-typescript@npm:^2.4.0":
version: 2.4.0
resolution: "react-docgen-typescript@npm:2.4.0"
peerDependencies:
Expand Down