# Elements

Every part of the booking widget is a custom element whose name starts with `mellow-`. This page lists each one. The tables of attributes and values come from the widget's own types, `widget/types/elements.d.ts`, when the site is built, so they always match the script.

## Rules for every element

1. **The page's own DOM.** An element draws into the page, not into a shadow root. The site's CSS thus reaches every part. `isolate` on `<mellow-booking>` is the one exception: see [Look](/widget/look).
2. **Default content, or your own.** An element with no child elements draws its default content. An element with child elements draws nothing of its own: it binds the children it finds. Thus `<mellow-search></mellow-search>` is a whole search form, and `<mellow-search>` around your own form gives your form Mellow's behaviour.
3. **State as attributes.** An element shows its state as `data-*` attributes, such as `data-state`, `data-selected`, `data-disabled`, `data-loading`, `data-invalid` and `data-empty`. No element sets a class or an inline style. Style these attributes in your CSS.
4. **Native controls.** The default content uses `<button>`, `<select>`, `<input>`, `<fieldset>` and `<legend>`. The calendar follows the ARIA grid pattern. The words come in English, Icelandic, German, French, Spanish and Italian.
5. **One store.** `<mellow-booking>` owns the store of one booking. Every element inside reads it. A `<mellow-search>` that stands alone owns its own store.
6. **Strings only.** Every setting is a string attribute. Thus a server render, a WordPress shortcode and plain HTML set them the same way. An attribute with "no value" in the tables is on when it is there, such as `<mellow-booking isolate>`.
7. **Your own inputs.** Inside the search and the driver form, an `input`, a `select` or a `textarea` with a name the widget knows binds to the store.

### Scopes

`<mellow-value>` and `<mellow-price>` show one field of the nearest **scope** above them. A scope is a car type, an insurance option, a product, a feature, a quote line or the confirmation. Outside every scope they read the order itself:

- Fields: the chosen car type's fields, `pickupAt`, `returnAt`, `pickupLocation`, `returnLocation`, `days`, `customer.firstName`, `reference` and `code`.
- Prices: `payNow`, `total`, `payOnArrival`, `perDay`, `tax` and `deposit` of the order's quote.

### Loading

While a read runs, the element waiting for it says so: `data-loading` and `aria-busy="true"` on `<mellow-search>` (the locations and their hours), `<mellow-car-types>` (the offers), `<mellow-insurance>` and `<mellow-products>` (the car type's products), and `<mellow-summary>` and `<mellow-lines>` (the quote). The step the address asks for shows at once. In the default content, and in a list with a template, skeletons stand in the place of what comes, in its shape: each is `[part=skeleton]` with `data-kind` (`car-type`, `option`, `product` or `line`), made of `[part=bone-picture]`, `[part=bone-line]`, `[part=bone-short]`, `[part=bone-price]` and `[part=bone-button]`. A re-quote keeps the last numbers in the summary. `<mellow-next>` stays disabled while the offers, the products or a quote load.

### Templates

A list element repeats its first `<template>` child for each item: `<mellow-car-types>`, `<mellow-features>`, `<mellow-products>`, `<mellow-lines>` and `<mellow-progress>`. Without a template it draws its default item. A list with a template draws the items only: `<mellow-add-ons>` and `<mellow-products>` then leave out their heading.

```html
<mellow-car-types>
  <template>
    <article class="car">
      <mellow-image index="0"></mellow-image>
      <h3><mellow-value of="name"></mellow-value></h3>
      <p>
        <mellow-value of="seats"></mellow-value> seats ·
        <mellow-value of="transmission"></mellow-value>
      </p>
      <mellow-price of="payNow"></mellow-price>
      <mellow-choose>Choose</mellow-choose>
    </article>
  </template>
</mellow-car-types>
```

## The root and the flow

### `<mellow-booking>`

The root. It makes the store, reads the order from the page's address, and keeps the step in the address, so the browser's Back button works. With no child elements it draws the whole flow: the step list, the currency picker, every step, the errors, Back and Next, and the summary beside the steps.

::::widget-attributes{tag="mellow-booking"}
- `steps`: The steps in order, space-separated, from `search car-type insurance extras driver pay done`. Leave one out to skip it, such as `extras`. The insurance step and the extras step also drop out by themselves when the chosen car type has nothing for them.
- `paths`: One page for each step, such as `car-type=/book insurance=/book/insurance driver=/book/details`. Without it every step is on this page. See [Pages and paths](/widget/pages).
- `url-state`: `query` (the default) keeps the step and the order in the address. `none` keeps them in memory.
- `pay`: `embedded` (the default) draws the processor's card form inside `<mellow-pay>`. `redirect` sends the customer to the processor's own page.
- `return-url`: Where the processor's page sends the customer back after `pay="redirect"`. The pay step's page by default.
- `isolate`: Draws the default flow into a shadow root, for a site whose theme CSS leaks in. Your own child elements are then left out. Only the tokens and `::part()` style it. The embedded Rapyd form does not work under `isolate` yet.
::::

State: `data-step` (the current step), `data-loading` and `aria-busy` while a read is open, and `data-narrow` while the summary has no room beside the steps.

Where it is narrow, the default flow shows the total in a bar at the bottom of the screen in place of the summary under the steps. `[part=nav]`, the row of Back and Next, sticks to the bottom and holds `[part=total]`, a button with the total and "Tax included", and Next. A press on the total opens the whole summary in `[part=sheet]`, a dialog from the bottom with a `<mellow-summary>` of its own and `[part=close]`. Back stands above the step. `[part=main]` carries `data-narrow` too, and `[part=nav]` has `data-empty` until a car type is chosen. Your own child elements get none of this.

After a step change, the new step's first heading takes the focus with no visible ring, so a screen reader starts there.

### `<mellow-step>`

Shows its children only while the store's step is one of the names in `when`. `when="extras driver"` puts two steps on one screen. State: `data-state` is `active` or `inactive`, and the element has `hidden` while it is inactive.

::::widget-attributes{tag="mellow-step"}
- `when`: One or more step names, space-separated.
::::

### `<mellow-progress>`

A list of the steps, without done. Each item has `data-step` and `data-state`: `done`, `current` or `todo`. A done step is a button back to it. A `<template>` child sets each item, with `<mellow-value of="name">` and `<mellow-value of="number">`, and any `<button>` in it goes to that step.

### `<mellow-back>`

A button one step back. It is hidden on the first step and on done. Your own `<button>` inside takes the behaviour. State: `data-hidden`.

### `<mellow-next>`

A button one step on. It is hidden on the search, pay and done steps: the search has its own button, and Pay is the last move. It has `data-disabled` while the step is not complete, and a press then shows what is missing in `<mellow-error>`. Your own `<button>` inside takes the behaviour. State: `data-hidden`, `data-disabled`.

### `<mellow-error>`

The last refusal, as text in the customer's language, such as "No car of that type is free for those days." It is empty, with `data-empty`, while there is none. `data-code` holds the API's code, such as `none_free`. A `<mellow-error>` with no `for` leaves out the parts that have their own `<mellow-error for="…">` on the page.

::::widget-attributes{tag="mellow-error"}
- `for`: The parts to show, space-separated: a step name, `coupon`, `quote`, `pay` or `offers`.
::::

## The search

### `<mellow-search>`

The search form. It keeps a draft while the customer fills it, and sends the search on submit:

- **Inside `<mellow-booking>`**, it sets the store's search and moves to the car types.
- **Alone, with `action`**, it sends the browser to `action` with the search in the address. Put `<mellow-booking>` on that page. This is the shape of a search box on the home page.

An empty search starts filled in: the first pickup location, pickup 7 days from today and return 4 days later (or the rental's minimum stay), each on the first day the location is open, at 12:00 or the nearest open time. A search in the address, or a value in your own box, wins. The customer still presses Search.

Your own inputs inside bind by name: `pickup`, `return`, `otherReturn`, `pickupAt` and `returnAt` (as `datetime-local`), or `pickupDate`, `pickupTime`, `returnDate` and `returnTime`, then `driverAge` and `coupon`. An empty `<select>` of yours gets the options. State: `data-complete` once a location, both dates and both times are set, and `data-other-return` while the return goes to another location.

::::widget-attributes{tag="mellow-search"}
- `rental`: The rental's slug. Only when the search stands alone, outside `<mellow-booking>`.
- `auth`: The widget's browser key. Only when the search stands alone.
- `key`: The old name of `auth`, for plain HTML. Only when the search stands alone.
- `locale`: As on the root. Only when the search stands alone.
- `preset`: As on the root. Only when the search stands alone.
- `layout`: `stack` puts the fields under each other. `row` puts them side by side, and the calendar opens in a dialog. The DOM is the same: the preset reads the attribute.
- `car-type`: A car type's slug, id or code, for that car type's own page. Only when the search stands alone. The search then prices that car type and shows `<mellow-book>` in place of `<mellow-submit>`.
::::

The address it writes holds `pickup`, `pickupAt`, `return`, `returnAt`, `driverAge`, `coupon` and `mref`. Times are the rental's local time, written `YYYY-MM-DDTHH:MM`, as the API takes them.

### `<mellow-location>`

The rental's locations that serve a pickup or a return, as a list to pick from. The default content is a button that opens the list under it, or above it when the room below is too short. The list follows the ARIA listbox pattern: the arrow keys, Home, End, Page Up and Page Down move, a typed letter jumps to a location, Enter and Space pick, Escape and Tab close, and the focus goes back to the button. A location closed on the chosen day has `data-disabled` on its option. While the return goes to the pickup location, `for="return"` has `data-hidden`. A rental with only one location for a side hides that side, with `data-hidden`, and the search takes the location. Your own `<select>` inside binds instead. State: `data-empty` when no location serves it, `data-open` while the list is open.

Default parts: `[part=trigger]` (the button) with `[part=value]` and `[part=chevron]`, `[part=popover]` around `[part=list]`, and `[part=option]` for each location. `[part=trigger]` has `data-placeholder` while nothing is picked. `[part=popover]` has `data-side` (`bottom` or `top`) and `data-align` (`start` or `end`). An option has `data-selected`, `data-highlighted` and `data-disabled`.

::::widget-attributes{tag="mellow-location"}
- `for`: `pickup` or `return`.
::::

### `<mellow-location-detail>`

A text box that shows only when the chosen location asks for more, such as "Which hotel?". The location's own question is the label. State: `data-hidden` while the location asks for nothing.

::::widget-attributes{tag="mellow-location-detail"}
- `for`: `pickup` or `return`.
::::

### `<mellow-other-return>`

A checkbox: return to another location. It is off by default, and the return location then follows the pickup. Ticked, the return location shows and asks for its pick. Your own checkbox inside, or one named `otherReturn` in the search, binds instead. State: `data-selected` while ticked, and `data-hidden` when fewer than two locations take returns.

### `<mellow-dates>`

A range calendar. The customer picks the dates and never types them. One day takes the Tab key. The arrow keys move a day or a week, Home and End go to the ends of the week, Page Up and Page Down move a month, and Enter and Space pick. A picked day gets 10:00, or the nearest time the location is open. The rental's minimum stay blocks a return that is too early. State: `data-state` is `empty`, `picking-return` or `complete`.

::::widget-attributes{tag="mellow-dates"}
- `months`: How many months show side by side, 1 to 3. 1 by default.
- `mode`: `inline` (the default) draws the calendar in the page. `popover` draws a button that opens it in a `<dialog>`. `layout="row"` on the search sets `popover`.
::::

Default parts: `[part=head]`, `[part=hint]`, and for `popover` also `[part=label]`, `[part=open]` and `[part=close]`. For a calendar of your own, put these parts inside:

### `<mellow-month>`

One month as a `<table role="grid">` of `<mellow-day>` elements, with its name in `[part=month-name]`, a heading by its `role`. State: `data-month`, such as `2027-06`.

::::widget-attributes{tag="mellow-month"}
- `offset`: The month after the first one shown, counted from 0. `offset="1"` is the second month.
::::

### `<mellow-day>`

One day, around its `<button>`. `<mellow-month>` makes these. `data-state` on the element is a space-separated list of `start`, `end`, `in-range`, `selectable`, `closed` (the location is closed), `blocked` (before the lead time, or past the longest stay) and `today`. Style it with `[data-state~=start]`.

::::widget-attributes{tag="mellow-day"}
- `date`: The day, `YYYY-MM-DD`. `<mellow-month>` sets it.
::::

### `<mellow-month-prev>`

A button to the month before. State: `data-disabled` on the current month.

### `<mellow-month-next>`

A button to the month after.

### `<mellow-time>`

The times the location is open on the chosen day, `step` minutes apart. A closed time and a lunch close are left out. The default content is a button with the time that opens one of two pickers:

- **A wide screen**: a popover with a grid of the open hours, and a row of the open minutes of the picked hour. A press on an hour keeps the minutes. A press on the minutes closes it. The arrow keys move in the grid, Home and End go to its ends, digits jump to an hour, and Escape closes.
- **A phone** (a coarse pointer, or a screen under 40rem): a sheet from the bottom with two wheels, hours and minutes, that snap as they turn. The time follows the wheels. Each wheel is a listbox that the arrow keys turn. Done or Escape closes it.

Your own `<select>` inside binds instead and gets the open times. State: `data-empty` when the location is closed that day, `data-open` while a picker is open.

Default parts: `[part=trigger]` with `[part=value]` and `[part=chevron]`; `[part=popover]` with `[part=hours]` and `[part=minutes]`, of `[part=hour]` and `[part=minute]` buttons; `[part=sheet]` (a `<dialog>`) with `[part=sheet-head]`, `[part=sheet-title]`, `[part=done]`, `[part=wheels]`, `[part=wheel]` and `[part=wheel-item]`. An hour, a minute and a wheel item have `data-selected`.

::::widget-attributes{tag="mellow-time"}
- `for`: `pickup` or `return`.
- `step`: The minutes between two times. 30 by default.
::::

### `<mellow-driver-age>`

The driver's age, a list from the youngest age any car type takes, with the same button, parts and keys as `<mellow-location>`. Type "25" to jump to 25. It is optional. Your own `<select>` inside binds instead. With an age, a quote adds the young driver fee, and a car type the driver is too young for shows `data-state="too-young"`.

### `<mellow-coupon>`

A coupon box. Inside the search it goes with the search. Anywhere else it has an Apply button that prices the order with the coupon at once. Inside the search, Search checks the coupon against the car types' prices before it moves on. State: `data-state` is `empty`, `checking`, `applied` or `refused`. The default content says the result under the box in `[part=status]`: the coupon and what it gives, such as "SPRING10: 10% off", or the refusal, such as "This coupon does not exist." In your own markup the refusal shows in `<mellow-error for="coupon">`. A refused coupon changes no price, and it keeps the customer on the search until they fix it or clear it. A coupon that some car types take is applied, and each car type that took it shows the price before the coupon in `<mellow-price of="before">`.

### `<mellow-submit>`

The search button. State: `data-disabled` until a location, both dates and both times are set.

### `<mellow-book>`

On a car type's own page, in a search with `car-type`: the price of that car type for the search, and **Book**. It asks for the price by itself once a location, both dates and both times are set, and again after each change. Book opens `action` with the search and the car type chosen, on the insurance step. When the car type cannot be booked, it says why, such as "Not free on these dates", and **See other campers** (or cars) opens `action` on the car type step for the same search. State: `data-state` is `empty` until the search is complete, then `loading`, `free`, `sold-out`, `unavailable` or `too-young`, and `data-loading` while the price comes.

Default parts: `[part=price]` with `[part=amount]` and `[part=days]`, `[part=status]` (the hint, the wait or the reason), `[part=book]` and `[part=others]`. In your own markup inside, a button named `book` books and one named `others` opens the other car types.

## Car types

### `<mellow-car-types>`

The car types for the search, each with its price, from one quote request for all of them. A car type that cannot be booked stays in the list with its reason in `data-state` on its `<mellow-car-type>`. The first `<template>` child is each item. Without one, each item is the default card: the first picture, the name, one line of facts, the pay-now total and the price a day, then Details and Choose side by side. The picture and Details both open the car type's details (`<mellow-details>`). State: `data-empty` when nothing is left to show, `data-loading` while the prices come.

::::widget-attributes{tag="mellow-car-types"}
- `sort`: `price` (the default), `name`, `seats`, or `rental` for the rental's own order.
- `hide-unavailable`: Leaves out every car type that cannot be booked.
::::

### `<mellow-car-type>`

One car type's scope. The list makes one for each item. Place one yourself with `id` to show one car type on its own page: beside a search with `car-type`, it reads that search, and inside a `<mellow-booking>` that starts without the car type step, it chooses itself. State: `data-selected`, `data-state` (`free`, `sold-out`, `unavailable` or `too-young`), `data-kind` (`car` or `camper`), `data-transmission`, `data-drive` and `data-fuel`. Thus `[data-kind=camper]` styles campers apart.

::::widget-attributes{tag="mellow-car-type"}
- `id`: The car type's id, slug or code, for a car type placed on its own page. The list sets nothing here.
::::

Fields of a car type for `<mellow-value>`: `name`, `description`, `make`, `model`, `code`, `slug`, `acriss`, `seats`, `doors`, `largeBags`, `smallBags`, `transmission`, `drive`, `fuel`, `kind`, `sleeps`, `minDriverAge`, `includedKmPerDay`, `yearFrom`, `fRoads`, `guaranteedModel` and `customFields.<key>`. Words such as `manual` come in the customer's language.

### `<mellow-value>`

One field of the nearest scope, as bare text with no markup of its own. State: `data-empty` when the field is empty.

::::widget-attributes{tag="mellow-value"}
- `of`: The field's name, such as `name`, `seats` or `customFields.beds`.
::::

### `<mellow-price>`

One price of the nearest scope, in the customer's locale, and in the currency the customer picked. State: `data-empty` when there is none, `data-loading` while the price comes.

::::widget-attributes{tag="mellow-price"}
- `of`: `payNow` (the default): what the customer pays with the booking. `total`: the whole total, fees at the desk included. `payOnArrival`: the fees paid at the desk, empty when 0. `perDay`: `payNow` over the days. `tax`: the tax in the total. `deposit`: the deposit. Inside a car type: `before`, the price before the coupon, empty when no coupon took anything off. Inside an option: `optionDelta` ("Included", "Refused", or what it adds) and `optionTotal`. Inside `<mellow-line>`: `line`. Inside `<mellow-product>`: `unit`, the product's own price, such as "1,500 kr. a day, at most 7 days".
::::

### `<mellow-image>`

One of the car type's pictures as an `<img>`, with `alt` from the name and `loading="lazy"`. Inside a `<mellow-product>` or a `<mellow-option>` it is the product's one picture instead, and `index` does nothing. The `<img>` has a `srcset` of the picture's widths, so the browser loads the smallest one that fills the space. State: `data-empty` when there is no such picture.

::::widget-attributes{tag="mellow-image"}
- `index`: Which picture, counted from 0. 0 by default.
- `sizes`: The width the picture takes on the page, as the `sizes` of an `<img>`, such as `(max-width: 40rem) 100vw, 20rem`. `auto, 100vw` by default: the browser measures a lazy picture itself.
::::

### `<mellow-gallery>`

Every picture of the car type in one strip that swipes on a phone, with Previous and Next buttons over it and a count, such as 2 / 4. The pictures come in the rental's order, and a picture tagged `floor_plan` comes last. Parts: `[part=track]` (the strip of `<img>`), `[part=prev]`, `[part=next]` and `[part=count]`. Each `<img>` has `data-tag` (`exterior`, `interior` or `floor_plan`) when the rental tagged it: Plain shows a floor plan whole, never cut. State: `data-index` (from 0), `data-empty`.

### `<mellow-features>`

The car type's features that are on, in Mellow's groups. Each item is a `<mellow-feature>`. A `<template>` child sets the item's markup. State: `data-empty`.

::::widget-attributes{tag="mellow-features"}
- `group`: One group only: `technology`, `utilities`, `camping`, `heating_cooling`, `kitchen` or `sleeping`. Every group by default.
::::

### `<mellow-feature>`

One feature's scope. Fields: `name`, `group`, `count`, `size` and `label` (the name with the count and the size, such as "2 × Duvet, 140 × 200 cm"). State: `data-group`.

### `<mellow-details>`

The details of the car type of its scope, in a native modal `<dialog>`: the pictures, the description, the features in their groups, the name with "or similar" when the model is not guaranteed, the price for the trip and a day, Choose (or the car type's state, such as Sold out), and the specs. With no child elements it draws a Details button, `[part=open]`. With child elements, a press on any button or link among them opens it, such as `<mellow-details><button>More</button></mellow-details>` or a picture inside a button. A `<template>` child sets the dialog's body, and the widget's parts inside it read the same car type. The body is drawn at each open and removed at close.

The dialog traps the focus, Escape or Close (`[part=close]`) shuts it, and the focus goes back to the button that opened it. Choose inside it chooses and closes it. It sends `mellow-details` when it opens. Parts of the dialog: `[part=dialog]`, `[part=head]`, `[part=close]`, `[part=body]`, then in the default body `[part=main]` (pictures, description, features), `[part=side]`, `[part=title]`, `[part=similar]`, `[part=buy]` (the price and Choose), `[part=price]`, `[part=days]` and `[part=features]`, with one `<section data-group>` for each group. With Plain, a phone gets a sheet over the whole screen that slides up, with Choose fixed at the bottom. State: `data-open`.

```html
<mellow-car-types>
  <template>
    <article class="car">
      <mellow-details
        ><button class="pic"><mellow-image index="0"></mellow-image></button
      ></mellow-details>
      <h3><mellow-value of="name"></mellow-value></h3>
      <mellow-details><button class="more">See inside</button></mellow-details>
      <mellow-choose>Choose</mellow-choose>
    </article>
  </template>
</mellow-car-types>
```

### `<mellow-specs>`

The car type's specs as a `<dl>`, one `<div data-spec>` for each spec it has: `sleeps` (a camper), `seats`, `doors`, `bags`, `transmission`, `drive`, `fuel`, `year` (such as "2022 or newer"), `km` (such as "250 km a day", or "Unlimited"), `age` (the youngest driver) and `f-roads` (only where the rental's country has them). State: `data-empty`.

### `<mellow-description>`

The car type's description from the rental's Markdown: paragraphs, lists, bold, italic and https links, drawn as elements, never as HTML. State: `data-empty`.

### `<mellow-choose>`

A button that chooses the car type of its scope and moves on. A car type that cannot be booked shows why in `[part=status]` in its place: Sold out, Not available, or Driver too young. State: `data-disabled`, `data-loading`.

### `<mellow-payment-terms>`

The payment terms the rental sells for the car type of its scope, as a radio list: each term's title, its difference from the cheapest term ("Included" on the cheapest), and one line on what it allows under the picked one. Picking a term prices the order on it, and the hold and the booking take it. The car type's details show it under Choose. A rental that sells one term shows nothing. Each row is a `<label part="term">` with `data-selected`, holding `[part=title]`, `[part=difference]` and `[part=summary]`. State: `data-empty`. See Payment terms in the API guide.

### `<mellow-filter>`

Checkboxes over the list, one for each value the list holds of one field. State: `data-empty` when the list holds fewer than two values.

::::widget-attributes{tag="mellow-filter"}
- `field`: The field to filter on.
::::

### `<mellow-sort>`

A `<select>` that sorts the list by price, name, seats or the rental's own order.

## Insurance

The insurance step sells what the rental sells, in the shape the rental sells it. A **level** (such as Basic, Premium, Platinum) keeps the cover of each level below it, and a booking holds one. An **add-on cover** goes beside any level. A **package** is one priced line of other products. An **included cover** is on every booking.

### `<mellow-insurance>`

The step's container. With no child elements it picks its layout from what the rental sells for the chosen car type. State: `data-shape`:

| `data-shape` | The rental sells                 | The default layout                                  |
| ------------ | -------------------------------- | --------------------------------------------------- |
| `levels`     | Levels, with or without packages | `<mellow-compare>`, then `<mellow-add-ons>` below   |
| `packages`   | Packages only                    | `<mellow-compare>` with one column for each package |
| `add-ons`    | Add-on covers only               | `<mellow-add-ons>` alone, as a list of switches     |
| `included`   | Only included covers             | `<mellow-liability>` alone. The step needs no click |
| `none`       | Nothing                          | The step drops out                                  |

With levels or packages, the default layout puts `[part=intro]` above the compare: "What you pay at most if something happens". Under the compare, when the included level may be refused, it puts `[part=no-cover]`: one tick box, "Rent with no cover". Ticked (`data-selected`), `[part=result]` says in place what that leaves, each only when true: nothing is covered, the deposit, and that the price does not change. `[part=keep]`, "Keep cover", takes the cover back. The included level's option then has `data-declined` and says "Refused" in place of "Included".

### `<mellow-compare>`

The options side by side: each level, and each package that holds a cover. It is a radio group, as a booking holds one level. The included level is the first pick. Each `<mellow-option>` has the same rows in the same order, so a table on a wide screen and stacked cards on a phone come from one DOM. State: `data-empty`, `data-loading`.

::::widget-attributes{tag="mellow-compare"}
- `perils`: The perils that make the rows, in order, such as `collision theft gravel sand_ash`. By default the perils whose excess differs between the options.
- `recommend`: The code of the option to mark as the pick, such as `PREMIUM`.
- `price`: `delta` (the default) shows what each option adds to the price. `total` shows the whole pay-now with that option.
::::

Perils: `collision`, `theft`, `glass`, `tyres`, `gravel`, `sand_ash`, `scratches`, `undercarriage`, `water`, `interior`, `keys` and `other`.

### `<mellow-option>`

One level or one package: its scope. Fields: `name`, `description`, `code`, `level` and `includes` (the names of what it holds). State: `data-selected`, `data-included` (on the booking at no charge), `data-recommended`, `data-unavailable` (the driver's age needs a higher level, and the badge says so), `data-kind` (`level` or `package`), `data-level` (1 to 3) and `data-declined` (the included level, refused: no level is then picked). Default parts: `[part=badge]` and `[part=includes]`.

::::widget-attributes{tag="mellow-option"}
- `id`: The product's id or code, for an option you place yourself. The compare sets nothing here.
::::

### `<mellow-pick>`

The option's radio button. Picking the included level takes the picked one off. Picking any option ends a refusal of the included level. State: `data-selected`.

### `<mellow-coverage>`

The excess left for each peril, as a `<dl>`: under an option, its quote's liability (the levels below counted in), and under an add-on cover, the cover's own. Each row has `data-peril` and `data-state`: `full` (no excess: "Covered in full"), `reduced` (an amount) or `none` (not covered). State: `data-empty`.

Under an option the list has `[part=head]`: "If this happens" and "You pay at most", the words of the rental agreement. A row that says the same as the option before it (for the first option: not covered) has `data-same`. With `data-fold` (some rows are the same, some are not), Plain shows only the other rows where the options stack as cards, and `[part=more]`, "Show all 11" with the real count, opens the rest (`data-open`). The table on a wide step shows every row.

### `<mellow-add-ons>`

The add-on covers of the chosen car type, each a `<mellow-product>` row with its price, its Add button and its `<mellow-coverage>`. One that the picked option already holds shows `data-included` and "Included in Premium" in place of the button. State: `data-empty`.

### `<mellow-liability>`

What the booking leaves the customer to pay at most, after every cover on it, the damage handling and the deposit. It changes as the customer picks. Fields: `deposit`, `handling` (the cost for each damage on top of the excess, when a cover names one), `excess.<peril>` and `included`. State: `data-empty`, `data-loading`.

## Extras

### `<mellow-products>`

The products of the extras step for the chosen car type. A `<template>` child sets each item. The package ladder comes first, low to high: the customer holds one package level at most, and adding one takes the other off. State: `data-empty`.

::::widget-attributes{tag="mellow-products"}
- `kind`: `extra` gives the extras, `package` the packages with no cover. Both by default.
::::

### `<mellow-product>`

One product's scope. Fields: `name`, `description`, `code`, `maxQuantity`, `extraDrivers` and `includes` (a package's products, by name). The default product is one row: a `<mellow-image>` (a 44 px square of its picture, an empty square when it has none), `[part=text]` with `[part=name]`, `[part=description]` (one line) and `[part=includes]`, then `<mellow-price of="unit">` and `<mellow-quantity>`. State: `data-selected`, `data-included` (in a level or a package the customer took), `data-default` (on every booking), `data-declined`, `data-kind` and `data-level` (a package on the ladder).

::::widget-attributes{tag="mellow-product"}
- `id`: The product's id or code, for a product you place yourself. The lists set nothing here.
::::

### `<mellow-quantity>`

The product's control. The default content is an Add button (`[part=add]`). For a product taken once, or a package on the ladder, it turns to "Added ✓" (`aria-pressed="true"`), and a second press removes the product. For a product with `maxQuantity` above 1 it turns to a stepper (`[part=stepper]`: −, `[part=count]`, +), and 0 brings back Add. A product on every booking, or in a package the customer took, shows `[part=included]` in place of the control. Your own markup binds an `<input>` (a checkbox, a radio or a number box) and two buttons, one less and one more. State: `data-value`, `data-included`.

### `<mellow-decline>`

A checkbox to refuse a product that is on every booking and may be refused. It is hidden for any other product. State: `data-selected`, `data-hidden`. The default option holds none: the included level is refused by "Rent with no cover" under the compare. Refusing an included level takes a picked level off, so the booking holds no level.

## The driver

### `<mellow-driver>`

The customer form. With no child elements it holds every field the booking may take. Your own inputs with the same `name` bind instead. The details stay in the tab's `sessionStorage`, never in an address. State: `data-invalid` while a required field is empty.

| `name`                    | Required     | Meaning                                |
| ------------------------- | ------------ | -------------------------------------- |
| `customer.firstName`      | yes          |                                        |
| `customer.lastName`       | yes          |                                        |
| `customer.email`          | yes          | For the confirmation                   |
| `customer.phone`          | no           |                                        |
| `customer.country`        | no           | A list of countries, empty at first    |
| `flightNumber`            | no           | Shown when the pickup is at an airport |
| `passengers`              | no           |                                        |
| `notes`                   | no           |                                        |
| `customer.marketingOptIn` | no           | A checkbox, off by default             |
| `pickupDetail`            | when asked   | The location's own question            |
| `returnDetail`            | when asked   | The location's own question            |
| `terms`                   | with `terms` | The terms checkbox                     |

### `<mellow-field>`

One field: a `<label>`, the control and an error text in `[part=error]`. State: `data-invalid` on a refusal, `data-hidden` when the field does not apply.

::::widget-attributes{tag="mellow-field"}
- `name`: The field's name, from the table above.
- `optional`: Makes `customer.email` optional.
::::

### `<mellow-terms>`

The terms checkbox with a link to the rental's terms. It shows only when the root has `terms`, and then the customer must tick it before Pay. State: `data-hidden`, `data-invalid`.

## Pay and done

### `<mellow-pay>`

The last step. On Pay it does these things in order:

1. It runs the bot check: Cloudflare Turnstile, in a small frame on Mellow's host, inside `[part=turnstile]`. The frame has no height until Turnstile needs the customer, then it is 300 by 65 pixels.
2. It holds one car for the order, at a fixed total, for 15 minutes.
3. It asks for the payment of the pay-now amount on the hold.
4. With `pay="embedded"` the processor's card form opens inside it. With `pay="redirect"` the browser goes to the processor's page.
5. It waits until the processor's paid webhook makes the booking from the hold, and then moves to done.

No card data reaches the page's code or Mellow. A refusal, such as `none_free` when the last car went, shows in `<mellow-error>`. State: `data-state` is `ready`, `booking` (the hold), `paying`, `paid` or `failed`, and `data-test` with a test key. Default parts: `[part=review]`, `[part=secure]`, `[part=pay]`, `[part=estimate]`, `[part=status]`, `[part=processor]`, `[part=turnstile]`, and for a test key `[part=test]` and `[part=open-test]`.

The default content is a review before the payment. `[part=review]` has a row for each of these, and only when its data exists: `[part=driver]` (the name over the email, with `[part=change]`, which goes back to the driver step), `[part=term]` (the payment term the price is on: its title over its one line on cancelling and refunds) and `[part=deposit]` ("Deposit at pickup" with its amount). `[part=secure]` says that the card goes in on the payment provider's secure page. The one amount is on `[part=pay]`, the charge in the rental's currency. `[part=estimate]` shows under it while another currency is picked.

### `<mellow-confirmation>`

The booking reference, the trip, and links to manage the booking and to check in online, on the rental's own address. Fields: `reference`, `code`, `customer.firstName`, `pickupAt`, `returnAt`, `pickupLocation` and `returnLocation`. State: `data-state` is `waiting` or `booked`, and `data-test` for a test booking, which shows its `code` (such as TEST-4F2A) and no links. Default parts: `[part=thanks]`, `[part=links]`.

### `<mellow-summary>`

The live quote, beside any step from the car type on. It is empty, and hidden, until a car type is chosen. The default content stacks: `[part=car-type]`, `[part=similar]` ("or similar", when the model is not guaranteed), `[part=trip]` (`[part=pickup]` and `[part=return]`, each a `[part=place]` over a `[part=when]` such as "Thu 19 Nov · 10:00"), the lines, `[part=totals]` and `[part=tax]`, `[part=deposit]` ("Deposit at pickup", when the quote holds one), then a `<mellow-currency-note>`. The totals show only `[part=total]`, unless a fee is paid at pickup: then `[part=pay-now]` and `[part=on-arrival]` come above it. State: `data-empty`, `data-loading`.

### `<mellow-lines>`

The quote's lines. A `<template>` child sets each line. State: `data-empty`.

### `<mellow-line>`

One line of the quote: its scope. Fields: `name`, `quantity`, `kind` and `detail` (how the amount came about, such as "3 days × ISK 6,785", empty when no quantity or days apply), and `<mellow-price of="line">`. State: `data-kind` (`vehicle`, `extra`, `insurance`, `fee`, `package`, `location_fee`, `one_way_fee` or `discount`) and `data-on-arrival`. Default parts: `[part=name]`, the price, then `[part=detail]` under the name.

## Currency

### `<mellow-currency>`

A `<select>` of the currencies the customer may see prices in, with a `<mellow-currency-note>`. The charge stays in the rental's currency. It may stand outside `<mellow-booking>`, such as in the site's header: it then reads the first `<mellow-booking>` or `<mellow-search>` on the page. Your own `<select>` inside binds instead. State: `data-currency`, `data-converted` (another currency than the rental's), and `data-empty` (fewer than two to pick from: the default content is then hidden). See [Display currency](/widget/currency).

::::widget-attributes{tag="mellow-currency"}
::::

### `<mellow-currency-note>`

The note while prices show in another currency: "Prices in EUR are estimates. You pay in ISK." It is empty, with `data-empty`, and hidden while the rental's own currency shows.
