Open demo

CSS code

HTML
<section class="feed" aria-labelledby="feed-title">
  <h2 id="feed-title">Activity</h2>
  <ol>
    <li><span class="av a1" aria-hidden="true">MC</span><div class="body"><p class="line"><b>Maya Chen</b> merged <span class="obj">#482 — checkout redesign</span> into main</p><time datetime="2026-10-15T09:48">12 minutes ago</time><span class="pill">All checks passed</span></div></li>
    <li><span class="av a2" aria-hidden="true">TS</span><div class="body"><p class="line"><b>Tomás Silva</b> commented on <span class="obj">Billing migration plan</span></p><time datetime="2026-10-15T09:00">1 hour ago</time><div class="attach"><b>“One thing to double-check —”</b>the proration logic when a plan changes mid-cycle. I left a test case in the doc.</div></div></li>
    <li><span class="av a3" aria-hidden="true">AO</span><div class="body"><p class="line"><b>Amara Obi</b> uploaded <span class="obj">Q3-forecast.xlsx</span> to Finance</p><time datetime="2026-10-15T07:00">3 hours ago</time></div></li>
    <li><span class="av a4" aria-hidden="true">RK</span><div class="body"><p class="line"><b>Rui Kovač</b> closed <span class="obj">14 support tickets</span> — weekly record</p><time datetime="2026-10-14T18:42">Yesterday, 18:42</time></div></li>
  </ol>
</section>
CSS
/* Activity feed: avatars sit on a thin rail drawn by ::before; each event is a list item, newest first. */
.feed{--feed-rail:#edf0f5;--feed-text:#4a5468;--feed-strong:#1b2230;--feed-link:#2563eb;--feed-muted:#6b7385;
  box-sizing:border-box;width:100%;max-width:520px;background:#fff;border:1px solid #e7eaf0;border-radius:16px;padding:26px 28px;
  box-shadow:0 14px 32px -20px rgba(20,28,48,.18)}
.feed h2{margin:0 0 22px;font-size:1.05rem}
.feed ol{position:relative;margin:0;padding:0;list-style:none}
.feed ol::before{content:"";position:absolute;left:17px;top:8px;bottom:8px;width:2px;background:var(--feed-rail)}
.feed li{position:relative;display:flex;gap:16px;padding-bottom:26px}
.feed li:last-child{padding-bottom:0}
.feed .av{box-sizing:border-box;width:36px;height:36px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;
  font-size:.8rem;font-weight:800;color:#fff;position:relative;z-index:1;border:3px solid #fff}
.feed .a1{background:#4f46e5}.feed .a2{background:#0369a1}.feed .a3{background:#b45309}.feed .a4{background:#047857}
.feed .body{flex:1;min-width:0;padding-top:2px}
.feed .line{margin:0;font-size:.88rem;line-height:1.5;color:var(--feed-text)}
.feed .line b{color:var(--feed-strong);font-weight:700}
.feed .obj{color:var(--feed-link);font-weight:600}
.feed time{display:block;font-size:.74rem;color:var(--feed-muted);margin-top:3px}
.feed .attach{margin-top:10px;border:1px solid #e7eaf0;background:#f9fafc;border-radius:10px;padding:10px 14px;font-size:.82rem;color:var(--feed-text)}
.feed .attach b{display:block;color:var(--feed-strong);font-size:.84rem;margin-bottom:2px}
.feed .pill{display:inline-block;margin-top:10px;font-size:.72rem;font-weight:700;padding:4px 10px;border-radius:999px;background:#e8f8ee;color:#15803d}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<section class="feed card" aria-labelledby="feed-title">
  <div class="card-body">
    <h2 class="card-title fw-bold" id="feed-title">Activity</h2>
    <ol class="list-unstyled mb-0">
      <li class="d-flex"><span class="av a1 rounded-circle flex-shrink-0 d-flex align-items-center justify-content-center text-white" aria-hidden="true">MC</span><div class="body flex-grow-1"><p class="line mb-0"><b>Maya Chen</b> merged <span class="obj">#482 — checkout redesign</span> into main</p><time class="d-block" datetime="2026-10-15T09:48">12 minutes ago</time><span class="badge rounded-pill">All checks passed</span></div></li>
      <li class="d-flex"><span class="av a2 rounded-circle flex-shrink-0 d-flex align-items-center justify-content-center text-white" aria-hidden="true">TS</span><div class="body flex-grow-1"><p class="line mb-0"><b>Tomás Silva</b> commented on <span class="obj">Billing migration plan</span></p><time class="d-block" datetime="2026-10-15T09:00">1 hour ago</time><div class="attach rounded-3"><b class="d-block">“One thing to double-check —”</b>the proration logic when a plan changes mid-cycle. I left a test case in the doc.</div></div></li>
      <li class="d-flex"><span class="av a3 rounded-circle flex-shrink-0 d-flex align-items-center justify-content-center text-white" aria-hidden="true">AO</span><div class="body flex-grow-1"><p class="line mb-0"><b>Amara Obi</b> uploaded <span class="obj">Q3-forecast.xlsx</span> to Finance</p><time class="d-block" datetime="2026-10-15T07:00">3 hours ago</time></div></li>
      <li class="d-flex"><span class="av a4 rounded-circle flex-shrink-0 d-flex align-items-center justify-content-center text-white" aria-hidden="true">RK</span><div class="body flex-grow-1"><p class="line mb-0"><b>Rui Kovač</b> closed <span class="obj">14 support tickets</span> — weekly record</p><time class="d-block" datetime="2026-10-14T18:42">Yesterday, 18:42</time></div></li>
    </ol>
  </div>
</section>
CSS
/* Activity feed in a Bootstrap .card: avatars are .rounded-circle flex boxes on a rail drawn by ::before,
   the status is a .badge.rounded-pill. Card and badge are restyled through their --bs-* variables. */
.feed{--feed-rail:#edf0f5;--feed-text:#4a5468;--feed-strong:#1b2230;--feed-link:#2563eb;--feed-muted:#6b7385;
  --bs-card-border-color:#e7eaf0;--bs-card-border-radius:16px;--bs-card-spacer-y:26px;--bs-card-spacer-x:28px;--bs-card-title-spacer-y:22px;
  width:100%;max-width:520px;box-shadow:0 14px 32px -20px rgba(20,28,48,.18)}
.feed .card-title{font-size:1.05rem;line-height:normal}
.feed ol{position:relative}
.feed ol::before{content:"";position:absolute;left:17px;top:8px;bottom:8px;width:2px;background:var(--feed-rail)}
.feed li{position:relative;gap:16px;padding-bottom:26px}
.feed li:last-child{padding-bottom:0}
.feed .av{width:36px;height:36px;font-size:.8rem;font-weight:800;position:relative;z-index:1;border:3px solid #fff}
.feed .a1{background:#4f46e5}.feed .a2{background:#0369a1}.feed .a3{background:#b45309}.feed .a4{background:#047857}
.feed .body{min-width:0;padding-top:2px}
.feed .line{font-size:.88rem;line-height:1.5;color:var(--feed-text)}
.feed .line b{color:var(--feed-strong);font-weight:700}
.feed .obj{color:var(--feed-link);font-weight:600}
.feed time{font-size:.74rem;color:var(--feed-muted);margin-top:3px}
.feed .attach{margin-top:10px;border:1px solid #e7eaf0;background:#f9fafc;padding:10px 14px;font-size:.82rem;color:var(--feed-text);line-height:normal}
.feed .attach b{color:var(--feed-strong);font-size:.84rem;margin-bottom:2px;font-weight:700}
.feed .badge{--bs-badge-padding-x:10px;--bs-badge-padding-y:4px;--bs-badge-font-size:.72rem;--bs-badge-color:#15803d;line-height:normal;margin-top:10px;background:#e8f8ee}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<section aria-labelledby="feed-title" class="w-full max-w-[520px] rounded-2xl border border-[#e7eaf0] bg-white px-7 py-[26px] shadow-[0_14px_32px_-20px_rgba(20,28,48,.18)]">
  <h2 id="feed-title" class="mb-[22px] text-[1.05rem] font-bold">Activity</h2>
  <ol class="relative before:absolute before:top-2 before:bottom-2 before:left-[17px] before:w-0.5 before:bg-[#edf0f5]">
    <li class="relative flex gap-4 pb-[26px]"><span aria-hidden="true" class="relative z-[1] flex size-9 flex-none items-center justify-center rounded-full border-3 border-white bg-[#4f46e5] text-[.8rem] font-extrabold text-white">MC</span><div class="min-w-0 flex-1 pt-0.5"><p class="text-[.88rem] leading-[1.5] text-[#4a5468]"><b class="font-bold text-[#1b2230]">Maya Chen</b> merged <span class="font-semibold text-[#2563eb]">#482 — checkout redesign</span> into main</p><time datetime="2026-10-15T09:48" class="mt-[3px] block text-[.74rem] text-[#6b7385]">12 minutes ago</time><span class="mt-2.5 inline-block rounded-full bg-[#e8f8ee] px-2.5 py-1 text-[.72rem] font-bold text-[#15803d]">All checks passed</span></div></li>
    <li class="relative flex gap-4 pb-[26px]"><span aria-hidden="true" class="relative z-[1] flex size-9 flex-none items-center justify-center rounded-full border-3 border-white bg-[#0369a1] text-[.8rem] font-extrabold text-white">TS</span><div class="min-w-0 flex-1 pt-0.5"><p class="text-[.88rem] leading-[1.5] text-[#4a5468]"><b class="font-bold text-[#1b2230]">Tomás Silva</b> commented on <span class="font-semibold text-[#2563eb]">Billing migration plan</span></p><time datetime="2026-10-15T09:00" class="mt-[3px] block text-[.74rem] text-[#6b7385]">1 hour ago</time><div class="mt-2.5 rounded-[10px] border border-[#e7eaf0] bg-[#f9fafc] px-3.5 py-2.5 text-[.82rem] text-[#4a5468]"><b class="mb-0.5 block text-[.84rem] font-bold text-[#1b2230]">“One thing to double-check —”</b>the proration logic when a plan changes mid-cycle. I left a test case in the doc.</div></div></li>
    <li class="relative flex gap-4 pb-[26px]"><span aria-hidden="true" class="relative z-[1] flex size-9 flex-none items-center justify-center rounded-full border-3 border-white bg-[#b45309] text-[.8rem] font-extrabold text-white">AO</span><div class="min-w-0 flex-1 pt-0.5"><p class="text-[.88rem] leading-[1.5] text-[#4a5468]"><b class="font-bold text-[#1b2230]">Amara Obi</b> uploaded <span class="font-semibold text-[#2563eb]">Q3-forecast.xlsx</span> to Finance</p><time datetime="2026-10-15T07:00" class="mt-[3px] block text-[.74rem] text-[#6b7385]">3 hours ago</time></div></li>
    <li class="relative flex gap-4"><span aria-hidden="true" class="relative z-[1] flex size-9 flex-none items-center justify-center rounded-full border-3 border-white bg-[#047857] text-[.8rem] font-extrabold text-white">RK</span><div class="min-w-0 flex-1 pt-0.5"><p class="text-[.88rem] leading-[1.5] text-[#4a5468]"><b class="font-bold text-[#1b2230]">Rui Kovač</b> closed <span class="font-semibold text-[#2563eb]">14 support tickets</span> — weekly record</p><time datetime="2026-10-14T18:42" class="mt-[3px] block text-[.74rem] text-[#6b7385]">Yesterday, 18:42</time></div></li>
  </ol>
</section>

About this set

An activity feed in a white panel, the kind found on a dashboard home or beside a project. Four events run down a faint rail: Maya Chen merged a pull request, Tomás Silva commented on a plan, Amara Obi uploaded a spreadsheet and Rui Kovač closed fourteen tickets. Each row has a round initials avatar sitting on the rail, a sentence with the person in bold and the object in blue, and a small timestamp. Rows can carry extras: a green All checks passed pill, or a quoted comment in a light grey box. Use it for audit logs, team activity, order histories or notifications. The panel is a section labelled by its heading, the events are an ordered list newest first, and every relative time such as 12 minutes ago sits in a time element with a full ISO timestamp. The avatars are aria-hidden because the name follows them, and their colours were darkened so the white initials reach 4.5:1. The Bootstrap version uses .card, .rounded-circle, .d-flex and a .badge, and the Tailwind version uses flex, size-9 and before: utilities for the rail.

What’s included

  • Initials avatars sitting on a vertical rail
  • Optional status pill and quoted-comment box per event
  • Relative times backed by ISO datetime values
  • Avatar colours darkened for 4.5:1 white initials
  • Panel is a labelled section with an ordered list

Accessibility

The feed is a section named by its Activity heading, and the events are an ordered list. Avatars are aria-hidden so names are not read twice. Each relative time has a datetime attribute with the exact timestamp. Timestamps were darkened to #6b7385 and all text reaches 4.5:1. There are no controls or animation.

Customise it

Change the --feed-* properties on .feed for rail, text, link and muted colours, and the .a1 to .a4 backgrounds for avatars. Bootstrap also takes --bs-card-spacer-x and --bs-card-border-radius on .feed; in Tailwind edit the bg-[#…] avatar classes and before:bg-[#edf0f5].