# Display modes
A **mode** is a way the application looks for whoever turns it on. A mode only controls what is shown. What a user
may do is still decided by their scopes (mmo.oauth), and the server checks every scope again.
| Kind | How many | Examples | Effect |
|---|---|---|---|
| **level** | one at a time, ordered by `rank` | `simple` (0), `standard` (1), `expert` (2) | content that asks for a level is shown from that level up |
| **layer** | any number, drawn over the level | `dev` (mmo.admin), `writer`, `translator`… | tools and cues |
`MMO\client\Modes` (PHP) and `MMO.modes` (JS). The helper is `\MMO\client\mode()`; it is not called `modes()`, because PHP
function names ignore case and the configuration would mistake a `modes()` function for the class `Modes`.
## Declaring modes
- **Levels**: `{"MMO\\client\\Modes": {"levels": ["simple", "standard", "expert"]}}`. The default is `["standard"]`: one
level gives no choice, so the switcher shows none.
- **A layer, in the configuration**: `"modes": {"writer": {"kind": "layer", "label": "Writer", "icon": "pen", "scope": "modes.writer"}}`.
- **A layer, in code**: listen to `MMO\client\events\ModesList` and add a `MMO\client\classes\Mode`. This is what
mmo.admin's `dev` layer does.
- **A mode with a `scope`** is offered only to users who hold that scope. mmo.oauth answers `ModeAccess`, and lists the
scope in the roles editor.
## Which modes are shown
For the level, and separately for the layers, the first source that says something wins:
1. `?modes=simple+dev` in the address, kept for the session. `?modes=` forgets it. Use it to look at a page as
someone else would.
2. What the user saved: `config.modes`, through mmo.oauth.
3. What the user's roles give by default: `Role::$modes`, for example `{"layers": ["dev"]}`.
4. The `mmo-modes` cookie: an application without users saves the choice there. It is set on the site's url as the
browser sees it (`siteurl`, `MMO.modes.cookiePath()`), not on the folder of `index.php`, which differs behind a
rewrite: a cookie on that folder never came back.
5. `Modes::$default`.
## What takes part
| Where | Mark | Effect |
|---|---|---|
| `index.json`, folder or file | `"level": "expert"`, `"modes": ["dev"]` (or `"!dev"`) | removed from the menu only: the address still opens the page, and its scopes still apply |
| markup | `data-level="expert"`, `data-mode="dev"` (or `!dev`) | hidden by a stylesheet the server writes into the page, so nothing flashes before the scripts run |
| PHP | `\MMO\client\mode()->has('dev')`, `->atLeast('expert')`, `->matches($level, $modes)` | for what is costly or sensitive |
| JS | `MMO.modes.has()`, `.atLeast()`, `.set({level, layers})`, `.toggle('dev')`, `.forget()`, `.on('change')` | a change saves, then reloads the page |
| shell | `` | a shadow DOM can read these attributes |
| guided tours | `"level": "standard"`, `"levels": ["simple"]` on a tour or a step | the tours and steps of the level shown only ([mcp/Guide.md](mcp/Guide.md)) |
Where the user chooses:
- the account card: the level as a segmented control, and a switch per layer;
- the command palette;
- a layer's `shortcut`;
- the top bar: a chip per active layer, which turns it off.
## Related pieces, built for the layers
- **`MMO.overlay`** (`src/js/core/Overlay.js`): pins notes on elements. A target is a CSS selector, with ` >>> ` to go
into a shadow root. `MMO.overlay.pick()` lets the user point at an element and returns a selector that finds it again.
- **Notification centre** (`MMO\client\Notifications`, `NotificationController`, ``):
- it keeps each user's notifications until they are read: JSON files under `{db}/notifications`, or a store of your
own implementing `iNotificationStore`;
- send one with `\MMO\client\notify($uids, $title, ['kind', 'body', 'url', 'icon'])`;
- other channels (email, chat) listen to `NotificationSent`;
- the bell appears in the top bar once the application declares the controller.