Skip to content

Commit

Permalink
feat(SkipContent): add new component to skip large contents when usin…
Browse files Browse the repository at this point in the history
…g tab key (#1981)

Co-authored-by: Anders <anderslangseth@gmail.com>

Add SkipContent.Return
  • Loading branch information
tujoworker committed Feb 23, 2023
1 parent 03e7680 commit 9607418
Show file tree
Hide file tree
Showing 26 changed files with 1,528 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## February, 20. 2023

- [New major version 10](/uilib/about-the-lib/releases/eufemia/v10-info/)
- New components released:
- [SkipContent](/uilib/components/skip-content)

## November, 22. 2022

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,32 +98,20 @@ Responsive components and layout design should be planned from the beginning and

## Focus management

### Where should the focus start on the first page init?

If there is a Skip-Link then this should focus first since the point of having it is to avoid having to go through blocks of material that are repeated on multiple pages.
The order in which tabbed focus occurs is up to the UX designer if the Skip-Link link is bypassed. For example:
Tab 1: Skip-Link
Enter: Focus is applied to the main content

There are no fixed rules for the order of what becomes focusable after the Skip-Link link. It can be based on context for example:
There are several situations, where we need to help the user to overcome focus management. The two most important are:

**If on index**
- If your page contains large parts of content (tables, lists etc.), you need to give the user, when using a keyboard for navigation, the ability to skip these parts – similar to a Skip-Link. Use the [SkipContent](/uilib/components/skip-content) component to handle such situations.
- If you open a context menu or something similar, you need to set focus actively via JavaScript in order to let the user continue the correct tab placement and order. You may consider if this [focus-helper](/uilib/usage/accessibility/focus/#focus-helper) could help you out with your focus management.

- Tab 1: Skip-Link
- Tab 2: First link in the main menu or main menu button or search field

**If not on index**

- Tab 1: Skip-Link
- Tab 2: Logo with link to the homepage
- Tab 3: First link in the main menu or main menu button or search field
### Where should the focus start on the first page init?

Or, it can be based on user needs. For example, it may be more beneficial for a search field to be in focus rather than the main menu.
A Skip-Link is typical placed as the very first focusable element.
It helps people using keyboard navigation, skipping header areas with many focusable elements. And lets the user directly navigate to the main content.

### Is a Skip Link required?

Again, this is dependent on both content, context, and user needs which ultimately affect the user experience. If there are blocks of repeating content on multiple pages then it may improve user experience to add a Skip-Link.
It is not a requirement to have a Skip-Link but an asset.
It depends on content, context, and user needs. If your page has blocks of content, which are identical throughout many pages, then you should give the user the ability to skip these blocks.
It is not a requirement to have a Skip-Link but a needed assesity.

## Accessibility checklist for designers

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: 'SkipContent'
description: 'SkipContent gives users – using their keyboard for navigation – the option to skip over content which contains a large amount of interactive elements.'
status: 'new'
showTabs: true
---

import SkipContentInfo from 'Docs/uilib/components/skip-content/info'
import SkipContentDemos from 'Docs/uilib/components/skip-content/demos'

<SkipContentInfo />
<SkipContentDemos />
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* UI lib Component Example
*
*/

import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox'
import {
SkipContent,
Button,
Table,
Td,
Tr,
Th,
Checkbox,
Input,
Section,
H4,
Dl,
Dd,
Dt,
} from '@dnb/eufemia/src'

export const SkipContentTable = () => (
<ComponentBox hideCode scope={{ LargeTableWithInteractiveElements }}>
<section aria-labelledby="table-with-caption heading">
<H4 id="heading" space={0}>
This table has many focusable elements
</H4>

<SkipContent selector="#submit-area" text="Skip table content" top />

<LargeTableWithInteractiveElements id="table-with-caption" />
</section>

<Section id="submit-area" spacing="small" style_type="divider" top>
<SkipContent.Return selector="#submit-area" bottom>
Back to beginning of table
</SkipContent.Return>

<Button>Submit</Button>
</Section>
</ComponentBox>
)

const LargeTableWithInteractiveElements = (props) => {
const TdCheckbox = () => {
return <Checkbox label="Select row" label_sr_only />
}
const TdInput = () => {
return <Input label="Label" label_sr_only size={4} />
}

const Row = ({ nr }) => {
return (
<Tr>
<Td>
<TdCheckbox />
</Td>
<Td>Row {nr}</Td>
<Td spacing="horizontal">
<TdInput />
</Td>
<Td align="right">Row {nr}</Td>

<Td.AccordionContent>
<Section top spacing>
<Dl>
<Dt>Favorittfarge</Dt>
<Dd>Grønn</Dd>
<Dt>Favorittmat</Dt>
<Dd>Taco</Dd>
</Dl>
</Section>
</Td.AccordionContent>
</Tr>
)
}

const Rows = []
for (let i = 0, l = 10; i < l; i++) {
Rows.push(<Row key={i} nr={String(i + 1)} />)
}

return (
<Table.ScrollView top>
<Table accordion border outline size="medium" {...props}>
<caption className="dnb-sr-only">A Table Caption</caption>

<thead>
<Tr>
<Th>Column A</Th>
<Th>Column B</Th>
<Th>Column C</Th>
<Th align="right">Column D</Th>
</Tr>
</thead>

<tbody>{Rows}</tbody>
</Table>
</Table.ScrollView>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
showTabs: true
---

import {
SkipContentTable,
} from 'Docs/uilib/components/skip-content/Examples'

## Demos

### SkipContent with section

<SkipContentTable />
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
showTabs: true
---

import { Button } from '@dnb/eufemia/src'

## Description

`SkipContent` gives users – using their keyboard for navigation – the option to skip over content which contains a large amount of interactive elements.

**When is it desired?**

Typical when an action button, such as a save button, is placed below the content.

**What are interactive elements?**

- Text links/Anchors
- Buttons
- Inputs and other form elements
- Basically every focusable element

**What is considered as large contents?**

- Tables with interactive elements
- Lists with interactive elements
- Articles with interactive elements
- Parts of a form

**How does it work?**

1. An initially hidden button will reveal when `tab` key is used.
2. The user can then press this button, or continue tabbing when desired.
3. When the user decides to continue using the `tab` key, the button will disappear again.
4. When the button gets pressed, the focus will be set to another defined HTML class selector and the browser will scroll to the element.

**Good description**

The revealing button needs a clear message to let the user easily understand the intention.

### Placement

Ensure you put a header or a section before the `SkipContent` component. It should describe the content, so the user understands the context.

- Example with a section landmark (section) and header + `SkipContent.Return`:

```jsx
<section aria-labelledby="heading-id">
<H2 id="heading-id">Description of table</H2>

<SkipContent
selector="#my-selector"
text="Skip table content"
/>

<Table aria-labelledby="heading-id" />

</section>

<section id="my-selector" aria-label="Submit">
<SubmitForm />
</section>
```

- Example using a section landmark (section) and table caption:

```jsx
<section aria-labelledby="table-id">
<SkipContent selector=".my-selector">Skip table content</SkipContent>

<Table id="table-id">
<caption>Description of table</caption>
</Table>

<div className="my-selector">
<SkipContent.Return
selector=".my-selector" // same as SkipContent
text="Back to beginning of table"
/>

<SubmitForm />
</div>
</section>
```

### Return button

Optionally, you should consider to include the `SkipContent.Return` utility as well. It lets the user jump back to where they came from (before the large content). This button is only focusable when the enter action by the skip button was done.

### Screen readers and landmarks

The `SkipContent` helper component is mainly dedicated to keyboard navigation.

In order to let screen readers skip large parts of content, you need to ensure your HTML has [logical landmarks and regions](/uilib/usage/accessibility/checklist/#landmark--and-semantics-example).
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
showTabs: true
---

## Properties

The following properties applies to `SkipContent.Return` as well.

| Properties | Description |
| -------------------- | ------------------------------------------------------------------------------------------------- |
| `selector` | _(required)_ Define an existing HTML element selector to focus when the inner button got pressed. |
| `text` or `children` | _(required)_ Define a clear message describing the choices the users has. |
| `focusDelay` | _(optional)_ Defines the delay after the enter key has been pressed. |
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,12 @@ More info about that function in the [focus section about better accessibility](
```js
import { applyPageFocus } from '@dnb/eufemia/shared/helpers'

applyPageFocus(key*: Number, callback*: Function)
applyPageFocus(selector*: String, callback*: Function)
```

#### \* Optional values (defaults)

- key = _'default'_
- selector = _'default'_ (can be a HTML element selector, starting with a `.` or `#`)
- callback = _null_

### setPageFocusElement
Expand All @@ -224,7 +224,7 @@ More info about that function in the [focus section about better accessibility](
```js
import { setPageFocusElement } from '@dnb/eufemia/shared/helpers'

setPageFocusElement(selectorOrElement, key*: String) // returns Void
setPageFocusElement(selectorOrElement: String|HTMLElement, key*: String) // returns Void
```

#### \* Optional values (defaults)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Eufemia includes a range of tools to help you make better accessible application
- [Heading](!/uilib/components/heading) handles heading leveling automatically.
- [GlobalStatus](!/uilib/components/global-status) includes grouping of form status messages and live announcements for screen readers.
- [FormRow](!/uilib/components/form-row) includes `<fieldset>` and `<legend>`.
- [VisuallyHidden](!/uilib/components/visually-hidden) hides text visually, while makes it available for screen readers. Its based on the helper HTML class `dnb-sr-only`.
- [SkipContent](!/uilib/components/skip-content) similar to a skip link. It allows a user, while tabbing, to skip large parts of content, to reach quickly a save button etc.

All form components includes a `label` property to bind automatically the FormLabel to the components (HTML element).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ description: 'Accessibility checklist to help you remember the most important ta
- [ ] Group form elements inside `<fieldset />` and `<legend />`. The [FormRow](/uilib/components/form-row) is doing this by default.
- [ ] Do never expose a form element as `disabled` to the user. Use good UX instead.
- [ ] Have a [Skip Link](/uilib/usage/accessibility/focus#skip-link) in place if the user has to tab many times to reach the main content.
- [ ] Use the [SkipContent](/uilib/components/skip-content/) helper to let the user skip large parts of content, while using keyboard navigation.
- [ ] Make good use of [`aria-label`](/uilib/usage/accessibility/screenreader#usage-of-aria-label-aria-labelledby-and-aria-describedby) and `aria-hidden`, e.g. of [decorative content](/uilib/usage/accessibility/icons#decorative-icons).
- [ ] Make [images and illustrations](/uilib/usage/accessibility/screenreader#images-and-illustrations) accessible.
- [ ] Have `aria-live` in place for dynamic content, like updates coming from the server.
Expand All @@ -22,7 +23,7 @@ description: 'Accessibility checklist to help you remember the most important ta

## Viewport

Allow zooming in web pages, especially important on touch and mobile devices.
Allow zooming in web pages, especially important on touch devices.

```html
<meta
Expand All @@ -38,25 +39,25 @@ Example usage of HTML5 `landmarks` (e.g. `<nav>` or `<section>` etc.):
```html
<body>
<header>Header</header>
<nav>Main navigation</nav>
<nav>Main navigation landmark</nav>

<main>

<section aria-label="I'm now a region">
<section aria-label="I need a label to be a region landmark">
<h1 class="dnb-h--large">h1 styled as h2</h1>
<p class="dnb-o">text</p>
</section>

<article>
<h2 class="dnb-h--xx-large">h2 styled as h1</h2>
<article aria-labelledby="article-1">
<h2 id="article-1" class="dnb-h--xx-large">h2 styled as h1</h2>
<h3 class="dnb-h--medium">h3</h2>
<h4 class="dnb-h--basis">h4</h2>
...
</article>

<article>
<article aria-labelledby="article-2">
<header>I'm not a landmark anymore, because I'm inside article</header>
<h2 class="dnb-h--large">Another article h2</h2>
<h2 id="article-2" class="dnb-h--large">Another article h2</h2>
...
<footer>I'm not a landmark anymore, because I'm inside article</footer>
</article>
Expand All @@ -65,7 +66,7 @@ Example usage of HTML5 `landmarks` (e.g. `<nav>` or `<section>` etc.):

<aside>Aside the main landmark</aside>

<footer>Footer</footer>
<footer>Footer landmark</footer>
</body>
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ This helper also handles both the `tabindex="-1"` and the `class="dnb-no-focus"`

### Focus helper

Set focus on a HTML Element, that exists inside the DOM. It can be any HTML Element, no matter if it's an interactive element or not. No interactive elements will be handled by changing the `tabindex` to 0 alongside a CSS class `dnb-no-focus`, so no blue focus border is visible.

Simple example:

```js
import { applyPageFocus } from '@dnb/eufemia/shared/helpers'

applyPageFocus('.my-selector')
applyPageFocus('#my-id')
```

Asynchronous example:

```js
import {
setPageFocusElement,
Expand Down
Loading

0 comments on commit 9607418

Please sign in to comment.