---
title: "Composing layouts"
description: "A layout of your own from Clover's parts: the Root, the parts and the property parts, the information panel, panes side by side, parts of your own, languages and renderers."
url: https://clover.iiif.library.northwestern.edu/docs/composing
version: "4"
---

# Composing layouts

Every template is a composition of Clover’s parts, and so is a layout of your own. A `Root` (web components: `<clover-root>`) loads the content and holds where the reader is. Every part inside it reads from the nearest `Root` (web components: `<clover-root>`), wherever it sits in your markup, so a layout is your own HTML with parts placed in it.

## A layout of your own

The parts are named exports. `import * as Clover` gives them dot notation, and they tree-shake: a page loads only the parts it renders. Here the title, position and buttons sit beside the Body. The code is live: move the aside’s parts, or change what a Label reads, and the layout follows.

> **Example**: a live example on the page, showing sample IIIF data. What it shows is demonstration content, not about Clover; its code, where the page gives it, follows.

**React**

JSX

```jsx
import * as Clover from "@nulib/clover";
import "@nulib/clover/styles.css";

<Clover.Root
  iiifContent="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json"
  className="my-layout"
>
  <Clover.Body />
  <aside>
    <Clover.Label as="h3" />
    <Clover.Label of="canvas" />
    <Clover.Position />
    <Clover.Previous />
    <Clover.Next />
  </aside>
</Clover.Root>;
```

**Web components**

HTML

```html
<script type="module">
  import "@nulib/clover/web-components";
</script>

<clover-root
  iiif-content="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json"
  class="my-layout">
  <clover-body></clover-body>
  <aside>
    <clover-label as="h3"></clover-label>
    <clover-label of="canvas"></clover-label>
    <clover-position></clover-position>
    <clover-previous></clover-previous>
    <clover-next></clover-next>
  </aside>
</clover-root>
```

Arrow up or down moves the part, as Move up and Move down do.

## Start from a template

The [templates](https://clover.iiif.library.northwestern.edu/docs/viewer#templates) are built only from these parts, so a template’s source is a good place to start. Copy `Default.tsx` and `Default.css` from `packages/clover/src/templates/default/` (or another template’s folder) into your app, and read `../../parts` in its imports as `@nulib/clover`. Rename its `clover-template-default-*` classes to your own, and change what you like: nothing in a template is private.

To keep the Viewer’s shell, pass your layout as its `template`; it takes the `Root` (web components: `<clover-root>`) props like any template:

**React**

```jsx
import Viewer, * as Clover from "@nulib/clover";
 
function MyLayout(props) {
  return (
    <Clover.Root {...props}>
      <Clover.Body />
      <Clover.Items layout="pager" />
    </Clover.Root>
  );
}
 
<Viewer iiifContent={url} template={MyLayout} />;
```

**Web components**

```html
<!-- A layout of your own is the markup itself: a root and its parts. -->
<clover-root
  iiif-content="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json"
>
  <clover-body></clover-body>
  <clover-items layout="pager"></clover-items>
</clover-root>
```

## `Root`

| Prop                                                                   | Type                              | Description                                                                                                                                                                                                     |
| ---------------------------------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iiifContent` (web components: `iiif-content`)                         | `string \| object`                | A URL, a IIIF resource (Presentation 2, 3 or 4), or a content state.                                                                                                                                            |
| `canvasId` (web components: `canvas-id`)                               | `string`                          | The Canvas to show, controlled. Moving calls `onCanvasChange` (web components: `clover-canvas-change`) instead.                                                                                                 |
| `defaultCanvasId` (web components: `canvas-id`)                        | `string`                          | The Canvas to start on. A content state, then the Manifest’s `start`, win over it.                                                                                                                              |
| `onCanvasChange` (web components: `clover-canvas-change`)              | `(canvasId: string) => void`      | The reader moved to a Canvas.                                                                                                                                                                                   |
| `onStatusChange` (web components: `clover-status-change`)              | `(status, error) => void`         | `"loading"`, `"ready"` or `"error"`.                                                                                                                                                                            |
| `request` (web components: `.request`)                                 | `{ credentials, headers, fetch }` | Used for every request any part makes.                                                                                                                                                                          |
| `language`                                                             | `string`                          | The language to read IIIF text in. The page’s, then the reader’s, by default.                                                                                                                                   |
| `highContrast` (web components: `high-contrast`)                       | `boolean`                         | Clover’s higher-contrast mode, for every part inside: the accent kept to rings, outlines and marks, never under words or a glyph. On wherever the reader’s system asks for more contrast, too. Default `false`. |
| `annotations`                                                          | URL, object or list               | AnnotationPages or AnnotationCollections to show beside the Manifest’s own.                                                                                                                                     |
| `manifestId` (web components: `manifest-id`)                           | `string`                          | In a Collection, the member to show, controlled.                                                                                                                                                                |
| `defaultManifestId` (web components: `manifest-id`)                    | `string`                          | In a Collection, the member to open first.                                                                                                                                                                      |
| `onManifestChange` (web components: `clover-manifest-change`)          | `(manifestId) => void`            | In a Collection, the reader moved, or asks to move, to another member.                                                                                                                                          |
| `searchQuery` (web components: `search-query`)                         | `string`                          | A Content Search to run once the Manifest is shown, and whenever it changes.                                                                                                                                    |
| `onContentStateChange` (web components: `clover-content-state-change`) | `({ json, encoded }) => void`     | Where the reader is, as a content state, each time it settles.                                                                                                                                                  |
| `renderers` (web components: `.renderers`)                             | list                              | Components that draw views Clover does not; see [below](https://clover.iiif.library.northwestern.edu/docs/composing#renderers-of-your-own).                                                                     |
| `as`                                                                   | element type                      | Default `div`.                                                                                                                                                                                                  |

`iiifContent` (web components: `iiif-content`) can be a Manifest, a Collection, a Canvas or a content state, as a URL or an object; see [what the Viewer opens](https://clover.iiif.library.northwestern.edu/docs/viewer#what-iiifcontent-can-be). Every template, and the Viewer, takes these props and hands them to its `Root` (web components: `<clover-root>`).

## Parts

| Part                                                                                                                                                                | What it renders                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Root` (web components: `<clover-root>`)                                                                                                                            | Loads the content and holds where the reader is. Renders a `div` (or `as`), and announces moves.                                                                                                                                                                                                           |
| [`Body` (web components: `<clover-body>`)](https://clover.iiif.library.northwestern.edu/docs/body)                                                                  | The current view, drawn by `Canvas` (web components: `<clover-canvas>`): a page, a spread, a video or sound. Children join its controls. While it loads, Clover’s loading mark runs along its top edge; nothing moves.                                                                                     |
| [`Label` (web components: `<clover-label>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/label)                                                    | The Manifest’s label, or the current Canvas’s with `of="canvas"`, in the reader’s language.                                                                                                                                                                                                                |
| [`Previous` (web components: `<clover-previous>`), `Next` (web components: `<clover-next>`)](https://clover.iiif.library.northwestern.edu/docs/items#one-at-a-time) | Round arrow controls that move a view at a time (a spread, in a paged book), as Items’ arrows do, or with `of="members"` a Collection’s Manifest. Each names itself on hover and focus, and shows disabled at an end.                                                                                      |
| [`Position` (web components: `<clover-position>`)](https://clover.iiif.library.northwestern.edu/docs/items#one-at-a-time)                                           | “2 / 5”, counted in views (or members); “Item 2 of 5” to assistive technology, in the reader’s language. Its room is its widest count’s.                                                                                                                                                                   |
| `Status` (web components: `<clover-status>`)                                                                                                                        | Loading, as Clover’s loading mark (announced “Loading…”), or what went wrong. Once loaded, its status region stays, empty and hidden, so the next load is announced. `loading={false}` (web components: `loading="false"`) only announces it, where `Body` (web components: `<clover-body>`) shows it.     |
| [`Items` (web components: `<clover-items>`)](https://clover.iiif.library.northwestern.edu/docs/items)                                                               | Thumbnails of the views, as a slider, list or grid, or `layout="pager"`: `Position` (web components: `<clover-position>`), `Previous` (web components: `<clover-previous>`) and `Next` (web components: `<clover-next>`) in a `nav`. See [Items](https://clover.iiif.library.northwestern.edu/docs/items). |
| [`Panel` (web components: `<clover-panel>`)](https://clover.iiif.library.northwestern.edu/docs/panel)                                                               | The resource’s information in sections, as tabs or an accordion: the ones it has data for, or `Tab` (web components: `<clover-tab>`)s of your own. See [Panel](https://clover.iiif.library.northwestern.edu/docs/panel).                                                                                   |
| [`Tab` (web components: `<clover-tab>`)](https://clover.iiif.library.northwestern.edu/docs/panel#sections-of-your-own)                                              | One tab of the panel: `value`, `label`, and what it shows. Your own components go here.                                                                                                                                                                                                                    |
| [`About` (web components: `<clover-about>`)](https://clover.iiif.library.northwestern.edu/docs/panel#sections)                                                      | Everything the Manifest says about itself, as 3.x’s About tab shows it.                                                                                                                                                                                                                                    |
| [`PanelToggle` (web components: `<clover-panel-toggle>`)](https://clover.iiif.library.northwestern.edu/docs/panel#showing-and-hiding-it)                            | Shows and hides the panel. As a child of `Body` (web components: `<clover-body>`), it sits with the Canvas’s controls.                                                                                                                                                                                     |
| [`Annotations` (web components: `<clover-annotations>`)](https://clover.iiif.library.northwestern.edu/docs/annotations)                                             | The view’s annotations by set: content to read, a button to find each on the Canvas, and an eye before each set’s label that hides its annotations on the Canvas (shared with the Canvas’s annotations menu).                                                                                              |
| [`Download` (web components: `<clover-download>`)](https://clover.iiif.library.northwestern.edu/docs/download)                                                      | The `rendering` of the page in view and of the Manifest (a PDF, say), as links in two groups.                                                                                                                                                                                                              |
| [`Transcript` (web components: `<clover-transcript>`)](https://clover.iiif.library.northwestern.edu/docs/transcript)                                                | The view’s captions as text: the current cue marked as it plays, a press seeking there.                                                                                                                                                                                                                    |
| [`Contents` (web components: `<clover-contents>`)](https://clover.iiif.library.northwestern.edu/docs/contents)                                                      | The table of contents from the Manifest’s Ranges, the entry the reader is in marked. Hovering an entry draws its chapter out on the Canvas’s seek bar, and the chapter under the pointer there marks its entry.                                                                                            |
| [`Search` (web components: `<clover-search>`)](https://clover.iiif.library.northwestern.edu/docs/search)                                                            | IIIF Content Search (1 or 2): a field, then the hits by page with the words around them.                                                                                                                                                                                                                   |
| [`Collection` (web components: `<clover-collection>`)](https://clover.iiif.library.northwestern.edu/docs/collection)                                                | The Collection the Manifest is in: its label, summary, members, homepage and address, as the `Panel` (web components: `<clover-panel>`)’s Collection section shows it. Fetched when it first shows. `headingLevel` (web components: `heading-level`) sets its headings (default 3).                        |
| [`CollectionSelect` (web components: `<clover-collection-select>`)](https://clover.iiif.library.northwestern.edu/docs/collection)                                   | In a Collection, a menu of its members; choosing one shows it. By keyboard, the arrows move through the members and Enter opens one; Escape, or leaving the menu, takes the step back.                                                                                                                     |
| [`Canvas` (web components: `<clover-canvas>`)](https://clover.iiif.library.northwestern.edu/docs/canvas)                                                            | The renderer itself. Inside a `Root` (web components: `<clover-root>`) it shows the current view; see [Canvas](https://clover.iiif.library.northwestern.edu/docs/canvas).                                                                                                                                  |
| `Tip` (web components: `<clover-tip>`)                                                                                                                              | A control’s popover: its name and its key, on hover and focus. See [below](https://clover.iiif.library.northwestern.edu/docs/composing#controls-of-your-own).                                                                                                                                              |

Parts that have nothing to show render nothing: `Items` (web components: `<clover-items>`) and its pager for a single view, `Label` (web components: `<clover-label>`) with no label.

## Properties

[Primitives](https://clover.iiif.library.northwestern.edu/docs/primitives) has a page for each. Each IIIF descriptive property is a part of its own, reading the Manifest, or the current Canvas with `of="canvas"`. A value passed as `value` wins, so they also work outside a `Root` (web components: `<clover-root>`). Text is in the reader’s language and marked with `lang`; HTML is limited to what IIIF allows and sanitized, by a sanitizer that loads only when a value carries markup.

| Part                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Property                                                                                       |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [`Label` (web components: `<clover-label>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/label)                                                                                                                                                                                                                                                                                                                                                                                         | `label` (takes `label` rather than `value`)                                                    |
| [`Summary` (web components: `<clover-summary>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/summary)                                                                                                                                                                                                                                                                                                                                                                                   | `summary`, cut to five lines with “Read more” (below)                                          |
| [`Metadata` (web components: `<clover-metadata>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/metadata)                                                                                                                                                                                                                                                                                                                                                                                | `metadata`, as a description list; `renderValue` (web components: `.renderValue`) to customize |
| [`RequiredStatement` (web components: `<clover-required-statement>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/required-statement)                                                                                                                                                                                                                                                                                                                                                   | `requiredStatement`                                                                            |
| [`Rights` (web components: `<clover-rights>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/rights)                                                                                                                                                                                                                                                                                                                                                                                      | `rights`, as a link                                                                            |
| [`Homepage` (web components: `<clover-homepage>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/homepage), [`Rendering` (web components: `<clover-rendering>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/rendering), [`SeeAlso` (web components: `<clover-see-also>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/see-also), [`PartOf` (web components: `<clover-part-of>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/part-of) | Linked resources, by label, with their format                                                  |
| [`Thumbnail` (web components: `<clover-thumbnail>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/thumbnail)                                                                                                                                                                                                                                                                                                                                                                             | `thumbnail` (or a Canvas’s first image), at `size`                                             |
| [`Id` (web components: `<clover-id>`)](https://clover.iiif.library.northwestern.edu/docs/primitives/id)                                                                                                                                                                                                                                                                                                                                                                                                  | The resource’s `id`, as a link                                                                 |

`Value` (web components: `<clover-value>`) shows any other IIIF language map you hold, an annotation’s text say, marked and sanitized the same way. It reads a value strictly by its `format`: `text/html` as IIIF’s HTML, sanitized to the tags the Presentation API allows; `text/plain` as written, markup and all, its line breaks kept; and with no `format`, by IIIF’s rule for property values, as HTML only when it runs from a first `<` to a last `>`. With `format="text/markdown"` it reads Markdown, as `Annotations` (web components: `<clover-annotations>`) does for a `text/markdown` body: its lists are lists, and its headings are headings to assistive technology (`role="heading"`, drawn bold, so your page’s heading styles stay out of them). The first heading takes `headingLevel` (web components: `heading-level`) (default 3), and each later one sits a level under the nearest shallower heading before it, so none is skipped even where the source skips one. In `Annotations` (web components: `<clover-annotations>`) they sit a level under the set’s heading. Past `h6`, none is a heading ([Headings](https://clover.iiif.library.northwestern.edu/docs/composing#headings)).

Every summary Clover shows is `Summary` (web components: `<clover-summary>`), cut to five lines with a “Read more” button under it when it runs longer, and “Read less” to cut it again. The number of lines is a CSS custom property, `--clover-summary-lines`, set on `.clover-summary` or anything around it; `none` shows it whole:

```css
.my-layout .clover-summary {
  --clover-summary-lines: 3;
}
```

`ReadMore` (web components: `<clover-read-more>`) gives text of your own the same button: wrap one element (or a component that forwards its ref) and clamp it in your CSS; it carries `data-read-more="open"` while open.

**React**

```jsx
<Clover.ReadMore>
  <p className="my-note">{note}</p>
</Clover.ReadMore>
```

**Web components**

```html
<clover-read-more>
  <p class="my-note">…</p>
</clover-read-more>
```

## The information panel

`Panel` (web components: `<clover-panel>`) shows About, and each other section the content has something for: Collection, Contents, Search, Map, Transcript and Annotations. Give it `Tab` (web components: `<clover-tab>`)s to choose the sections yourself, in your order. Each part a default section uses works on its own too, in a `Tab` (web components: `<clover-tab>`) or anywhere in the layout, and so does a part of your own.

**React**

```jsx
<Clover.Panel>
  <Clover.Tab value="about" label="About">
    <Clover.About />
  </Clover.Tab>
  <Clover.Tab value="page" label="This page">
    <Clover.Label of="canvas" as="h3" />
    <Clover.Summary of="canvas" />
    <Clover.Metadata of="canvas" />
  </Clover.Tab>
</Clover.Panel>
```

**Web components**

```html
<clover-panel>
  <clover-tab value="about" label="About">
    <clover-about></clover-about>
  </clover-tab>
  <clover-tab value="page" label="This page">
    <clover-label of="canvas" as="h3"></clover-label>
    <clover-summary of="canvas"></clover-summary>
    <clover-metadata of="canvas"></clover-metadata>
  </clover-tab>
</clover-panel>
```

`PanelToggle` (web components: `<clover-panel-toggle>`) shows and hides it; as a child of `Body` (web components: `<clover-body>`), it sits with the Canvas’s controls. [Panel](https://clover.iiif.library.northwestern.edu/docs/panel) has its layouts, the sections and when each shows, headings, closing a panel that floats over the page, and its props.

## Panes side by side

`Split` (web components: `<clover-split>`), `SplitPane` (web components: `<clover-split-pane>`) and `SplitHandle` (web components: `<clover-split-handle>`) put panes side by side, with a bar between them that shares the width. One pane beside each bar has a `size` (with `min` and `max`, in px, rem or %); the other takes what is left. The reader drags the bar by its grip, or clicks or taps it: each click steps the pane on to its next width, from its narrowest to its own `size` to its widest and then around again, so one pointer can resize it without dragging. The bar is a focusable `separator` that names itself on hover and focus: the arrow keys move it (Shift for bigger steps), and Home and End go to the limits. With `storageKey` (web components: `storage-key`) the reader’s sizes are remembered. The `Default` and `Document` templates use it, and nothing ties it to any part.

**React**

```jsx
<Clover.Root iiifContent={url}>
  <Clover.Split storageKey="my-layout">
    <Clover.SplitPane min="16rem">
      <Clover.Body />
    </Clover.SplitPane>
    <Clover.SplitHandle />
    <Clover.SplitPane size="24rem" min="14rem">
      <Clover.Panel />
    </Clover.SplitPane>
  </Clover.Split>
</Clover.Root>
```

**Web components**

```html
<clover-root
  iiif-content="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json">
  <clover-split storage-key="my-layout">
    <clover-split-pane min="16rem">
      <clover-body></clover-body>
    </clover-split-pane>
    <clover-split-handle></clover-split-handle>
    <clover-split-pane size="24rem" min="14rem">
      <clover-panel></clover-panel>
    </clover-split-pane>
  </clover-split>
</clover-root>
```

## Parts of your own

Hooks read from the nearest `Root` (web components: `<clover-root>`). Each re-renders only when what it reads changes.

| Hook                                                                     | Returns                                                                                                                                                        |
| ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `useStatus()` (web components: `root.clover.status()`)                   | `{ status, error }`                                                                                                                                            |
| `useManifest()` (web components: `root.clover.manifest()`)               | The Manifest, in Presentation 4.                                                                                                                               |
| `useSequence()` (web components: `root.clover.sequence()`)               | `{ canvases, views, direction }`                                                                                                                               |
| `useVisibleCanvases()` (web components: `root.clover.visibleCanvases()`) | The Canvases of the current view.                                                                                                                              |
| `useActiveCanvas()` (web components: `root.clover.activeCanvas()`)       | The Canvas the reader is on.                                                                                                                                   |
| `useNavigation()` (web components: `root.clover.navigation()`)           | `{ position, total, hasPrevious, hasNext, previous, next, first, last, goTo }`                                                                                 |
| `useAnnotationCollection()`                                              | Annotations walked as one whole, the content’s AnnotationPage or the Manifest’s AnnotationCollection: `{ id, type, label, total, pages, entries }`, or `null`. |
| `useLocalized(value)`                                                    | `{ text, lang }` for a IIIF language map, in the Root’s language.                                                                                              |
| `useT()`                                                                 | `t(key, values)`: Clover’s strings, in its interface language.                                                                                                 |
| `useLangOf()`                                                            | `langOf(key)`: a string’s `lang` where it is not the page’s language.                                                                                          |

**React**

```jsx
function PageCount() {
  const { position, total } = Clover.useNavigation();
  return (
    <span>
      {position + 1} / {total}
    </span>
  );
}
```

**Web components**

```html
<span id="page-count"></span>
 
<script type="module">
  const root = document.querySelector("clover-root");
  const count = document.getElementById("page-count");
  const show = () => {
    const { position, total } = root.clover.navigation();
    count.textContent = `${position + 1} / ${total}`;
  };
  const start = () => {
    show();
    root.clover.subscribe(show);
  };
  if (root.clover) start();
  else root.addEventListener("clover-ready", start, { once: true });
</script>
```

`useClover()` (web components: `root.clover`) gives `{ clover }`, the Root’s state and actions (`clover.goTo(canvasId)`, `clover.next()`, `clover.search(query)` and the rest), for parts that act rather than read.

### Headings

Every part that draws a title takes `headingLevel` (web components: `heading-level`), one below the heading it sits under, so a screen reader’s list of headings follows your page. HTML’s headings end at `h6`, and screen readers misread a deeper `aria-level` (JAWS, and TalkBack with Chrome, announce a 7 as a 2), so past 6 Clover draws no heading: a title that would be an `h7` names the group it heads instead. `Clover.titled` does the same in a layout of your own.

**React**

```jsx
function MyLayout({ url, headingLevel }) {
  const title = Clover.titled(headingLevel, "my-title");
  return (
    <Clover.Root iiifContent={url} {...title.group}>
      <Clover.Label as={title.as} {...title.title} />
      <Clover.Body />
    </Clover.Root>
  );
}
```

**Web components**

```html
<clover-root
  iiif-content="https://iiif.io/api/cookbook/recipe/0009-book-1/manifest.json"
>
  <clover-label as="h2"></clover-label>
  <clover-body></clover-body>
</clover-root>
```

### Controls of your own

Every control Clover draws as an icon alone names itself in a popover on hover and on keyboard focus, with its keyboard shortcut when it has one. Give a control of your own the same: its name as `aria-label`, its key (if it has one) as `aria-keyshortcuts`, and `Tip` (web components: `<clover-tip>`) inside it, last, with the same words. Tip is hidden from assistive technology, which reads the control’s own name, and hides on Escape. Each time it shows, it is kept inside whatever clips it (the Body, a scrolling panel, the window): it wraps to the room there is and moves in from the edge.

**React**

```jsx
<button type="button" aria-label="Bookmark" onClick={save}>
  <BookmarkIcon aria-hidden="true" />
  <Clover.Tip label="Bookmark" />
</button>
```

**Web components**

```html
<button type="button" id="bookmark" aria-label="Bookmark">
  <svg aria-hidden="true">…</svg>
  <clover-tip label="Bookmark"></clover-tip>
</button>
 
<script type="module">
  document.getElementById("bookmark").addEventListener("click", save);
</script>
```

Clover’s popups open one at a time: the Canvas’s information and its menus (annotations, Choice, an animation’s frames), the playback controls’ menus and Download’s list. Opening one closes whichever other is open, however it was opened. A popup of your own joins them with `useOneOpen` (web components: `root.clover.oneOpen()`), given its open state and the way to close it:

**React**

```jsx
const [open, setOpen] = useState(false);
Clover.useOneOpen(open, () => setOpen(false));
```

**Web components**

```html
<script type="module">
  const root = document.querySelector("clover-root");
  let release = null;
  const close = () => {
    menu.hidden = true;
    release?.();
  };
  const open = () => {
    menu.hidden = false;
    release = root.clover.oneOpen(close);
  };
</script>
```

## Languages

Clover’s own words (its controls’ names, its messages, what it announces) are in its interface language: the one set with `initCloverI18n`, else the page’s `<html lang>`, else the first of the browser’s languages it has, else English, so it speaks the language of the page around it. It ships English, Finnish, French, Norwegian (Bokmål and Nynorsk), Portuguese (European) and Spanish, each complete. IIIF text is read in the Root’s `language`, else the page’s, else the browser’s. Where what the resource says of itself comes in several languages, the Panel’s About offers them, and the one the reader chooses is the whole Root’s until the page sets another `language`.

**React**

```jsx
import { initCloverI18n } from "@nulib/clover";
 
// The page's language, and strings of your own for one Clover does not ship.
initCloverI18n({
  lng: "de",
  resources: { de: { clover: { informationPanelTabsAbout: "Über" } } },
});
```

**Web components**

```html
<script type="module">
  import { initCloverI18n } from "@nulib/clover/web-components";
 
  initCloverI18n({
    lng: "de",
    resources: { de: { clover: { informationPanelTabsAbout: "Über" } } },
  });
</script>
```

English comes with Clover; each other language loads the first time Clover is to speak it, so a page in English never fetches one. Until it arrives, Clover’s words show in English, marked `lang="en"`, and they change when it does. A page rendered on the server, or a test, that needs them in place first waits for them:

```js
await initCloverI18n({ lng: "fr" }).ready();
```

Where the interface language is not the page’s (a French reader on an English page), each of Clover’s strings carries it as `lang`, on the element that shows it or is named by it. The page’s language is the nearest `lang` around the `Root` (web components: `<clover-root>`), or one you give the `Root` (web components: `<clover-root>`). The `Root` (web components: `<clover-root>`) carries none of its own, so what you put in it, and IIIF text in no language (`none`), keep the page’s. A language you bootstrap with some strings falls back to English for the rest, each such string marked `lang="en"` too. In a part of your own, mark Clover’s strings the same way:

**React**

```jsx
const t = Clover.useT();
const langOf = Clover.useLangOf();
 
<button
  type="button"
  aria-label={t("commonClose")}
  lang={langOf("commonClose")}
>
  <CloseIcon aria-hidden="true" />
  <Clover.Tip label={t("commonClose")} />
</button>;
```

**Web components**

```html
<button type="button" id="close">
  <svg aria-hidden="true">…</svg>
  <clover-tip></clover-tip>
</button>
 
<script type="module">
  const root = document.querySelector("clover-root");
  const close = document.getElementById("close");
  const start = () => {
    const label = root.clover.t("commonClose");
    const lang = root.clover.langOf("commonClose");
    close.setAttribute("aria-label", label);
    close.querySelector("clover-tip").setAttribute("label", label);
    if (lang) close.lang = lang;
    else close.removeAttribute("lang");
  };
  if (root.clover) start();
  else root.addEventListener("clover-ready", start, { once: true });
</script>
```

## Renderers of your own

Give `Root` (web components: `<clover-root>`) (or one `Body` (web components: `<clover-body>`)) `renderers` (web components: `.renderers`), and a view they claim is drawn by your component instead of Clover’s own (Canvas, Timeline or Scene): a PDF, a 3D model drawn your way, anything Clover does not draw, or draws otherwise than you want. Claim a view by its Canvas id, by a painted body’s `format`, or with a `match` test. The first renderer that claims a view draws it. This is 3.x’s `customDisplays`.

**React**

```jsx
function ModelViewer({ bodies }) {
  // Inside the Root: Clover's hooks work here (useNavigation, useActiveCanvas, useClover…).
  return <model-viewer src={bodies[0].id} camera-controls />;
}
 
<Clover.Root
  iiifContent={url}
  renderers={[{ format: ["model/gltf-binary"], component: ModelViewer }]}
>
  <Clover.Body />
</Clover.Root>;
```

**Web components**

```html
<clover-root
  iiif-content="https://iiif.io/api/cookbook/recipe/0608-mvm-3d/manifest.json"
>
  <clover-body></clover-body>
</clover-root>
 
<script type="module">
  document.querySelector("clover-root").renderers = [
    {
      format: ["model/gltf-binary"],
      render(container, { bodies }) {
        const model = document.createElement("model-viewer");
        model.setAttribute("src", bodies[0].id);
        model.setAttribute("camera-controls", "");
        container.append(model);
      },
    },
  ];
</script>
```

A renderer gets the view’s `canvases` (web components: `.canvases`), their painting `bodies` (a `Choice` as its first item, a `SpecificResource` as its source) and `Body` (web components: `<clover-body>`)’s `children`. The Viewer and every template pass `renderers` (web components: `.renderers`) through to their `Root` (web components: `<clover-root>`).

| 3.x `customDisplays`         | 4.x `renderers` (web components: `.renderers`)           |
| ---------------------------- | -------------------------------------------------------- |
| `target.canvasId`            | `canvasId` (web components: `canvas-id`)                 |
| `target.paintingFormat`      | `format`                                                 |
| (none)                       | `match: ({ canvases, bodies }) => boolean`               |
| `display.component`          | `component`                                              |
| `display.componentProps`     | close over them: `component: (p) => <X {...p} a />`      |
| `id`, `annotationBody` props | `canvases` (web components: `.canvases`), `bodies` props |
| `hooks` prop                 | Clover’s hooks, called in the component                  |

## Styles

Clover’s stylesheets, the custom properties a layout sets (the Body’s height, the insets, the colors), the classes and states to select, and the color pairs to keep accessible are on [Styling](https://clover.iiif.library.northwestern.edu/docs/styling).
