Hi. You can save position of each year and then draw lines between them:
#let get-margin(side) = {
if page.margin == auto or page.margin.at(repr(side)) == auto {
calc.min(page.height, page.width) * 2.5 / 21
} else {
page.margin.at(repr(side))
}
}
#let timeline(year) = [#year#metadata(none)<timeline>]
#show table: it => context {
let selector = here => selector(<timeline>).before(here)
let init-marks = query(selector(here()))
let before = here()
it
context {
let marks = query(selector(here()))
if init-marks.len() == marks.len() { return }
let selector = std.selector(<timeline>).after(before).before(here())
let marks = query(selector).map(x => x.location().position())
let left-margin = get-margin(left)
let top-margin = get-margin(top)
let year-width = measure[2000].width
for (a, b) in marks.windows(2) {
place(
top + left,
dx: a.x - left-margin + year-width / 2,
dy: a.y - top-margin,
line(length: b.y - a.y - 0.6em, stroke: green + 1mm, angle: 90deg),
)
}
}
}
#set text(size: 12pt)
#table(
columns: (1fr, 10fr),
stroke: none,
timeline[2025], lorem(100),
timeline[2024], lorem(50),
timeline[2024], lorem(20),
)
#table(
columns: (1fr, 10fr),
stroke: none,
timeline[1999], lorem(10),
timeline[2010], lorem(10),
timeline[2077], lorem(10),
)
The get-margin is used from How to right annotate a span of list items with a brace and text? - #2 by Andrew.
