# Timeline

Use the timeline shortcode to show items ordered on a vertical timeline.

## Overview

Added in v0.15.3

Use the timeline shortcode to show items ordered on a vertical timeline.

```markdown
{{< timeline data="timeline-sample" background="body-tertiary" >}}
```

## Data format

Define a file in the `data` folder that contains the timeline data. The format supports the following attributes:

| Attribute | Required | Description                                                                                  |
|-----------|----------|----------------------------------------------------------------------------------------------|
| title     | Yes      | Required title of the timeline element.                                                      |
| icon      | Yes      | Required class and name of a Font Awesome icon to include. The icons use shorthand notation. |
| color     | No       | Optional theme color of the timeline element, defaults to `primary`.                         |
| date      | No       | Optional date of the timeline element, placed below the title.                               |
| badge     | No       | Optional label of a pill badge placed next to the title.                                     |
| url       | No       | Optional url of the timeline element, added as link to the title when set[^1].               |
| content   | No       | Optional content of the timeline element, supports markdown.                                 |

[^1]: The url is joined with the `release` attribute of the documentation configuration, unless the url is absolute (e.g. starts with `http`).

The following snippet defines a single timeline element in `yml` format.

```yml
- title: Product launch
  icon: rocket
  color: primary
  date: 2023-07-03
  badge: v0.15.3
  url: https://github.com/gethinode/hinode/releases/tag/v0.15.3
  content:
    Lorem ipsum dolor sit amet, consectetur adipisicing elit.
```

## Styling

The file `assets/scss/components/_timeline.scss` defines the Hinode-specific styling of the `timeline` shortcode.

```scss
// scss-docs-start timeline
$connector-radius: 0.8rem;
$semi-circle-radius: 8rem;
$semi-circle-border: 0.2rem;

:root {
    --timeline-highlight: var(--bs-primary);
    --timeline-icon-radius: #{$semi-circle-radius};
    --timeline-offset: 50%;
    --timeline-connector-bg: var(--bs-body-bg);
}

@each $state in map-keys($theme-colors) {
    .timeline-#{$state} {
        --timeline-highlight: var(--#{$prefix}#{$state});
    }

    .timeline-bg-#{$state} {
        --timeline-connector-bg: var(--#{$prefix}#{$state});
    }

    .timeline-bg-#{$state}-subtle {
        --timeline-connector-bg: var(--#{$prefix}#{$state}-bg-subtle);
    }
}

// scss-docs-end timeline

.timeline-container {
    border-radius: #{$theme-border-radius}; 
}

.timeline, .timeline-sm {
    position: relative;
}

.timeline-sm {
    --timeline-icon-radius: calc(#{$semi-circle-radius} / 2.4);
    --timeline-offset: 25%
}

@include media-breakpoint-up(sm) {
    .timeline-sm {
        --timeline-icon-radius: calc(#{$semi-circle-radius} / 2);
    }
}
  
.timeline::before, .timeline-sm::before {
    content: "";
    width: 2 * $semi-circle-border;
    margin: 0 auto;
    background: var(--bs-body-color);
    position: absolute;
    inset: 0;
}

.timeline-sm::before {
    margin: 0 calc(var(--timeline-offset) - #{$semi-circle-border});
}

.timeline-semi-circle-start, .timeline-semi-circle-end {
    width: var(--timeline-icon-radius);
    height: var(--timeline-icon-radius);
    border-radius: 100%;
    position: relative;
}

.timeline-semi-circle-start {
    left: -$semi-circle-border;
}

.timeline-semi-circle-end {
    left: $semi-circle-border;
}

.timeline-semi-circle-start::before {
    content: "";
    width: var(--timeline-icon-radius);
    height: var(--timeline-icon-radius);
    border-radius: 100%;
    border: $semi-circle-border solid;
    position: absolute;
    border-color: transparent var(--timeline-highlight) var(--timeline-highlight) var(--timeline-highlight);
    transform: rotate(-90deg);
}

.timeline-semi-circle-start::after {
    content: "";
    left: 0;
    top: 0;
    width: var(--timeline-icon-radius);
    height: var(--timeline-icon-radius);
    border-radius: 100%;
    border: $semi-circle-border solid;
    position: absolute;
    border-color: var(--timeline-highlight) var(--timeline-highlight) var(--timeline-highlight) transparent;
    transform: rotate(-45deg);
}

.timeline-semi-circle-end::before {
    content: "";
    width: var(--timeline-icon-radius);
    height: var(--timeline-icon-radius);
    border-radius: 100%;
    border: $semi-circle-border solid;
    position: absolute;
    border-color: transparent var(--timeline-highlight) var(--timeline-highlight) var(--timeline-highlight);
    transform: rotate(-225deg);
}

.timeline-semi-circle-end::after {
    content: "";
    left: 0;
    top: 0;
    width: var(--timeline-icon-radius);
    height: var(--timeline-icon-radius);
    border-radius: 100%;
    border: $semi-circle-border solid;
    position: absolute;
    border-color: var(--timeline-highlight) var(--timeline-highlight) var(--timeline-highlight) transparent;
    transform: rotate(-180deg);
}

.timeline-description-text-start {
    border-bottom: $semi-circle-border solid var(--timeline-highlight);
    margin-right: 2 * $connector-radius;
}

.timeline-description-text-end {
    border-bottom: $semi-circle-border solid var(--timeline-highlight);
    margin-left: 2 * $connector-radius;
}

.timeline-panel-start, .timeline-panel-end, .timeline-connector-start, .timeline-connector-end {
    top: calc(var(--timeline-icon-radius) / 2);
    position: relative;
    width: calc(var(--timeline-icon-radius) / 2);
    height: calc($semi-circle-border + var(--timeline-icon-radius) / 2);
    border: $semi-circle-border solid var(--timeline-highlight);
    border-bottom: none;
    border-right: none;
    border-left: none;
}

.timeline-panel-start {
    top: 50%;
    right: 50%;
    position: absolute;
    width: calc(var(--timeline-icon-radius) * 2);
    width: 2 * $connector-radius;
    height: 50%;
    border-top: $semi-circle-border solid var(--timeline-highlight);
    border-left: $semi-circle-border solid var(--timeline-highlight);
}

.timeline-panel-end {
    top: 50%;
    position: absolute;
    width: calc(var(--timeline-icon-radius) * 2);
    width: 2 * $connector-radius;
    height: 50%;
    border-top: $semi-circle-border solid var(--timeline-highlight);
    border-right: $semi-circle-border solid var(--timeline-highlight);
}

.timeline-dot::after {
    display: inline-block;
    content: "";
    position: absolute;
    top: 50%;
    left: var(--timeline-offset);
    margin: (-$connector-radius) 0 0 (-$connector-radius);
    width: 2 * $connector-radius;
    height: 2 * $connector-radius;
    border-radius: 100%;
    border: ($semi-circle-border * 1.5) solid var(--timeline-connector-bg);
    color: var(--timeline-highlight);
    background: var(--timeline-highlight);
}
```

## Arguments

The shortcode supports the following arguments:

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `background` | background |  |  | Background style of the section. |
| `class` | string |  |  | Class attributes of the element. It supports Bootstrap attributes to modify the styling of the element. |
| `data` | string | yes |  | Path of the input data relative to the site's data folder. Supported data formats include `JSON`, `TOML`, `YAML`, and `XML`. You can omit the file extension. |
| `justify` | select |  | `start` | Justification of the child elements. Supported values: [`start`, `end`, `center`, `between`, `around`, `evenly`]. |

