32 CSS Timelines10 / 32

Pure CSSMIT licensed

Concert Tour Timeline — Ticket Stub Cards with Perforated Edges

Every event a ticket: tour dates rendered as stub cards with real punched notches (radial-gradient masks), perforated tear lines, rotated date tabs and a sold-out stamp — the textured timeline for concert schedules, festival lineups, event series and travel itineraries.

Published Updated

Live Demo
Try it

The code

<section class="tls-10" aria-label="Tour dates timeline demo">
  <div class="tls-10__wrap">
    <h3 class="tls-10__h">The Static Bloom — North America ’26</h3>
    <ol class="tls-10__list" role="list">
      <li class="tls-10__row">
        <span class="tls-10__node" aria-hidden="true"></span>
        <article class="tls-10__stub">
          <div class="tls-10__tab" aria-hidden="true">JUL</div>
          <div class="tls-10__main">
            <time class="tls-10__date" datetime="2026-07-24">Fri, Jul 24</time>
            <p class="tls-10__venue">Kings Theatre</p>
            <p class="tls-10__city">Brooklyn, NY</p>
          </div>
          <div class="tls-10__tear">
            <span class="tls-10__seat">GA · Doors 7pm</span>
            <span class="tls-10__price">$45</span>
          </div>
        </article>
      </li>
      <li class="tls-10__row">
        <span class="tls-10__node" aria-hidden="true"></span>
        <article class="tls-10__stub" data-status="soldout">
          <div class="tls-10__tab" aria-hidden="true">JUL</div>
          <div class="tls-10__main">
            <time class="tls-10__date" datetime="2026-07-27">Mon, Jul 27</time>
            <p class="tls-10__venue">The Anthem</p>
            <p class="tls-10__city">Washington, DC</p>
            <p class="tls-10__status">Sold out</p>
          </div>
          <div class="tls-10__tear">
            <span class="tls-10__seat">GA · Doors 7pm</span>
            <span class="tls-10__price">$45</span>
          </div>
        </article>
      </li>
      <li class="tls-10__row">
        <span class="tls-10__node" aria-hidden="true"></span>
        <article class="tls-10__stub">
          <div class="tls-10__tab" aria-hidden="true">AUG</div>
          <div class="tls-10__main">
            <time class="tls-10__date" datetime="2026-08-02">Sun, Aug 2</time>
            <p class="tls-10__venue">Thalia Hall</p>
            <p class="tls-10__city">Chicago, IL</p>
          </div>
          <div class="tls-10__tear">
            <span class="tls-10__seat">Balcony · Doors 6:30pm</span>
            <span class="tls-10__price">$52</span>
          </div>
        </article>
      </li>
    </ol>
  </div>
</section>
.tls-10,
.tls-10 *,
.tls-10 *::before,
.tls-10 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-10 {
  min-height: 100vh;
  width: 100%;
  --tl-paper: #fff8ec;
  --tl-accent: #b91c1c;
  --tl-ink: #292018;
  --tl-mut: #95866f;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: #1c1917 radial-gradient(1200px 400px at 50% -10%,#33291f,transparent);
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

.tls-10__wrap {
  width: min(560px,100%);
}

.tls-10__h {
  font-family: Georgia,serif;
  font-size: clamp(20px,3.4vw,27px);
  font-weight: 700;
  color: #f5efe4;
  letter-spacing: -.01em;
  margin-bottom: 26px;
}

.tls-10__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.tls-10__row {
  position: relative;
  padding-left: 34px;
}

.tls-10__row::before {
  content: '';
  position: absolute;
  left: 8px;
  top: -10px;
  bottom: -10px;
  width: 2px;
  background: repeating-linear-gradient(180deg,#57493a 0 6px,transparent 6px 12px);
}

.tls-10__row:first-child::before {
  top: 26px;
}

.tls-10__row:last-child::before {
  bottom: calc(100% - 26px);
}

.tls-10__node {
  position: absolute;
  left: 3.5px;
  top: 22px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--tl-accent);
  box-shadow: 0 0 0 4px #1c1917;
}

.tls-10__stub {
  display: flex;
  align-items: stretch;
  background: var(--tl-paper);
  border-radius: 10px;
  overflow: hidden;
  transition: transform .18s,box-shadow .18s;
  -webkit-mask: radial-gradient(9px at left 71%,transparent 97%,#000) left / 51% 100% no-repeat,radial-gradient(9px at right 71%,transparent 97%,#000) right / 51% 100% no-repeat;
  mask: radial-gradient(9px at left 71%,transparent 97%,#000) left / 51% 100% no-repeat,radial-gradient(9px at right 71%,transparent 97%,#000) right / 51% 100% no-repeat;
}

.tls-10__stub:hover {
  transform: translateY(-3px) rotate(-.4deg);
}

.tls-10__tab {
  flex: none;
  width: 38px;
  display: grid;
  place-items: center;
  writing-mode: vertical-rl;
  rotate: 180deg;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .3em;
  color: var(--tl-paper);
  background: var(--tl-accent);
}

.tls-10__main {
  flex: 1;
  padding: 14px 18px;
  position: relative;
}

.tls-10__date {
  display: block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--tl-accent);
}

.tls-10__venue {
  font-family: Georgia,serif;
  font-size: 19px;
  font-weight: 700;
  color: var(--tl-ink);
  margin-top: 3px;
}

.tls-10__city {
  font-size: 12.5px;
  color: var(--tl-mut);
  margin-top: 2px;
}

.tls-10__status {
  position: absolute;
  right: 10px;
  top: 12px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--tl-accent);
  border: 2px solid var(--tl-accent);
  border-radius: 4px;
  padding: 3px 8px;
  rotate: -8deg;
  opacity: .85;
}

.tls-10__stub[data-status="soldout"] .tls-10__main {
  opacity: .75;
}

.tls-10__stub[data-status="soldout"] {
  filter: saturate(.55);
}

.tls-10__tear {
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 12px 16px;
  border-left: 2px dashed #d9c9a8;
  text-align: right;
}

.tls-10__seat {
  font-size: 10.5px;
  color: var(--tl-mut);
}

.tls-10__price {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 16px;
  font-weight: 700;
  color: var(--tl-ink);
}

@media (prefers-reduced-motion: reduce) {
  .tls-10__stub {
    transition: none;
  }
}
/* No JavaScript — the punched notches are a two-part radial-gradient mask (works over any page background); perforation is a dashed border; the month tab is writing-mode:vertical-rl. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Timeline from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Concert Tour Timeline — Ticket Stub Cards with Perforated Edges
Source: https://codefronts.com/layouts/css-timelines/tour-stub-timeline/

Every event a ticket: tour dates rendered as stub cards with real punched notches (radial-gradient masks), perforated tear lines, rotated date tabs and a sold-out stamp — the textured timeline for concert schedules, festival lineups, event series and travel itineraries.
## HTML
```html
<section class="tls-10" aria-label="Tour dates timeline demo">
  <div class="tls-10__wrap">
    <h3 class="tls-10__h">The Static Bloom — North America ’26</h3>
    <ol class="tls-10__list" role="list">
      <li class="tls-10__row">
        <span class="tls-10__node" aria-hidden="true"></span>
        <article class="tls-10__stub">
          <div class="tls-10__tab" aria-hidden="true">JUL</div>
          <div class="tls-10__main">
            <time class="tls-10__date" datetime="2026-07-24">Fri, Jul 24</time>
            <p class="tls-10__venue">Kings Theatre</p>
            <p class="tls-10__city">Brooklyn, NY</p>
          </div>
          <div class="tls-10__tear">
            <span class="tls-10__seat">GA · Doors 7pm</span>
            <span class="tls-10__price">$45</span>
          </div>
        </article>
      </li>
      <li class="tls-10__row">
        <span class="tls-10__node" aria-hidden="true"></span>
        <article class="tls-10__stub" data-status="soldout">
          <div class="tls-10__tab" aria-hidden="true">JUL</div>
          <div class="tls-10__main">
            <time class="tls-10__date" datetime="2026-07-27">Mon, Jul 27</time>
            <p class="tls-10__venue">The Anthem</p>
            <p class="tls-10__city">Washington, DC</p>
            <p class="tls-10__status">Sold out</p>
          </div>
          <div class="tls-10__tear">
            <span class="tls-10__seat">GA · Doors 7pm</span>
            <span class="tls-10__price">$45</span>
          </div>
        </article>
      </li>
      <li class="tls-10__row">
        <span class="tls-10__node" aria-hidden="true"></span>
        <article class="tls-10__stub">
          <div class="tls-10__tab" aria-hidden="true">AUG</div>
          <div class="tls-10__main">
            <time class="tls-10__date" datetime="2026-08-02">Sun, Aug 2</time>
            <p class="tls-10__venue">Thalia Hall</p>
            <p class="tls-10__city">Chicago, IL</p>
          </div>
          <div class="tls-10__tear">
            <span class="tls-10__seat">Balcony · Doors 6:30pm</span>
            <span class="tls-10__price">$52</span>
          </div>
        </article>
      </li>
    </ol>
  </div>
</section>
```
## CSS
```css
.tls-10,
.tls-10 *,
.tls-10 *::before,
.tls-10 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tls-10 {
  min-height: 100vh;
  width: 100%;
  --tl-paper: #fff8ec;
  --tl-accent: #b91c1c;
  --tl-ink: #292018;
  --tl-mut: #95866f;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: #1c1917 radial-gradient(1200px 400px at 50% -10%,#33291f,transparent);
  padding: 56px 24px;
  display: grid;
  place-items: center;
}

.tls-10__wrap {
  width: min(560px,100%);
}

.tls-10__h {
  font-family: Georgia,serif;
  font-size: clamp(20px,3.4vw,27px);
  font-weight: 700;
  color: #f5efe4;
  letter-spacing: -.01em;
  margin-bottom: 26px;
}

.tls-10__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.tls-10__row {
  position: relative;
  padding-left: 34px;
}

.tls-10__row::before {
  content: '';
  position: absolute;
  left: 8px;
  top: -10px;
  bottom: -10px;
  width: 2px;
  background: repeating-linear-gradient(180deg,#57493a 0 6px,transparent 6px 12px);
}

.tls-10__row:first-child::before {
  top: 26px;
}

.tls-10__row:last-child::before {
  bottom: calc(100% - 26px);
}

.tls-10__node {
  position: absolute;
  left: 3.5px;
  top: 22px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--tl-accent);
  box-shadow: 0 0 0 4px #1c1917;
}

.tls-10__stub {
  display: flex;
  align-items: stretch;
  background: var(--tl-paper);
  border-radius: 10px;
  overflow: hidden;
  transition: transform .18s,box-shadow .18s;
  -webkit-mask: radial-gradient(9px at left 71%,transparent 97%,#000) left / 51% 100% no-repeat,radial-gradient(9px at right 71%,transparent 97%,#000) right / 51% 100% no-repeat;
  mask: radial-gradient(9px at left 71%,transparent 97%,#000) left / 51% 100% no-repeat,radial-gradient(9px at right 71%,transparent 97%,#000) right / 51% 100% no-repeat;
}

.tls-10__stub:hover {
  transform: translateY(-3px) rotate(-.4deg);
}

.tls-10__tab {
  flex: none;
  width: 38px;
  display: grid;
  place-items: center;
  writing-mode: vertical-rl;
  rotate: 180deg;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .3em;
  color: var(--tl-paper);
  background: var(--tl-accent);
}

.tls-10__main {
  flex: 1;
  padding: 14px 18px;
  position: relative;
}

.tls-10__date {
  display: block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--tl-accent);
}

.tls-10__venue {
  font-family: Georgia,serif;
  font-size: 19px;
  font-weight: 700;
  color: var(--tl-ink);
  margin-top: 3px;
}

.tls-10__city {
  font-size: 12.5px;
  color: var(--tl-mut);
  margin-top: 2px;
}

.tls-10__status {
  position: absolute;
  right: 10px;
  top: 12px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--tl-accent);
  border: 2px solid var(--tl-accent);
  border-radius: 4px;
  padding: 3px 8px;
  rotate: -8deg;
  opacity: .85;
}

.tls-10__stub[data-status="soldout"] .tls-10__main {
  opacity: .75;
}

.tls-10__stub[data-status="soldout"] {
  filter: saturate(.55);
}

.tls-10__tear {
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 12px 16px;
  border-left: 2px dashed #d9c9a8;
  text-align: right;
}

.tls-10__seat {
  font-size: 10.5px;
  color: var(--tl-mut);
}

.tls-10__price {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 16px;
  font-weight: 700;
  color: var(--tl-ink);
}

@media (prefers-reduced-motion: reduce) {
  .tls-10__stub {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — the punched notches are a two-part radial-gradient mask (works over any page background); perforation is a dashed border; the month tab is writing-mode:vertical-rl. */
```

How this works

The notches that make a ticket read as a ticket are a CSS mask: mask: radial-gradient(9px at left center, transparent 97%, #000) left / 51% 100% no-repeat, radial-gradient(9px at right center, transparent 97%, #000) right / 51% 100% no-repeat — two half-masks, each cutting a semicircle out of its edge. Because it's a mask (not a background trick), the cut works over ANY page background, including gradients and images.

The perforation is a dashed border on the stub's tear line, and the date tab uses writing-mode: vertical-rl so the day sits rotated like a real stub's tear-off. Each ticket is an <li> on the standard left-rail spine — the ticket is the event card; the timeline semantics stay an ol/li + time structure underneath the costume.

The sold-out state is a data-attribute: data-status="soldout" desaturates the stub and rubber-stamps 'SOLD OUT' via a rotated, bordered ::after — state text that's real content (announced), styled like a stamp.

Techniques used: mask:radial-gradient edge notches (background-independent cutouts) · dashed-border perforation + vertical-rl date tab · data-status stamp styling with a rotated ::after chip · ol/li + time semantics under the visual costume · left-rail spine with venue dots · :hover lift with composed child transform

Make it yours

  • Notch size: the 9px radius in both mask gradients — scale with card size.
  • Stamp text: the data-status attr styles the stub; the visible 'Sold out' text is real markup you can localize.
  • Palette: one --tl-paper and one --tl-accent recolor every stub; try cream + red for vintage, black + neon for club listings.
  • Make stubs links: wrap the card in an <a> — the whole stub becomes the target, and :focus-visible ring is already styled.

Gotchas — read before shipping

  • The two half-masks must overlap (51% each) or a 1px un-masked seam appears at center on fractional widths.
  • Don't fake notches with background-colored circles — they break the moment the section background changes (masks don't).
  • writing-mode tabs need explicit width; without it some browsers collapse the tab and the stub layout jumps.
  • Keep the stamp on ::after but the status as visible text too — a purely visual 'sold out' fails screen-reader users buying tickets.

Browser support

ChromeSafariFirefoxEdge
all15.4+allall

CSS mask shorthand is baseline since 2023 (webkit-prefixed pairs included for older Safari); writing-mode is universal.

Techniques used in this demo

Search CodeFronts

Loading…