# Introduction

In this handbook you will find best practices we introduced at [FUN](https://www.fun-mooc.fr) (France Université Numérique); it is mainly focused on technical considerations of FUN's mission, *i.e.* how we code collaboratively, how we communicate with remote workers, etc.

This book is constantly evolving as some practices may need to be refined or introduced. Feel free to contribute by proposing changes, giving us feedback or asking questions about how we work at FUN.


# FUN with Git

[Git](https://git-scm.com) is used as a [DVCS](https://en.wikipedia.org/wiki/Distributed_version_control) for every FUN project involving code or documentation. In this chapter, you will find how we use Git on a daily-basis to code and collaborate with the team.

If you are looking for a good introduction to Git, take a look at the [*Git In Practice*](https://github.com/GitInPractice/GitInPractice#readme) book from Mike McQuaid.

## Git conventions

### Commit granularity

We tend to favor low granularity-but-consistent commits to a long series of commit. If you have multiple commits in a feature branch, it means that you had to address multiple issues to achieve your feature. We can run the test suite on every commit and it should always stay green 😎.

### Commit message

We follow an emoji-driven commit message format adapted from [Angular guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines). A typical commit message should look like:

```
<type>(<scope>) <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```

### Type, scope and subject

Where **type** must be an emoji unicode character (e.g. 🎉 **and not** `:tada:`) chosen from the [gitmoji guide](https://gitmoji.carloscuesta.me/).

And the **scope** should point to the django application or stack component that may be affected, *e.g.*:

* docker
* apps:core
* plugins:foo

The **subject** contains succinct description of the change:

* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end

💩 Bad

```
:sparkles:(auth) add support for HTTP basic auth
✨(Auth.py) add support for HTTP basic auth
✨(auth) add support for HTTP basic auth.
✨(auth) Add support for HTTP basic auth
✨(auth)add support for HTTP basic auth
✨(auth) added support for HTTP basic auth
```

❤️ Good

```
✨(auth) add support for HTTP basic auth
```

#### Body

Just as in the subject, use the imperative, present tense for the **body**: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with the previous behavior.

In other words, you should explain WHY you do what you do, and what was the context that led you to do it the way you do it. It should not be a list of the things you change, because that's what `git diff` is here for.

Try to see the commit message as a "brain dump" of the state of your mind at the moment of committing the changes.

You can also imagine someone angry chasing you in 6 months and asking:

> Why on earth did you do this ??

You surely wouldn't answer with a list of what you did. You would try to explain and justify what you did 6 months ago. The problem is that, at that time, you will have forgotten about all these details... and your changes may be misunderstood. This may lead to tensions and criticism on the quality of your work. Worse, someone may revert an important change for lack of understanding the implications. Ironically, the angry person will most probably be... the future you!

Here is an example of a good versus a bad commit body:

💩 Bad

```
Deactivate the French language in production and activate it in development.
```

❤️ Good

```
Translators are still working on the French language, which will not
be ready before we go live on 2020/09/01. We must hide it in production
so that users don't see it while it is incomplete. However, we need to
keep it activated in the development environment so that the language
selector is displayed to the developper, and also because the demo site
that we use for develoment assumes that french and english are both
activated. The demo site generator would fail if we deactivated French
in development.
```

#### Footer

The **footer** should contain any information about breaking changes and is also the place to reference GitHub/GitLab issues that this commit closes.

Breaking changes should start with the word `BREAKING CHANGE`: with a space or two newlines. The rest of the commit message is then used for this.

### Git `commit-msg` hook

Commits are usually checked in the project CI, but you can install [gitlint](https://jorisroovers.com/gitlint/latest/commit_hooks/#commit-msg-hook) as a git `commit-msg` hook so that gitlint checks your commit messages locally after each commit.

For this, install the `gitlint` package globally using `pip`:

```
pip install gitlint
```

Install the `commit-msg` hook:

```
gitlint install-hook
```

Note that `gitlint` cannot work together with an existing hook. If you already have a `.git/hooks/commit-msg` file in your local repository, `gitlint` will refuse to install the `commit-msg` hook.

`gitlint` should now be runned locally for each commits you make.

If you want to manually trigger `gitlint` for your last commit message, use the following command:

```
gitlint --msg-filename .git/COMMIT_EDITMSG
```

## Git workflows

We use different workflows to handle collaborative coding with Git. The chosen workflow depends on the context and historical reasons. If you need more insights on popular Git worflows, we invite you to read [this section](https://github.com/GitInPractice/GitInPractice/blob/master/14-RecommendedTeamWorkflows.adoc) of the [Git in Practice](https://github.com/GitInPractice/GitInPractice) book.

### Open-source projects

For the sake of simplicity, to ease interaction with the community, we use the [GitHub flow](https://guides.github.com/introduction/flow/index.html) for open-source projects. In a few words:

* the `master` branch is always stable and deployable,
* tags from the master branch are considered as releases,
* contributors have to fork or create a new feature-branch to work on (if they are allowed to in the original repository) and propose a pull request to merge their branch to `master`.

### FUN private projects

Historically, we use [Git Flow](http://nvie.com/posts/a-successful-git-branching-model/) for internal projects. You will find plenty of resources on the web about this workflow, so in a few words:

* the `master` branch is considered as a stable and always deployable branch,
* the `develop` branch is a working branch where developped features are merged,
* contributors have to work on feature-branch that will target the `develop` branch,
* hotfix and release branches are merged to `master` and back-ported to `develop`,
* release branches create tags on the master branch when they are closed.

## Working with forges

### Declaring issues

*For now, new issues are declared in a dedicated Trello board. This is a temporary situation. In the following, we will describe how it should be* 🤓

When declaring a new issue, please describe as much as possible the **purpose** of your issue, and eventually make a **proposal** on how it should be solved or investigated. Choose wisely a label for this issue and please do not assign someone to it (unless you already discussed with her verbally and had an agreement).

### Working with pull requests (PR)

We recommend to create a pull request (PR) or merge request (MR) in GitLab semantic as soon as possible with the `WIP` flag preceding your PR title, *e.g.* `WIP: 😎(docker) add mongo service`.

When your work is done on this PR, remove the `WIP` flag and please ensure that:

* your feature or fix is \*\*tested \*\* (all continuous integration tests should be green and code coverage **should not** decrease),
* your feature is **documented**,
* your branch is **up-to-date** with the target branch (it should be rebased and force-pushed).

Once all of those requirements are met, ask for a review of your code by assigning maintainers to your PR. Your changes should be approved by **at least one contributor of the core team** to be merged.

Last but not least, a code review should not take more than half an hour per PR to be profitable for everyone. It means that you have anticipated the amount of changes required to achieve your work. If those changes are bigger than 500 lines, then you may consider to split your feature in multiple PRs.

Note that the target branch (`develop` or `master`) will be write-protected, *i.e.* no one is allowed to push to it. Hence you will need to use the forge UI to merge your PR once all tests are green and your changes have been approved. Our PR merging strategy is: **rebase and merge** ; we do not want a merge commit.

## Releasing new software version

Whatever the language you are using on a FUN project, cooking a new release (*e.g.* `4.18.1`) should follow a standard procedure described below:

1. Create a new branch named: `release/4.18.1`,
2. Bump the release number in the appropriate file, *i.e.* for python projects, the `setup.cfg` (or `__init__.py`, depending on the way you handle your package version) and/or `package.json` for node-based projects,
3. Update the project's `Changelog` following the [keepachangelog](https://keepachangelog.com/en/0.3.0/) recommandations,
4. Commit your changes with a structured message:
   * add a title including the version of the release and respecting the above described format using the 🔖 release emoji,
   * paste in the body all changes from the changelog concerned by this release, removing only the markdown tags and making sure that lines are shorter than 74 characters.

     ```
     🔖(minor) bump release to 4.18.0

     Added:

     - Implement base CLI commands (list, extract, fetch & push) for supported
     backends
     - Support for ElasticSearch database backend

     Changed:

     - Replace LDP storage backend by FS storage backend
     ```
5. Open a pull or merge request depending on the current forge of the project,
6. Wait for an approval from your peers,
7. Merge your pull or merge request,
8. Checkout and pull changes from the `master` branch,
9. Tag & push your commit: `git tag v4.18.1 && git push origin --tags`
10. Unless the CI has already taken care of it (check on the Circle CI config if there is a release job defined), manually release your version on GitHub.
11. Ensure your release is published on the package index.

### Checking project tags consistency

As we are only Humans, we are error-prone *per se*. To avoid tagging consistency errors, we recommend to integrate the following tests in the project's continuous integration workflow before publishing a new release:

```bash
# Get current tag corresponding commit ID
tag_commit=$(git rev-list -n 1 $CIRCLE_TAG)

# Check that the tag refer to a commit in the $TARGET_BRANCH (e.g.
# the master branch)
git branch -a --contains ${tag_commit} | grep ${TARGET_BRANCH}

# Check that the current tag (vX.Y.Z) matches the release number in
# setup.cfg (X.Y.Z)
grep "$(echo $CIRCLE_TAG | sed 's/^v/version = /')" setup.cfg
```

In this example script `$CIRCLE_TAG` is an environment variable defined by the contious integration platform (CircleCI in this case) with the pushed tag value, and, `$TARGET BRANCH` is the `Git` branch that should have been tagged (e.g. the `master` branch).


# FUN with Python

In this section, we will describe our best practices when dealing with Python code.

## PEP 8

Your code should at least follow the [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide with one single exception: the maximal line length is 99 characters (instead of the default 79 characters).

Usually, we enforce this convention by adding a linting step to our continuous integration workflow using [`flake8`](https://flake8.readthedocs.io/en/latest/) with the following settings in the project's `setup.cfg`:

```
[flake8]
max-line-length = 99
exclude =
    .git,
    .venv,
    venv,
    __pycache__,
    node_modules,
*/migrations/*
```

## Documentation

The minimal documentation your code should have is a docstring (see [PEP 257](https://www.python.org/dev/peps/pep-0257/)) per module, method, class and function. For now we do not force you to use a particular style or describe all method or function arguments; we want you to explain to your peers (and maybe you in a few months) what your code is trying to achieve. Remember that it should be written for humans, while your code is for machines (and humans 🤓).

This also applies for tests, *e.g.*:

```python
class FooTestCase(TestCase):
    """Test the Foo class in condition X"""

    def test_my_method_when_bar_is_None(self):
        """
        Test that my method asserts a FooException when bar is None

        Steps:

        * create a new Foo instance
        * update foo.bar with None
        * call foo.my_method()
        * assert it raises a FooException
        """
        pass
```

## Indentation

We had long discussions about following the PEP 8 (with maximal line length) and having an indentation style that satisfies everyone. Our consensus follows:

Until you hit the 99 characters limit, write your statement in one line:

```python
my_function(foo, bar, baz=None, spam='YUMMY', summary='lorem ipsum', is_fake=True)
```

If your statement is too long, then split your line with one argument per line:

```python
my_function(
    foo,
    bar,
    baz=None,
    spam='YUMMY',
    summary='lorem ipsum',
    is_fake=True,
    verbose=True,
    debug=True,
)
```

**Pro tip**: To fit with the maximal line length limit for long strings, think parentheses!

```python
foo = (
    "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"
    "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud"
    "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure"
    "dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
    "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt"
    "mollit anim id est laborum."
)
```

## Import

Import statements should respect the following requirements:

* write global import statements first (`import logging`) then partial imports (`from copy import copy`),
* import statements should be written in the following order: 1. standard library, 2. Django imports 3. third party dependencies, and 4. application modules (relative imports); with an empty row between each,
* import statements should be sorted alphabetically (`import bar` is written before `import foo`),
* imported objects from a module should also be sorted alphabetically (`from foo import bar, baz, lol`).

An example follows:

```python
"""
This is a docstring for my module

[...]
"""
import logging
import re

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.urls.base import reverse
from django.utils import timezone
from django.utils.translation import ugettext as _
from django.views.generic import (
    DetailView, FormView, ListView, RedirectView, View
)
from django.views.generic.detail import BaseDetailView
from django.views.generic.edit import FormMixin

import pandas

from apps.core.models import Bar, Foo, Lol
from .forms import (
    BarForm, FooExportForm, FooFightersBandRegistrationForm, FooFiltersForm,
    FooFightersLongForm,FooFightersUserShortForm, FooSelectForm
)
from .utils import export_bars, export_foos
```

## Test files

In Django, we want good names for our test files: they should bear the name of the Python module being tested (followed by the snakecased name of the class or method if necessary), from the more generic to the more specific.

Some examples of good names for test files:

```
test_forms_contact.py
test_models_user_send_welcome_email.py
test_forms_fields_datetimerange.py
```


# FUN with Docker

We extensively use Docker at FUN. Mostly for developement, but also in production. In this document, you will find a few guidelines on how we write, run and manage our containers.

## Docker/host user mapping

it is commonly assumed that Docker containers **should not** run commands with a privileged account as the `root` user. So it's a good practice to create and declare a `USER` in your `Dockerfile`. When a docker volume is mounted from the host to a container, you may then encounter permission issues with the container's user trying to create new files on the host volume (*e.g.* when installing dependencies with *npm*), and this is a good thing! But it is a bit annoying as it may break your development workflow.

A workaround to solve this issue is to use the `--user` option of `docker(-compose) run`:

```
$ docker-compose run --rm --user="$(id -u):$(id -g)" node yarn install
```

In the previous example, we force our local user id and primary group id both accessible in a shell context *via* the `id` command. This little trick can also be used in a `Makefile`:

```bash
# Docker
COMPOSE              = docker-compose
COMPOSE_RUN          = $(COMPOSE) run --rm
COMPOSE_RUN_NODE     = $(COMPOSE_RUN) --user="$(id -u):$(id -g)" node

# Node
YARN                 = $(COMPOSE_RUN_NODE) yarn

build-saas: ## build Sass files to CSS
    @$(YARN) sass
.PHONY: build-saas
```


# FUN with Slack

In this section, we will describe our best practices when using Slack.

## Asynchronicity

Slack is used:

* for real-time discussion when two or more persons are actively connected at the same time,
* to replace emails when sharing information or for asynchronous conversation.

When you post a message to someone on Slack, you should not expect them to answer synchronously.

In other words, consider your messages like emails:

* communicate the whole point before pressing "Enter" and not just "hello are you here?" or

  "are you available to talk?",
* if the person answers straightaway, it means that he or she may be available to chat, but you

  should not expect it to be the case by default,
* give the person some time to answer: at least a few hours, unless the matter is unusually

  urgent. They can decide how fast they need to answer you, because you have given them the

  whole information in your message, right?

## Threads

We want technical discussions to happen in public channels as much as possible and not in private/group channels:

* someone else may have a good idea,
* someone else may learn something by reading your discussion.

In order to limit the noise for people not directly interested in your topic, switch to a "thread" as soon as you notice that a conversation is initiating.


# Code reviews at FUN

Code reviews are collaborative steps in the development of our projects. The purpose of code reviews is twofold. They allow both to:

* check the quality of the code and
* to communicate actively on the evolutions.

Code reviews concern all members who are involved in the development team, regardless of their competence level. This concept raises questions about their effectiveness and relevance both on the side of the developer submitting the review and on the side of the developers reviewing the code.

> When ? How? How long do we review codes? Which focus? Which feedback to give to the reviewer?

## Approach of code reviews

Repository managers such as GitHub or GitLab are helpful to review codes. Associated workflows, pull request (PR) or merge request (MR) ease the tracking of information shared in code reviews.

We recommend considering a fine granularity of code reviews. The benefit is both temporal and qualitative. By doing code reviews as frequently as possible, the exercise is made easier and all members feel more involved in development. By doing light and targeted code reviews on a specific topic, the review is done with more attention because it is less complex.

A code review is an opportunity to **actively** communicate about the changes to other developers and get feedback on the quality of the changes. As far as possible, a code review should be done in small changes. In this perspective, we advise against doing code reviews for a collection of changes that can be analyzed independently. However, it is important to keep in mind that what is under review must be understandable in its software context. Some complex refactorings or features can not be split in small changes as they would lose software design information. The reviewer would then fail to comment on design choices and be limited to a simple technical review.

When opening a PR (or MR) for a review, we recommend to assess the granularity of the review to keep visible the algorithm and design of the feature.

## Best practices for reviewing codes

The challenge of a code review is to position oneself in the face of another member's work production. It is not a question of sanctioning his/her work, but of assessing it; issuing an opinion.

Here are 5 good practices to adopt that may be beneficial for a code review.

### Engage conversation for clarification 💬

The point here is to question what has been produced. It is not because the reviewer has difficulty understanding the code that the code quality is bad. Code reviews are an exercise that enhances the value of the work produced in a team spirit. We recommend on asking the developer why he/she did it rather than being prescriptive or tell him/her to code in a specific way.

> I am not sure to understand. What was your point when doing this?

Also, to discuss about the choices made by the developer, we encourage to suggest alternatives that could optimize what has been done.

> Doing it like this could solve the problem in only one step. What do you think of it? (WDYT?)

### Have a pragmatic state of mind ✔️

It is important to keep in mind that what is provided is functional. External parameters can force the developer not to provide optimal code. He/she is not omniscient and learns from team work too. Having a functional code is the paramount issue (considering that good practices of coding are respected).

### Review as often as possible 🕙

Code reviews will be relevant and beneficial if the exercise is frequent within the team. It is not the business of a few members but of each one of them because such exercise contribute to the good integration and involvement of everyone.

### Focus on the main 🧠

A code review should not be overthought. The reviewer should focus on the algorithm and the code. With a problem comes a solution. It must be possible to be synthetic and have a global vision of what is in question.

### Communicate positively and with consideration 😃

Finally, the most important is the way to communicate during a review. We want to keep a warm and team spirit in the perspective of celebrating and encouraging progress in a joyful and motivating mood. In addition to ensuring that positive and enthusiastic comments are always written, don't hesitate on using gifs and emojis in PR (or MR) conversations. 😉

## References

* [On Code Reviews](https://tailordev.fr/blog/2017/02/03/on-code-reviews/)


# Accessibility guide

Table of contents:

* [Colors](#colors)
* [Text alternatives](#text-alternatives)
* [Links](#links)
* [Headings](#headings)
* [DOM elements order](#dom-elements-order)
* [Client side navigation and keyboard management](#client-side-navigation-and-keyboard-management)
* [Forms](#forms)

These are a few accessibility (a11y) related things to keep in mind when touching HTML/CSS/JS code.

Note that these are not "accessibility basics" but things to know to go the extra step when caring about a11y, that are often noticed on our code bases.

Of course not everything is set in stone: there are always exceptions related to each use case, and often multiple ways to solve a11y issues. So, remember this is not about unconditionally following rules. It's about making sure everyone has access to all information on your website, and can interact with it. So, when in doubt: test! Accessibility panels in browser devtools can help as a first checkup, but the best is to actually use screen readers:

* on Windows, you can use [NVDA](https://webaim.org/articles/nvda/) + Firefox for free
* on macOS, you can use [VoiceOver](https://webaim.org/articles/voiceover/) + Safari for free
* on Linux, sadly there is no feature-complete solution, so the best and easiest way to test correctly is to have a [Windows VM](https://developer.microsoft.com/en-us/windows/downloads/virtual-machines/) and use NVDA and Firefox on it.

## Colors

In terms of colors, we should respect RGAA rules: it is understood that a minimum contrast ratio between text color and background color must be respected.

However there is not only this rule to know about colors. We can mention one in particular to pay attention to.

### Contrast of interface components (RGAA criterion 3.3)

When a color used in a UI component is mandatory to perceive in order to understand the component, the color must have a strong enough contrast ratio with its surroundings.

#### Example 1: Interactive component

For example, a border around a form input is usually necessary to understand that it is a form input. Without the border, if the background behind the input is white, and the background of the input itself is white, it's difficult to understand that it is an input that you can interact with.

In this case, the border of the input should have a contrast ratio of at least **3:1** with the background color behind it. If the ratio is lower, some people will not understand that it is a form input, because they will not see the border.

#### Example 2: non-interactive component

Another example: a progress bar. This is a non-interactive element, but it carries information with its shape and colors. There is a background color, and a foreground color that takes up more or less space to indicate the progress.

In this case, if the two colors touch each other and the contrast ratio between the two is less than **3:1**, some people will not see the difference, and therefore will not get the information.

If you don't want to change the colors, you can decide to add a line between the two to delimit them. This line should have a minimum ratio of **3:1** with each of the two colors.

Note that if the progress is described as text in addition to the bar (like, "60%" written next to the bar), it's not mandatory to make the bar colors accessible. Because the information itself is now accessible through the text.

**tl;dr: every color necessary for the use and understanding of a component must be contrasted enough with what it touches**

## Text alternatives

When it comes to textual alternatives for images, there are several things to keep in mind.

### Only images that carry information

We'll want to give an alternative only to images that are not purely decorative.

Examples:

* a link to share the page on Facebook, which consists exclusively of an icon with the Facebook logo, must have a textual alternative.
* a link to share the page on Facebook, which consists of the Facebook logo and a text "Share on Facebook", does not need a textual alternative on the Facebook logo: the information is already here in text next to the logo, we will not want to bother a screen reader user by repeating ourselves
* a non-interactive zone composed of "\[logo representing a building] \[name of establishment]" may need a textual alternative on the logo if we consider that it is necessary to know the context to understand that we are reading the name of an establishment
* a non-interactive area displaying "\[clock logo] Effort: 1 hour" should not have an alternative on the logo, because its meaning is already given by the text "Effort".
* in general, images decorating a content site, like a stock photo of 2 businessmen shaking hands, should not have a textual alternative.

**So there are two steps to know if we define an alternative**

1. first, does my image carry information?
2. then, is this information only available through this image?

If the answer to both is yes, then a textual alternative is required.

### Describe the meaning of the image

The alternative indicates the meaning of the image, not its visual description.

Examples:

* a link with a facebook logo, made to share a page on facebook, will have "Share on Facebook" as an alternative, not "Blue Facebook logo".
* the link in the header of the site with a FUN logo will have "Home - FUN" as a textual alternative, not "FUN logo".
* a non-interactive area composed of "\[logo representing a building] \[name of establishment]" will have a textual alternative on the logo indicating "Establishment", not "Image of a two-story building". This is to give context to the screen reader user that the text coming next is indeed the name of an establishment.

### Technically, how to do this?

* an `img` tag without a textual alternative (because decoration or info already present in text) **must have an empty alt attribute `alt=""`**. :warning: Without this, some screen readers announce the image url.
* an `svg` tag without a textual alternative must have an `aria-hidden="true"` attribute.
* when using an `img` tag, the straightforward way to set up the alternative is to use the `alt` attribute. You can repeat it in the `title` attribute if you wish to have some sort of tooltip. Note that this attribute is generally not announced by a screen reader though. It's only here only for mouse users, if we consider that the image itself deserves a textual precision. That said, in this case, we can also ask the question of displaying text next to it at all times, as keyboard-only or smartphone users will not be able to display the `title` text.
* for an interactive element (`a`, `button`) having an image as their unique child, one can also decide to define the alternative on the `a`/`button` tag via an `aria-label` rather than on the image itself. This can be useful if for some technical reason during development, defining the `alt` is more complicated than adding an attribute on the interactive element.
* for an `svg` tag, you have to define a `role="img"` attribute and add the alternative via an `aria-label` attribute. It is also necessary to repeat the alternative in a `<title>` tag in the svg, for better support of older screen readers.
* It is also OK to define images/svg without alternative and, next to it, have a screen readers-only text (see the `offscreen` class for [example in richie](https://github.com/openfun/richie/blob/917c9fb703ea6082c8762b2fbc043167aed473b2/src/frontend/scss/generic/_accessibility.scss#L4)).

*Commit examples in richie:* [*32006c4b*](https://github.com/openfun/richie/commit/32006c4b1d4c39d65e0b7efe6f18a4e1c5962387)*,* [*9b550601*](https://github.com/openfun/richie/commit/9b5506016301bcc5b9b1773e178dde9238f08c91)*,* [*68065c92*](https://github.com/openfun/richie/commit/68065c9280f475f2abfb12777b4ab113786c591c)

## Links

### Have unique links accross the page

One way to navigate through a screen reader is to jump from link to link. This can be a good way to get an overall view of the content of a page.

That's why it's important that a link has a unique title. If several links in the page have the same text, it is great to make them unique, either by changing the text visible to all, or by adding text intended exclusively for screen readers to specify the context in the link, without impacting the visual.

*Commit examples in richie:* [*906b0271*](https://github.com/openfun/richie/commit/906b0271bc6366f50ca9c7a6110c24584a19ef40)*,* [*8fe1e531*](https://github.com/openfun/richie/commit/8fe1e53112802b198656f20f5e6c6dbeef410570)*,* [*6fc59477*](https://github.com/openfun/richie/commit/6fc59477c8ae71b9066ecf0c7d25cc2893c25a39)

### Avoid links with rich content

Quite often in pages listing resources, we'll want to design a list of large clickable cards, each with the resource's image, title, date, excerpt, etc.

The simplest technical implementation of this kind of block is to have an `a` that surrounds all the content.

However, this makes it difficult for screen reader users to read. Depending on the screen reader, the content of the link will be badly announced because of loss of semantics of the content, or will be hidden behind additional keyboard shortcuts to be triggered.

For this kind of case, it is better to do as is:

* have a normal link on the card title, for keyboard users and screen reader users
* then, if a larger clickable area is desired, manage this additionally, without creating redundancy with the "real" link. This is possible in several ways: by using `aria-hidden` and `tabindex="-1"` attributes, or by handling a custom JS event on a non-interactive element.

*Commit examples in richie:* [*8c702c1a*](https://github.com/openfun/richie/commit/8c702c1ad4d38a78761ad04e0c5a0c9f33fe8496)*,* [*82efb7bb*](https://github.com/openfun/richie/commit/82efb7bbc3e34270d07591844127f5c0677536f0)

## Headings

A good usage of heading tags, from `h1` to `h6`, is essential for a screen reader user to be able to navigate correctly on a site.

### Have a well-structured heading hierarchy

The first important thing is that the sub-section of a section marked with an `h2` must be titled with an `h3`. Not an `h4` or an `h5`. Note that having an `h3` that will visually look like an `h4` via CSS is totally okay. The important thing is to use the correct HTML tag.

This is because many screen reader users deduce the structure of a page via the heading tags. Screen readers have a "page summary" feature that lists the headings hierarchy, to get an overview of a page in a few seconds. If there are gaps in the hierarchy, it will be difficult to understand which sub-part corresponds to which section.

*Commit examples in richie :* [*cea7da9a*](https://github.com/openfun/richie/commit/cea7da9a6c10cd5c7a2998d0183d5615924be866)*,* [*297bfa2b*](https://github.com/openfun/richie/commit/297bfa2b715b8ae8885b7f880cec529b6f7eaf41)

#### The case of modal dialogs

A modal dialog is in its own context. If I open a dialog via a button contained in a section titled by an `h3`, the heading of the dialog has no real interest in being an `h4`. The screen reader user is warned that he ends up in a dialog, he knows he is in a new context. One can start the heading hierarchy with an `h1` in the dialog without accessibility problems. This is useful if you have very structured content in the dialog itself and you have 3 or 4 levels of headings.

*Commit examples in richie:* [*3ac600f8*](https://github.com/openfun/richie/commit/3ac600f80e1e99b0b9bf14a7ecf9a041f797de06)

### A heading gives context

Heading tags are crucial navigation points with a screen reader. That's why it's good practice to take a step back if you start to have a lot of them on a page, or if you quickly get to the `h6` level in your hierarchy.

**We should think of heading tags as sectioning tags that give context and meaning to what follows them.**

When I ask myself *"do I use a `<hx>` tag?"*:

* :x: rather than thinking *"is this important, do I want to render it as a big and bold text?"*,
* :white\_check\_mark: you should be thinking *"does this text mark the beginning of a new section of content and contextualize the things that follow it?"*

Examples:

* I want to display the price of a product. I'm not going to use a heading tag because, while the information is important, it's not there to set the context for a content section that follows it. It's standalone info. Instead, I'm going to make the info stand out visually in CSS and use a `p`, `span`, `strong` or `div` tag (not really important to a screen reader).
* I want to display the contact information of a user. Using a heading tag for the text "Contact information" at the beginning makes sense: this text gives context to what follows. A screen reader user will know that, until the next heading, it is the contact information that will be described.

*Commit examples in richie:* [*3ac600f8*](https://github.com/openfun/richie/commit/3ac600f80e1e99b0b9bf14a7ecf9a041f797de06)

## DOM elements order

The order of elements in the DOM is particularly important for screen reader users.

**A screen reader announces things by following the HTML tree. It does not guess the order of things based on the CSS.**

Uou have to keep this in mind especially when building complex UIs.

### Example: table-like layout

Sometimes, we'll want to build a layout that looks like a table. But the use of a `table` tag might be too technically restrictive, or too exaggerated for our case. So we'll build it ourselves via a `div` and design things the way we want.

By doing this, we risk creating a problem of vocalization order for a screen reader.

Look at the following screenshot.

![layout with a first row showing "start" and "end", a second row showing the start and end dates](/files/fsTxqgXxhuUQnXkdNQl2)

The easiest way to technically implement this UI in HTML would probably be to have the first row as a `div` containing "Début" and "Fin", and the second row as a `div` containing "21 mai 2022" and "23 juil. 2022".

But then screen reader will read this:

:x: Début, Fin, 21 mai 2022, 23 juil. 2022\_

We have to keep this UI and make it announce this:

:white\_check\_mark: *Début, 21 mai 2022, Fin, 23 juil. 2022*

#### How to do it?

The straightforward solution is to completely redo our implementation so that the DOM order matches the UI order.\
However, this may not be the most practical solution: technically complicated, or time-consuming redesign of an existing component.

A fallback solution is to duplicate the data in the DOM, have one part accessible only to screen readers, and another part hidden from screen readers.

1. In this example, we would make the first `div` containg "Début" and "Fin" invisible to screen readers, by setting an `aria-hidden="true"` attribute on it
2. Then we would add a screen reader-only text before each date in the 2nd `div` (via the "offscreen" class in richie, for example): `<span class="offscreen">Début </span> 21 mai 2022`

At the cost of a tiny content duplication in our HTML, we solved our accessibility problem without redoing all the UI from scratch.

*Commit example in richie:* [*2b797365*](https://github.com/openfun/richie/commit/2b7973652f4337081b1c959bdbc42ec240bfa62b)

### Example: data displayed before its heading

As we saw before in this doc, a heading tag is a crucial point in screen reader navigation.

If I display information related to a part marked with a heading, just before this part, a screen reader user has a great risk to completely miss this information. Or to believe that this information is related to the previous part.

Here again, you have to think about the DOM order: **even if I want to visually display something before my heading, the information must be described in the DOM&#x20;*****after*****&#x20;that heading**.

#### How to do it?

To solve these cases quickly, consider using **flexbox and the `order` property**: this is often the most efficient solution.

*Commit examples in richie:* [*b02600a7*](https://github.com/openfun/richie/commit/b02600a7a4aebda1551fc3a01b1c6881ca6af836)*,* [*7e512659*](https://github.com/openfun/richie/commit/7e512659911a27366b7e19d5b1172763c20776b4)*,* [*b3ddcf84*](https://github.com/openfun/richie/commit/b3ddcf84bf5acb105cc52d47d2d6ee0ded570b8b)

## Client side navigation and keyboard management

When you have a 100% JS-based navigation between components, you must not forget about keyboard-only users and screen reader users.

For example, you code a system with several steps in the same modal dialog. You move from one step to the next via a button. When you press this button, the content of the dialog is completely replaced.

By default here, this is problematic for a keyboard user or a screen reader user. **Since the button has been removed from the DOM, the keyboard focus has automatically returned to the body**. The screen reader will resume reading from the top of the page!

We have to put the keyboard focus back on the dialog itself, or, better in our example, on the name of the new step, which is certainly displayed at the top of the dialog.

* The HTML attribute `tabindex` is useful. Defining a `tabindex="-1"` on an element allows it to be programmatically focusable by your code, without adding it to the tab order. Useful in those cases where you only want to redirect the screen reader user to a specific spot at a specific time.
* in React, use `useRef`, `useEffect`, or even `MutationObserver` depending on the case, to manage the focus when mounting/unmounting components. The currently focused element in the page is referenced in `document.activeElement`.
* in a case where you don't need to specifically handle focus, but you want to warn the user of a change, don't hesitate to look at the `aria-live="polite"` attribute and potentially `aria-atomic`.

:warning: This is one of the most forgotten points in accessibility, **but one of the most critical**, because not managing it totally breaks the navigation for most screen reader and keyboard users.

**tl;dr:**, as soon as you have content that replaces another, don't forget to test the keyboard navigation to make sure it is not reset at the body level.

*Commit examples in richie:* [*dcf92ef1*](https://github.com/openfun/richie/commit/dcf92ef160ff4a28e0a6c5fd51e0bb0c6a8f1668)*,* [*cbcfaf35*](https://github.com/openfun/richie/commit/cbcfaf35665c5442bc21e619fdd01049690b61d1)*,* [*b3db0467*](https://github.com/openfun/richie/commit/b3db0467799912f22ad81e0ce0abea3b8216f802)*,* [*7dc7ac16*](https://github.com/openfun/richie/commit/7dc7ac16a6002a79a29a594a19baecc8318b44bc)*,* [*a9a28744*](https://github.com/openfun/richie/commit/a9a2874436fc3e63a7247ef4056d2965d6f22ec1)

## Forms

There are several simple but very important rules to keep in mind when designing forms.

### Always have a label

A form input should always have a label.

* The simplest way is to use the `label` tag with the `for` attribute targeting the `input` id.
* for a single input in a form that is explicit on his own, without the need for a visual label (a search input, or newsletter subscription input, etc.), we will put a label in the `aria-label` attribute.

**The `placeholder` attribute is not a label**. A screen reader will not necessarily announce it. You can definitely use the attribute if you want, but it is absolutely necessary to double the information of the placeholder in the `aria-label` if no label is shown next to the input.

*Commit examples in richie:* [*ba33c494*](https://github.com/openfun/richie/commit/ba33c49442a0afe3f6dd1dca6bb4587556a423e1)

### Technically link information to their inputs

Screen readers activate a "form" mode when stumbling upon forms in a page. Navigation is no longer necessarily done by following the DOM order, but by jumping from input to input.

This means that if we render something like this:

> \[date input] Error: please follow this example: 23/11/1995 \[mail input]

If my error text is not technically linked to my date input, I'll probably miss the info with my screen reader, because it will jump from the date input to the mail input without vocalizing what's between the inputs.

**The `aria-describedby` attribute is a good tool to use here**: I apply it to the `input` by specifying the `id` of the error text. When my screen reader focuses the form input, it will first read the input label, then its description (the error message).

This applies to an error message, a help message, an example of use... any information related to the input.

*Commit examples in richie:* [*6193ddf7*](https://github.com/openfun/richie/commit/6193ddf76a5439610cf271154934c7123d58411d)

### Leave the submit button active

A common practice is to set the submit button on a form to `disabled` until the form is valid.

For a screen reader user, this can potentially be a problem. When something has a `disabled` attribute, it is removed from the accessibility tree. A screen reader will not see it; it will not be announced as a disabled element.

The user may be lost and wonder why there is no submit button, and will have to go focus each input one by one, hoping to find the one that prevents the submission.

So the better thing to do is to let users the possibility to submit the form at any time. **When the user submits an invalid form handled in JavaScript, it is necessary to put the keyboard focus back on the area listing the errors at the top of the form, or on the first form input with an error**. In the same idea as the "client-side routing" part, this redirects the user in the right direction. Thanks to this attempt, the user is no longer lost.

*Commit examples in richie:* [*86e3f31b*](https://github.com/openfun/richie/commit/86e3f31bb4e280b03e1731e91d3e4f933c3440ec)*,* [*a95e1839*](https://github.com/openfun/richie/commit/a95e1839f30b0afc853f811079201ea006dbb6a9)


