Open demo

CSS code

HTML
<div class="tcard">
  <div class="tscroll" role="region" tabindex="0" aria-label="Sessions by channel, scrollable">
  <table class="tbl">
    <caption class="sr-only">Sessions by channel, with a seven-week trend, share of total sessions and progress towards goal</caption>
    <thead><tr><th scope="col">Channel</th><th scope="col" class="num">Sessions</th><th scope="col" class="num">Trend</th><th scope="col" class="num">Share of total</th><th scope="col" class="num">Goal</th></tr></thead>
    <tbody>
      <tr><th scope="row">Organic search</th><td class="num">18,442</td>
        <td><span class="spark" role="img" aria-label="Rising over the last 7 weeks"><i style="--h:38%"></i><i style="--h:52%"></i><i style="--h:44%"></i><i style="--h:61%"></i><i style="--h:58%"></i><i style="--h:74%"></i><i style="--h:92%"></i></span></td>
        <td><span class="bar"><span class="num">46%</span><span class="track" aria-hidden="true"><span class="fill" style="--p:46%"></span></span></span></td>
        <td><span class="ring" style="--p:92" role="img" aria-label="92 percent of goal"><span>92%</span></span></td></tr>
      <tr><th scope="row">Direct</th><td class="num">9,180</td>
        <td><span class="spark" role="img" aria-label="Flat over the last 7 weeks"><i style="--h:62%"></i><i style="--h:58%"></i><i style="--h:64%"></i><i style="--h:55%"></i><i style="--h:60%"></i><i style="--h:57%"></i><i style="--h:63%"></i></span></td>
        <td><span class="bar"><span class="num">23%</span><span class="track" aria-hidden="true"><span class="fill" style="--p:23%"></span></span></span></td>
        <td><span class="ring" style="--p:71" role="img" aria-label="71 percent of goal"><span>71%</span></span></td></tr>
      <tr><th scope="row">Referral</th><td class="num">6,730</td>
        <td><span class="spark" role="img" aria-label="Falling over the last 7 weeks"><i style="--h:80%"></i><i style="--h:72%"></i><i style="--h:66%"></i><i style="--h:58%"></i><i style="--h:49%"></i><i style="--h:44%"></i><i style="--h:38%"></i></span></td>
        <td><span class="bar"><span class="num">17%</span><span class="track" aria-hidden="true"><span class="fill a" style="--p:17%"></span></span></span></td>
        <td><span class="ring" style="--p:48" role="img" aria-label="48 percent of goal"><span>48%</span></span></td></tr>
      <tr><th scope="row">Email</th><td class="num">5,610</td>
        <td><span class="spark" role="img" aria-label="Rising over the last 7 weeks"><i style="--h:30%"></i><i style="--h:36%"></i><i style="--h:48%"></i><i style="--h:52%"></i><i style="--h:66%"></i><i style="--h:71%"></i><i style="--h:84%"></i></span></td>
        <td><span class="bar"><span class="num">14%</span><span class="track" aria-hidden="true"><span class="fill g" style="--p:14%"></span></span></span></td>
        <td><span class="ring" style="--p:88" role="img" aria-label="88 percent of goal"><span>88%</span></span></td></tr>
    </tbody>
  </table>
  </div>
</div>
CSS
/* Inline charts drawn in CSS. Each chart reads one custom property: --h for a sparkline bar,
   --p for a share bar or a conic-gradient ring. Charts that carry meaning without visible text
   are role="img" with an aria-label; the share bars sit next to their number and are hidden. */
.tcard{background:#fff;border:1px solid #e7e9f0;border-radius:14px;overflow:hidden}
.tscroll{overflow-x:auto}
.tscroll:focus-visible{outline:2px solid #4f46e5;outline-offset:-2px}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.tbl{width:100%;border-collapse:collapse;font-size:.89rem;white-space:nowrap}
.tbl thead th{text-align:left;font-size:.74rem;letter-spacing:.06em;text-transform:uppercase;
  color:#5f6778;font-weight:700;padding:13px 16px;background:#fafbfc;border-bottom:1px solid #eceef3}
.tbl td,.tbl tbody th{padding:13px 16px;border-bottom:1px solid #f2f4f7;color:#3c4048;text-align:left;font-weight:400}
.tbl tbody tr:last-child > *{border-bottom:0}
.tbl tbody tr:hover{background:#fafbfd}
.tbl .num{font-variant-numeric:tabular-nums;text-align:right}
.tbl thead th.num{text-align:right}

.bar{display:flex;align-items:center;gap:10px;justify-content:flex-end}
.bar .track{width:96px;height:7px;border-radius:99px;background:#eef0f4;overflow:hidden;flex-shrink:0}
.bar .fill{display:block;height:100%;border-radius:99px;background:#4f46e5;width:var(--p)}
.bar .fill.g{background:#16a34a}
.bar .fill.a{background:#f59e0b}
.spark{display:flex;align-items:flex-end;gap:2.5px;height:26px;justify-content:flex-end}
.spark i{width:5px;border-radius:2px 2px 0 0;background:#8b93a3;height:var(--h)}
.spark i:last-child{background:#4f46e5}
.ring{width:34px;height:34px;border-radius:50%;margin-left:auto;display:grid;place-items:center;
  background:conic-gradient(#4f46e5 calc(var(--p) * 1%),#eef0f4 0)}
.ring span{width:26px;height:26px;border-radius:50%;background:#fff;display:grid;place-items:center;
  font-size:.66rem;font-weight:800;color:#312e81}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="card tcard overflow-hidden">
  <div class="table-responsive" role="region" tabindex="0" aria-label="Sessions by channel, scrollable">
  <table class="table table-hover align-middle mb-0 tbl">
    <caption class="visually-hidden">Sessions by channel, with a seven-week trend, share of total sessions and progress towards goal</caption>
    <thead><tr><th scope="col">Channel</th><th scope="col" class="text-end">Sessions</th><th scope="col" class="text-end">Trend</th><th scope="col" class="text-end">Share of total</th><th scope="col" class="text-end">Goal</th></tr></thead>
    <tbody>
      <tr><th scope="row">Organic search</th><td class="text-end num">18,442</td>
        <td><span class="spark d-flex align-items-end justify-content-end" role="img" aria-label="Rising over the last 7 weeks"><i style="--h:38%"></i><i style="--h:52%"></i><i style="--h:44%"></i><i style="--h:61%"></i><i style="--h:58%"></i><i style="--h:74%"></i><i style="--h:92%"></i></span></td>
        <td><span class="d-flex align-items-center justify-content-end gap-2 bar"><span class="num">46%</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:46%"></span></span></span></td>
        <td><span class="ring d-grid ms-auto rounded-circle" style="--p:92" role="img" aria-label="92 percent of goal"><span class="d-grid rounded-circle bg-white">92%</span></span></td></tr>
      <tr><th scope="row">Direct</th><td class="text-end num">9,180</td>
        <td><span class="spark d-flex align-items-end justify-content-end" role="img" aria-label="Flat over the last 7 weeks"><i style="--h:62%"></i><i style="--h:58%"></i><i style="--h:64%"></i><i style="--h:55%"></i><i style="--h:60%"></i><i style="--h:57%"></i><i style="--h:63%"></i></span></td>
        <td><span class="d-flex align-items-center justify-content-end gap-2 bar"><span class="num">23%</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:23%"></span></span></span></td>
        <td><span class="ring d-grid ms-auto rounded-circle" style="--p:71" role="img" aria-label="71 percent of goal"><span class="d-grid rounded-circle bg-white">71%</span></span></td></tr>
      <tr><th scope="row">Referral</th><td class="text-end num">6,730</td>
        <td><span class="spark d-flex align-items-end justify-content-end" role="img" aria-label="Falling over the last 7 weeks"><i style="--h:80%"></i><i style="--h:72%"></i><i style="--h:66%"></i><i style="--h:58%"></i><i style="--h:49%"></i><i style="--h:44%"></i><i style="--h:38%"></i></span></td>
        <td><span class="d-flex align-items-center justify-content-end gap-2 bar"><span class="num">17%</span><span class="progress" aria-hidden="true" style="--bs-progress-bar-bg:#f59e0b"><span class="progress-bar" style="width:17%"></span></span></span></td>
        <td><span class="ring d-grid ms-auto rounded-circle" style="--p:48" role="img" aria-label="48 percent of goal"><span class="d-grid rounded-circle bg-white">48%</span></span></td></tr>
      <tr><th scope="row">Email</th><td class="text-end num">5,610</td>
        <td><span class="spark d-flex align-items-end justify-content-end" role="img" aria-label="Rising over the last 7 weeks"><i style="--h:30%"></i><i style="--h:36%"></i><i style="--h:48%"></i><i style="--h:52%"></i><i style="--h:66%"></i><i style="--h:71%"></i><i style="--h:84%"></i></span></td>
        <td><span class="d-flex align-items-center justify-content-end gap-2 bar"><span class="num">14%</span><span class="progress" aria-hidden="true" style="--bs-progress-bar-bg:#16a34a"><span class="progress-bar" style="width:14%"></span></span></span></td>
        <td><span class="ring d-grid ms-auto rounded-circle" style="--p:88" role="img" aria-label="88 percent of goal"><span class="d-grid rounded-circle bg-white">88%</span></span></td></tr>
    </tbody>
  </table>
  </div>
</div>
CSS
/* Bootstrap .card + .table-hover with charts in the cells. The share bars are Bootstrap .progress
   recoloured through --bs-progress-*; the sparkline (--h per bar) and the conic-gradient ring (--p)
   are a few lines of CSS because Bootstrap has no equivalent. */
.tcard{--bs-card-border-color:#e7e9f0;--bs-card-border-radius:14px;--bs-card-bg:#fff}
.tcard .table-responsive:focus-visible{outline:2px solid #4f46e5;outline-offset:-2px}
.tbl{--bs-table-color:#3c4048;--bs-table-bg:#fff;--bs-table-border-color:#f2f4f7;
  --bs-table-hover-color:#3c4048;--bs-table-hover-bg:#fafbfd;font-size:.89rem;white-space:nowrap}
.tbl > :not(caption) > * > *{padding:13px 16px}
.tbl > tbody > tr:last-child > *{border-bottom-width:0}
.tbl > thead > tr > th{font-size:.74rem;letter-spacing:.06em;text-transform:uppercase;color:#5f6778;
  --bs-table-bg:#fafbfc;border-bottom-color:#eceef3}
.tbl > tbody > tr > th{font-weight:400}
.tbl .num{font-variant-numeric:tabular-nums}
.tbl .bar{gap:10px!important}
.tbl .progress{--bs-progress-height:7px;--bs-progress-bg:#eef0f4;--bs-progress-bar-bg:#4f46e5;
  --bs-progress-border-radius:99px;width:96px;flex-shrink:0}
.tbl .progress-bar{border-radius:99px}
.spark{gap:2.5px;height:26px}
.spark i{width:5px;border-radius:2px 2px 0 0;background:#8b93a3;height:var(--h)}
.spark i:last-child{background:#4f46e5}
.ring{width:34px;height:34px;place-items:center;background:conic-gradient(#4f46e5 calc(var(--p) * 1%),#eef0f4 0)}
.ring span{width:26px;height:26px;place-items:center;font-size:.66rem;font-weight:800;color:#312e81}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="overflow-hidden rounded-[14px] border border-[#e7e9f0] bg-white">
  <div class="overflow-x-auto focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]" role="region" tabindex="0" aria-label="Sessions by channel, scrollable">
  <table class="w-full border-collapse text-[.89rem] whitespace-nowrap [&_tbody_tr:last-child>*]:border-b-0 [&_tbody>tr>*]:border-b [&_tbody>tr>*]:border-[#f2f4f7] [&_tbody>tr>*]:px-4 [&_tbody>tr>*]:py-[13px] [&_tbody>tr>*]:text-[#3c4048]">
    <caption class="sr-only">Sessions by channel, with a seven-week trend, share of total sessions and progress towards goal</caption>
    <thead><tr><th scope="col" class="border-b border-[#eceef3] bg-[#fafbfc] px-4 py-[13px] text-[.74rem] font-bold tracking-[.06em] text-[#5f6778] uppercase text-left">Channel</th><th scope="col" class="border-b border-[#eceef3] bg-[#fafbfc] px-4 py-[13px] text-[.74rem] font-bold tracking-[.06em] text-[#5f6778] uppercase text-right">Sessions</th><th scope="col" class="border-b border-[#eceef3] bg-[#fafbfc] px-4 py-[13px] text-[.74rem] font-bold tracking-[.06em] text-[#5f6778] uppercase text-right">Trend</th><th scope="col" class="border-b border-[#eceef3] bg-[#fafbfc] px-4 py-[13px] text-[.74rem] font-bold tracking-[.06em] text-[#5f6778] uppercase text-right">Share of total</th><th scope="col" class="border-b border-[#eceef3] bg-[#fafbfc] px-4 py-[13px] text-[.74rem] font-bold tracking-[.06em] text-[#5f6778] uppercase text-right">Goal</th></tr></thead>
    <tbody>
      <tr class="hover:bg-[#fafbfd]"><th scope="row" class="text-left font-normal">Organic search</th><td class="text-right tabular-nums">18,442</td>
        <td><span class="flex h-[26px] items-end justify-end gap-[2.5px]" role="img" aria-label="Rising over the last 7 weeks"><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:38%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:52%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:44%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:61%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:58%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:74%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#4f46e5]" style="--h:92%"></i></span></td>
        <td><span class="flex items-center justify-end gap-2.5"><span class="tabular-nums">46%</span><span class="h-[7px] w-24 shrink-0 overflow-hidden rounded-full bg-[#eef0f4]" aria-hidden="true"><span class="block h-full w-(--p) rounded-full bg-[#4f46e5]" style="--p:46%"></span></span></span></td>
        <td><span class="ml-auto grid size-[34px] place-items-center rounded-full bg-[conic-gradient(#4f46e5_calc(var(--p)*1%),#eef0f4_0)]" style="--p:92" role="img" aria-label="92 percent of goal"><span class="grid size-[26px] place-items-center rounded-full bg-white text-[.66rem] font-extrabold text-[#312e81]">92%</span></span></td></tr>
      <tr class="hover:bg-[#fafbfd]"><th scope="row" class="text-left font-normal">Direct</th><td class="text-right tabular-nums">9,180</td>
        <td><span class="flex h-[26px] items-end justify-end gap-[2.5px]" role="img" aria-label="Flat over the last 7 weeks"><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:62%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:58%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:64%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:55%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:60%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:57%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#4f46e5]" style="--h:63%"></i></span></td>
        <td><span class="flex items-center justify-end gap-2.5"><span class="tabular-nums">23%</span><span class="h-[7px] w-24 shrink-0 overflow-hidden rounded-full bg-[#eef0f4]" aria-hidden="true"><span class="block h-full w-(--p) rounded-full bg-[#4f46e5]" style="--p:23%"></span></span></span></td>
        <td><span class="ml-auto grid size-[34px] place-items-center rounded-full bg-[conic-gradient(#4f46e5_calc(var(--p)*1%),#eef0f4_0)]" style="--p:71" role="img" aria-label="71 percent of goal"><span class="grid size-[26px] place-items-center rounded-full bg-white text-[.66rem] font-extrabold text-[#312e81]">71%</span></span></td></tr>
      <tr class="hover:bg-[#fafbfd]"><th scope="row" class="text-left font-normal">Referral</th><td class="text-right tabular-nums">6,730</td>
        <td><span class="flex h-[26px] items-end justify-end gap-[2.5px]" role="img" aria-label="Falling over the last 7 weeks"><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:80%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:72%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:66%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:58%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:49%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:44%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#4f46e5]" style="--h:38%"></i></span></td>
        <td><span class="flex items-center justify-end gap-2.5"><span class="tabular-nums">17%</span><span class="h-[7px] w-24 shrink-0 overflow-hidden rounded-full bg-[#eef0f4]" aria-hidden="true"><span class="block h-full w-(--p) rounded-full bg-[#f59e0b]" style="--p:17%"></span></span></span></td>
        <td><span class="ml-auto grid size-[34px] place-items-center rounded-full bg-[conic-gradient(#4f46e5_calc(var(--p)*1%),#eef0f4_0)]" style="--p:48" role="img" aria-label="48 percent of goal"><span class="grid size-[26px] place-items-center rounded-full bg-white text-[.66rem] font-extrabold text-[#312e81]">48%</span></span></td></tr>
      <tr class="hover:bg-[#fafbfd]"><th scope="row" class="text-left font-normal">Email</th><td class="text-right tabular-nums">5,610</td>
        <td><span class="flex h-[26px] items-end justify-end gap-[2.5px]" role="img" aria-label="Rising over the last 7 weeks"><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:30%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:36%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:48%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:52%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:66%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#8b93a3]" style="--h:71%"></i><i class="h-(--h) w-[5px] rounded-t-[2px] bg-[#4f46e5]" style="--h:84%"></i></span></td>
        <td><span class="flex items-center justify-end gap-2.5"><span class="tabular-nums">14%</span><span class="h-[7px] w-24 shrink-0 overflow-hidden rounded-full bg-[#eef0f4]" aria-hidden="true"><span class="block h-full w-(--p) rounded-full bg-[#16a34a]" style="--p:14%"></span></span></span></td>
        <td><span class="ml-auto grid size-[34px] place-items-center rounded-full bg-[conic-gradient(#4f46e5_calc(var(--p)*1%),#eef0f4_0)]" style="--p:88" role="img" aria-label="88 percent of goal"><span class="grid size-[26px] place-items-center rounded-full bg-white text-[.66rem] font-extrabold text-[#312e81]">88%</span></span></td></tr>
    </tbody>
  </table>
  </div>
</div>

About this set

A traffic report by channel where three small charts sit inside the cells: a seven-bar sparkline for the recent trend, a horizontal bar beside each channel’s share of sessions, and a ring showing progress towards a goal with the percentage in its centre. Every chart is plain CSS reading one custom property, so no canvas, SVG or chart library is involved. Each sparkline bar sets –h as a percentage of a 26 pixel box and the last bar is drawn in indigo. The share bar sets –p as its width, in indigo, amber or green. The ring is a conic-gradient filled to –p percent with a white disc on top. To drive the charts from data, write those custom properties from your template or script. The share bars sit next to their numbers and are hidden from screen readers, while the sparklines and rings have no visible number and are exposed as images with labels such as Rising over the last 7 weeks and 92 percent of goal. Use it for dashboards and reports where a trend or proportion helps people scan a column. The Bootstrap version uses .progress for the share bars.

What’s included

  • Sparklines, share bars and goal rings in pure CSS
  • Each chart reads a single custom property
  • Bootstrap share bars built on .progress
  • Charts without visible numbers have text alternatives
  • Channel names are row headers

Accessibility

The caption describes every column, channels are row headers with scope="row", and sparklines and rings are role="img" with an aria-label that states the trend or goal. Share bars are aria-hidden because the percentage beside them is real text. Grey sparkline bars reach 3:1 against the white cell. The scroll region shows a 2px focus outline, and nothing animates.

Customise it

Set --h on each sparkline bar, --p on each share bar and --p on each ring. Colours are the #4f46e5, #f59e0b and #16a34a fills, the #8b93a3 sparkline grey and the #eef0f4 tracks; in Bootstrap the bar colour is --bs-progress-bar-bg.