# Pearl-Chain

Component of the SBB design system Lyne.

- Package: `@sbb-esta/lyne-elements` (Angular: `@sbb-esta/lyne-angular`)
- Structure: main element with parts; use parts only within the element they belong to
- Categories: Display
- Status: Design guidelines complete

## Links

- Page on digital.sbb.ch: https://digital.sbb.ch/en/design-system/lyne/components/pearl-chain/
- Storybook: https://lyne-elements.app.sbb.ch/?path=/docs/elements-pearl-chain--docs
- Angular (`@sbb-esta/lyne-angular`): https://lyne-angular.app.sbb.ch/angular/components/pearl-chain/overview (API: https://lyne-angular.app.sbb.ch/angular/components/pearl-chain/api)
- GitHub: https://github.com/sbb-design-systems/lyne-components

## Elements

- `sbb-pearl-chain`: It displays a chain of `sbb-pearl-chain-node` elements connected by lines, representing a journey with its stops.
  - `sbb-pearl-chain-node`, only within `sbb-pearl-chain`: Represents a station/point of interest within a `sbb-pearl-chain`.

## Overview

### What does the component do?
The pearl chain displays a journey as a chain of points connected by lines. Each point represents a stop. It shows the progress of the journey with the current position, as well as disruptions, walking sections and uncertain sections. It can be used horizontally or vertically.

### When should the component be used?
* To display the course of a connection with its stops, e.g. in a connection overview or a travel plan.
* To show travellers where they currently are on their journey.

### Rules.
* The stops are arranged in the order of the journey.
* The chain itself cannot be read by screen readers. Progress, disruptions and walking sections must therefore also be provided as text, e.g. hidden for screen readers.

## Implementation (web components)

The `<sbb-pearl-chain>` component visualizes a journey as a chain of points connected by lines, typically used to
display the stops of a public-transport connection along with its progress.

### Positioning nodes

Place a `<sbb-pearl-chain-node>` for every stop of the journey inside the `<sbb-pearl-chain>`, in the order they
occur. `<sbb-pearl-chain>` connects them with lines automatically, following the DOM order.

Consumers can slot the nodes alongside any other content (times, station names, icons, ...) and lay everything out
however they prefer (e.g. table, flexbox, css-grid). The `<sbb-pearl-chain>` will draw the lines that connect the nodes.

```html

<sbb-pearl-chain>
  <div style="display: flex;">
    <sbb-pearl-chain-node type="start"></sbb-pearl-chain-node>
    <span style="flex: 1;"></span>
    <sbb-pearl-chain-node type="stop"></sbb-pearl-chain-node>
    <span style="flex: 1;"></span>
    <sbb-pearl-chain-node type="end"></sbb-pearl-chain-node>
  </div>
</sbb-pearl-chain>
```

```html

<sbb-pearl-chain>
  <table>
    <tr>
      <td>
        <time datetime="2026-04-10T10:00:00">
          <span class="sbb-screen-reader-only">Departure:&nbsp;</span>10:00
        </time>
      </td>
      <td>
        <sbb-pearl-chain-node type="start" departure="..."></sbb-pearl-chain-node>
      </td>
      <td>Bern</td>
    </tr>
    <tr>
      <td>
        <time datetime="2026-04-10T12:00:00">
          <span class="sbb-screen-reader-only">Departure:&nbsp;</span>12:00
        </time>
      </td>
      <td>
        <sbb-pearl-chain-node type="stop" arrival="..." departure="..."></sbb-pearl-chain-node>
      </td>
      <td>Olten</td>
    </tr>
    <tr>
      <td>
        <time datetime="2026-04-10T15:00:00">
          <span class="sbb-screen-reader-only">Arrival:&nbsp;</span>15:00
        </time>
      </td>
      <td>
        <sbb-pearl-chain-node type="end" arrival="..."></sbb-pearl-chain-node>
      </td>
      <td>Zürich HB</td>
    </tr>
  </table>
</sbb-pearl-chain>
```

### Now / progress

Setting `arrival`/`departure` on the `<sbb-pearl-chain-node>` elements allows the `<sbb-pearl-chain>` to display the current
progress of the journey with a pulsing dot marking the current position.
For testing or demo purposes, the current time can be overridden using the `now` property of `<sbb-pearl-chain>`.

```html
<sbb-pearl-chain>
  <sbb-pearl-chain-node type="start" departure="2026-04-10T10:30:00"></sbb-pearl-chain-node>
  <sbb-pearl-chain-node
    type="stop"
    arrival="2026-04-10T10:50:00"
    departure="2026-04-10T10:55:00"
  ></sbb-pearl-chain-node>
  <sbb-pearl-chain-node type="end" arrival="2026-04-10T11:30:00"></sbb-pearl-chain-node>
</sbb-pearl-chain>
```

### Orientation

`<sbb-pearl-chain>` adapts its bullets depending on whether the nodes are laid out horizontally or vertically, so it looks correct in both horizontal and vertical layouts without any explicit configuration.

### Rendering

`<sbb-pearl-chain>` re-renders automatically when:

- a `<sbb-pearl-chain-node>` is added or removed;
- the pearl-chain element is resized (e.g. on window resize);
- the `IntersectionObserver` triggers, e.g. when the pearl-chain becomes visible.

Changes that are not detected automatically (e.g. a layout change caused by a CSS property) require a manual update.
In this case, call the `requestUpdate()` method on the `<sbb-pearl-chain>` element.

```js
document.querySelector('sbb-pearl-chain').requestUpdate();
```

### Utility

#### Node weights

The `<sbb-pearl-chain>` does not position the nodes: distributing them proportionally to the journey's duration is up to the consumer.
The `calculateNodeWeights()` method helps with that: it returns, for each node in DOM order, a value between `0` (first node) and `1` (last node),
proportional to its `departure` time (or `arrival`, if there is no `departure`).

```js
const chain = document.querySelector('sbb-pearl-chain');
const weights = chain.calculateNodeWeights(); // e.g. [0, 0.43, 1]
```

### Accessibility

`<sbb-pearl-chain>` and `<sbb-pearl-chain-node>` have **no built-in accessibility**: the rendered lines and bullets
are purely decorative and not exposed to assistive technology. If the information conveyed by the pearl chain
(e.g. progress, disruptions) needs to be accessible, it must be provided separately, through accessible markup e.g. via hidden text.

## API (web components)

#### class: `SbbPearlChainElement`, `sbb-pearl-chain`

##### Properties

| Name    | Attribute | Privacy | Type                                  | Default | Description                                                                                                               |
| ------- | --------- | ------- | ------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `nodes` | -         | public  | `readonly SbbPearlChainNodeElement[]` | `[]`    | Returns the registered `sbb-pearl-chain-node` elements, in DOM order.                                                     |
| `now`   | `now`     | public  | `Date`                                |         | A configured date which acts as the current date instead of the real current date. Only recommended for testing purposes. |

##### Methods

| Name                   | Privacy | Description                                                                                                                                                                          | Parameters | Return     | Inherited From |
| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ---------- | -------------- |
| `calculateNodeWeights` | public  | Returns the relative position (0 for the first node, 1 for the last one) of each node, in DOM order, proportional to its `departure` time (or `arrival` if there is no `departure`). |            | `number[]` |                |

#### class: `SbbPearlChainNodeElement`, `sbb-pearl-chain-node`

##### Properties

| Name         | Attribute    | Privacy | Type                                                                                                                                                                                                                                                                        | Default | Description                                                                                   |
| ------------ | ------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------- |
| `arrival`    | `arrival`    | public  | `Date \| null`                                                                                                                                                                                                                                                              | `null`  | The arrival date/time at this node. Accepts ISO 8601 datetime strings.                        |
| `departure`  | `departure`  | public  | `Date \| null`                                                                                                                                                                                                                                                              | `null`  | The departure date/time at this node. Accepts ISO 8601 datetime strings.                      |
| `disrupted`  | `disrupted`  | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the node as disrupted, coloring the bullet red (if rendered).                           |
| `irrelevant` | `irrelevant` | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the node as irrelevant, coloring the bullet gray (if rendered); supersedes `disrupted`. |
| `type`       | `type`       | public  | `'start' \| 'end' \| 'stop' \| 'skip' \| 'commercial' \| 'boarding' \| 'alighting' \| 'exceptional' \| 'exceptional-skip' \| 'exceptional-boarding' \| 'exceptional-alighting' \| 'stop-duty' \| 'stop-on-demand' \| 'boarding-on-demand' \| 'alighting-on-demand' \| null` | `null`  | Decides how the bullet is rendered. Defaults to \_empty\_ (no bullet).                        |
| `unsure`     | `unsure`     | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the connection as unsure, rendering the line dashed. Does not affect the bullet.        |
| `walk`       | `walk`       | public  | `'arrival' \| 'departure' \| true \| null`                                                                                                                                                                                                                                  | `null`  | Marks the node as a walk connection. Does not affect the bullet color.                        |
