# 5. Semantic Header Support

Date: 2020-10-19

## Status

Accepted

## Context

Screen-readers rely on DOM-level semantic header tags to provide low-vision users with shorcuts to move around large pages. An example of this is VoiceOver's "navigation rotor".

If screens are built without a sane hierarchy of `h1` ... `h6` tags in the DOM, these shortcuts are either not available to users of screen-readers or they have difficulty making sense of the page's information architecture.

Currently, most of our designs do have clear sections with clear headers visually associated with them, as a result of the design team's consideration of information architecture in their work. However the patchwork components currently used to implement them may or may not map well to the correct DOM header level, or even to a header element at all.

Further, it takes knowledge of the context in which a patchwork component is placed to assign it a semantic level, since these semantics have to do with the structure of the full screen.

## Principles

** No leaky DOM abstractions **: In keeping with Patchwork's design goal to provide a "UI Toolkit" abstraction over DOM and native elements, we want the "idea of semantics" to be a part of the API without making users think about specific underlying DOM elements.

** Separating semantics from presentation **: Which patchwork component to use where flows largely from Figma designs; these are mainly concerned with presentation and behaviour. Semantic level structure for applications can be less obvious than it is for static documents. We should afford users of Patchwork the power to adjust semantic level structure independently of presentation if needed.

** Sane defaults **: That said, components which our team members commonly use in a structural way (e.g. `SectionHeader`, `DocumentTitle`, etc.) should have default semantic levels such that a naive implementation of a screen has a good chance of providing a reasonable semantic page structure for screen readers without further intervention.

## Prior Art

As of this writing, an attempt has been made to provide semantic flexibility to our Head1...Head5 components via separate control over the font size: e.g.

```tsx
<Head1 fontSize="f0">Semantically important, aesthetically small</Head1>
<Head5 fontSize="f7">Semantically unimportant, aesthetically large</Head5>
```

The main issue with this approach is that it doesn't work with the several other components that are often used for header-type elements in our designs, like `<SectionHeader>`, `<PageTitle>` etc.

In addition, `<SectionHeader>` just renders a `p` element, which doesn't provide any semantic hierarchy at all.

## Proposal

I propose that we fix these issues by doing two things.

1. Ensure sensible defaults for components that are commonly used as headers. In particular, the following:

- `PageTitle`: `h1`
- `SectionHeader`: `h2`
- `Head1..n`: as they are now (`h1`...`n`)

2. Define a `HeaderLevel` mixin that can be applied to all the following rendering components to make then act as semantic headers in cases where their default semantic level needs to be overridden.

```jsx
type HeaderLevelProps = {
  headerLevel?: 1 | 2 | 3 | 4 | 5 | 6,
};
```

This mixin will be applied to the following components:

- `PageTitle`
- `SectionHeader`
- `Text`

If the `headerLevel` prop is omitted, we will render the default DOM element for that component:

- `PageTitle`: `h1`
- `SectionHeader`: `h2`
- `Text`: `p`

Otherwise, the component will render a header of the specified level.

### Hidden Headers

What about designs where parts of the page that don't have obvious header components? You may be tempted to add a 'hidden' header to serve as a navigational aid. This is generally discouraged, since hidden headers will drift from visible UI over time. It may be better in these cases to define a `<section title="Description">` for use with a screen reader's "Landmarks" menu instead.

Alternately, the presence of such "headerless UI" may indicate an information architecture bug to be discussed with your designer.

In extreme cases, you can use the exising `hideAccessibly` patchwork utility to define screen-reader-only text.

## Consequences

Choosing sensible defaults may improve many of our pages's navigability out of the gate. We may have to use the mixin prop to fine tune structure on screens where the defaults don't compose together into a structure that makes sense.
