Tucano v0.37.2

Timeline

History in order: the progress of an order, what happened to a contract, the versions of a changelog. It's just classes on <ol> — the order is part of the meaning, and the screen reader announces "item 2 of 5" without anyone writing ARIA. The dot and the rail are drawn by the class; the template only brings the content.

  1. Order confirmed

    Payment approved via Pix.
  2. Picked at the warehouse

  3. In transit

    Left the Campinas distribution center.
  4. Out for delivery

  5. Delivered

    Expected: 12/09/2026.

What already happened has a filled dot in the success tone; the current step, filled in the accent; what's still ahead stays hollow and neutral. The dot tells the state — the text is free to say what and when.

Examples

Everything below is the same list, with tone, fill, icon and badge swapped.

Contract history, with icons

tuc-timeline__icon as the item's first child puts the icon in place of the dot.

  1. Contract signed

    Both parties signed through the portal.
  2. Sent for signature

  3. Clause 4 revised

    Late fee went from 2% to 1%.
  4. Draft created

Audit log, with badge

A tuc-badge in the header aligns to the center of the row.

  1. Permission removed

    Access
    Ana Lima removed Rafael Souza's access to finance.
  2. Sign-in from a new device

    Session
    Chrome on macOS, São Paulo.
  3. Report exported

    Data

The five tones, hollow

Without is-filled the dot is a ring in the tone, over the background.

  1. is-accent

  2. is-success

  3. is-warning

  4. is-danger

  5. is-info

  6. no tone

The five tones, filled

is-filled fills the dot with the item's tone.

  1. is-accent is-filled

  2. is-success is-filled

  3. is-warning is-filled

  4. is-danger is-filled

  5. is-info is-filled

  6. is-filled, no tone

How to use

There's no JavaScript and no data-* attribute: write the classes and you're done. Each item has a header with title and time, and an optional body.

<ol class="tuc-timeline">
  <li class="tuc-timeline__item is-success is-filled">
    <div class="tuc-timeline__head">
      <p class="tuc-timeline__title">Contract signed</p>
      <time class="tuc-timeline__time" datetime="2026-09-12">12/09/2026</time>
    </div>
    <div class="tuc-timeline__body">Both parties signed.</div>
  </li>
  <li class="tuc-timeline__item">
    <div class="tuc-timeline__head">
      <p class="tuc-timeline__title">Sent for signature</p>
      <time class="tuc-timeline__time" datetime="2026-09-10">10/09/2026</time>
    </div>
  </li>
</ol>

With an icon

The icon goes in a <span> as the item's first child. When it's there, the dot goes away and the circle gets the tone's soft background; the SVG is sized by the class.

<li class="tuc-timeline__item is-success">
  <span class="tuc-timeline__icon"><svg viewBox="0 0 24 24" aria-hidden="true">...</svg></span>
  <div class="tuc-timeline__head">...</div>
</li>

In the Django template

The tone usually comes from the event type. A dictionary on the model, or a filter, avoids a cascade of if in the template.

class Event(models.Model):
    TONES = {"signed": "is-success", "revised": "is-warning", "canceled": "is-danger"}
    kind = models.CharField(max_length=20)
    title = models.CharField(max_length=120)
    created_at = models.DateTimeField(auto_now_add=True)

    @property
    def tone(self):
        return self.TONES.get(self.kind, "")
<ol class="tuc-timeline">
  {% for event in contract.events.all %}
  <li class="tuc-timeline__item {{ event.tone }}{% if forloop.first %} is-filled{% endif %}">
    <div class="tuc-timeline__head">
      <p class="tuc-timeline__title">{{ event.title }}</p>
      <time class="tuc-timeline__time" datetime="{{ event.created_at|date:'c' }}">{{ event.created_at|date:"d/m/Y H:i" }}</time>
    </div>
  </li>
  {% endfor %}
</ol>

Adjusting the geometry

Three variables on the list itself control the math for the dot and the rail. The first line of the header has a fixed height for the same reason as .tuc-choice: the center of the dot can't depend on the project's font.

.timeline-large {
  --tuc-timeline-gutter: 2rem;   /* dot column; the icon fills all of it */
  --tuc-timeline-dot: 1rem;      /* dot diameter */
  --tuc-timeline-line: 2rem;     /* height of the header's first line */
}

Accessibility

The structure does the work. <ol> gives each item's position, <time datetime> gives the date in a machine-readable format, and the dot and rail are pseudo-elements, invisible to the screen reader.

Color can't be the only information

The dot's tone doesn't reach people who can't see it and confuses people who can't tell colors apart. State it in the text — "Canceled", "In transit" — or in a tuc-badge in the header, and mark the icon's SVG with aria-hidden="true".

Classes

Classes only, no JavaScript component.

ClassWhereWhat it does
tuc-timeline<ol>The list; removes marker and indentation
tuc-timeline__item<li>An event; draws dot and rail. The last item has no rail
is-accent is-success is-warning is-danger is-infoitemTone of the dot and icon; without a tone, --tuc-subtle
is-filleditemFilled dot in the tone; without it, hollow
tuc-timeline__iconitem's first childIcon in place of the dot, with the tone's soft background
tuc-timeline__headitemRow for the title, the time and a badge; wraps when it doesn't fit
tuc-timeline__titlein the headerTitle in weight 600, no margin — can be a <p> or an <h2>/<h3>
tuc-timeline__timein the headerDate or time, smaller, in a muted tone and with fixed-width digits
tuc-timeline__bodyitemSupporting text in a muted tone
VariableDefaultControls
--tuc-timeline-gutter1.5remWidth of the dot column and size of the icon
--tuc-timeline-dot0.75remDot diameter
--tuc-timeline-line1.5remHeight of the header's first line, where the dot is centered