Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import styled from '@emotion/styled';
import { css } from '@codecademy/gamut-styles';

export const KeyboardKey = styled.kbd(
css({
mx: 2 as any,
fontFamily: 'monospace',
fontSize: '0.75rem' as any,
bg: 'background-selected',
border: 1,
borderColor: 'border-tertiary',
borderRadius: 'sm',
px: 4,
py: 2 as any,
boxShadow: 'inset 0 -1px 0 rgba(0, 0, 0, 0.2)',
})
);
1 change: 1 addition & 0 deletions packages/styleguide/.storybook/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './ImageWrapper';
export * from './TokenTable';
export * from './Headers';
export * from './Elements/Callout';
export * from './Elements/KeyboardKey';
export * from './Elements/Markdown';
export * from './Elements/ImageGallery';
export * from './Scales/ColorScale';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';

import { ComponentHeader } from '~styleguide/blocks';
import { ComponentHeader, KeyboardKey } from '~styleguide/blocks';

import * as SkipToContentStories from './SkipToContent.stories';

Expand All @@ -21,11 +21,10 @@ export const parameters = {

## Usage

Use `<SkipToContent />` to add a hidden-by-default link that becomes visible when the user <kbd>Tab</kbd>s over it (gives it focus).
Use `<SkipToContent />` to add a hidden-by-default link that becomes visible when the user <KeyboardKey>Tab</KeyboardKey>s over it (gives it focus).
It takes in the ID of the element it will scroll to and focus ([dev.to article explaining why](https://dev.to/robdodson/managing-focus-64l)) on click (via mouse or keyboard).

This type of control is necessary for pages that have a bunch of stuff keyboard users would have to Tab through to get
to the page's main content (like navigations).
This type of control is necessary for pages that have a bunch of stuff keyboard users would have to <KeyboardKey>Tab</KeyboardKey> through to get to the page's main content (like navigations).

For this element to work properly, there must be a `<SkipToContentTarget />` component with the id passed into this component for the `contentId` prop. The `<SkipToContentTarget />` component by default has a `tabIndex` of -1.
Example:
Expand All @@ -43,7 +42,7 @@ Example:
</div>
```

To see it, keep tabbing around on this page or visit [the catalog](https://www.codecademy.com/catalog/language/javascript) and navigate via tab to see it in action.
To see it, keep pressing <KeyboardKey>Tab</KeyboardKey> around on this page or visit [the catalog](https://www.codecademy.com/catalog/language/javascript) and navigate via <KeyboardKey>Tab</KeyboardKey> to see it in action.

## Playground

Expand Down
4 changes: 2 additions & 2 deletions packages/styleguide/src/lib/Molecules/Flyout/Flyout.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';

import { ComponentHeader, LinkTo } from '~styleguide/blocks';
import { ComponentHeader, KeyboardKey, LinkTo } from '~styleguide/blocks';

import * as FlyoutStories from './Flyout.stories';

Expand All @@ -25,7 +25,7 @@ export const parameters = {

## Usage

On button click, a container animates in from the left or right side of the window. Flyout can be configured to close by pressing the “X” close icon, pressing they “esc” key, and/or clicking outside of the container.
On button click, a container animates in from the left or right side of the window. Flyout can be configured to close by pressing the "X" close icon, pressing the <KeyboardKey>Esc</KeyboardKey> key, and/or clicking outside of the container.

Internally, Flyout is a combination of <LinkTo id="Molecules/Modals/Overlay">Overlay</LinkTo> and <LinkTo id="Atoms/Drawer">Drawer</LinkTo>.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';

import { ComponentHeader } from '~styleguide/blocks';
import { ComponentHeader, KeyboardKey } from '~styleguide/blocks';

import * as ModalStories from './Modal.stories';

Expand Down Expand Up @@ -77,7 +77,7 @@ If the close button is hidden, you will need to create a custom way to close the
Other than creating buttons that can close the Modal, there are other ways a Modal can be closed by the user.

1. Clicking the content outside of the Modal
2. Clicking the escape key on their keyboard
2. Pressing the <KeyboardKey>Esc</KeyboardKey> key on their keyboard

All of these methods default to `true` for accessibility reasons, and rely on setting the required `onRequestClose` prop and making sure it toggles the `isOpen` prop on the Modal.

Expand Down
4 changes: 2 additions & 2 deletions packages/styleguide/src/lib/Molecules/Tabs/Tabs.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';

import { ComponentHeader, LinkTo } from '~styleguide/blocks';
import { ComponentHeader, KeyboardKey, LinkTo } from '~styleguide/blocks';

import * as TabStories from './Tabs.stories';

Expand Down Expand Up @@ -67,7 +67,7 @@ It is recommended to add a unique `aria-label` to the `TabNav` component, to dif

### Tabs with interactive content

Normally, when you navigate to the contents of a `TabPanel` using the <kbd>tab</kbd> key or arrow keys, the entire section of the `TabPanel` will be in focus.
Normally, when you navigate to the contents of a `TabPanel` using the <KeyboardKey>Tab</KeyboardKey> key or arrow keys, the entire section of the `TabPanel` will be in focus.

However, if you have tabs with interactive content within them (such as a button or link), and tab into the `TabPanel`, the focus will be on the interactive content and not the entirety of the `TabPanel`.

Expand Down
18 changes: 9 additions & 9 deletions packages/styleguide/src/lib/Molecules/Tips/InfoTip/InfoTip.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Canvas, Controls, Meta } from '@storybook/blocks';

import { ComponentHeader } from '~styleguide/blocks';
import { ComponentHeader, KeyboardKey } from '~styleguide/blocks';

import * as InfoTipStories from './InfoTip.stories';

Expand All @@ -25,7 +25,7 @@ export const parameters = {

## Usage

A tip is triggered by clicking on an information icon button and can be closed by clicking outside, pressing <kbd>Esc</kbd>, or clicking the info button again.
A tip is triggered by clicking on an information icon button and can be closed by clicking outside, pressing <KeyboardKey>Esc</KeyboardKey>, or clicking the info button again.

Use an infotip to provide additional info about a nearby element or content.

Expand Down Expand Up @@ -65,20 +65,20 @@ Links or buttons within InfoTips should be used sparingly and only when the info

When using `placement="floating"`, InfoTips implements focus management for easier navigation:

- **Tab**: Navigate forward through focusable elements (links, buttons) inside the tip. When reaching the last element, wraps back to the InfoTip button for convenience
- **Shift+Tab**: Navigate backward naturally through the page
- **Escape**: Closes the tip and returns focus to the InfoTip button
- **<KeyboardKey>Tab</KeyboardKey>**: Navigate forward through focusable elements (links, buttons) inside the tip. When reaching the last element, wraps back to the InfoTip button for convenience
- **<KeyboardKey>Shift</KeyboardKey>+<KeyboardKey>Tab</KeyboardKey>**: Navigate backward naturally through the page
- **<KeyboardKey>Esc</KeyboardKey>**: Closes the tip and returns focus to the InfoTip button

<Canvas of={InfoTipStories.KeyboardNavigation} />

### InfoTips with Modals and Dialogs

InfoTips have intelligent Escape key handling that works correctly both inside and outside Modals:

- **InfoTips close on Escape** regardless of where keyboard focus is, and focus returns to the InfoTip button
- **InfoTips inside Modals close first** - When an InfoTip is placed inside a Modal, pressing Escape closes the InfoTip while keeping the Modal open
- **Layered navigation** - After closing an InfoTip inside a Modal, pressing Escape again closes the Modal
- **Modals take priority over external InfoTips** - InfoTips detect when `dialog[open]`, `role="dialog"`, or `role="alertdialog"` elements are present and defer Escape handling to them when the InfoTip is outside the Modal
- **InfoTips close on <KeyboardKey>Esc</KeyboardKey>** regardless of where keyboard focus is, and focus returns to the InfoTip button
- **InfoTips inside Modals close first** - When an InfoTip is placed inside a Modal, pressing <KeyboardKey>Esc</KeyboardKey> closes the InfoTip while keeping the Modal open
- **Layered navigation** - After closing an InfoTip inside a Modal, pressing <KeyboardKey>Esc</KeyboardKey> again closes the Modal
- **Modals take priority over external InfoTips** - InfoTips detect when `dialog[open]`, `role="dialog"`, or `role="alertdialog"` elements are present and defer <KeyboardKey>Esc</KeyboardKey> handling to them when the InfoTip is outside the Modal

<Canvas of={InfoTipStories.InfoTipInsideModal} />

Expand Down
Loading