Search rows as you type
An ordinary table plus a search box that hides non-matching rows. Type in the box to filter the demo. The HTML and CSS are below; a few lines of JavaScript do the filtering.
| Name | Role | City |
|---|---|---|
| Ada Lovelace | Engineer | London |
| Grace Hopper | Engineer | New York |
| Mae Jemison | Scientist | Decatur |
| Katherine Johnson | Mathematician | Hampton |
HTML
<input id="filter" type="search" placeholder="Filter by name or role…">
<table>
<thead><tr><th>Name</th><th>Role</th><th>City</th></tr></thead>
<tbody id="rows">
<tr><td>Ada Lovelace</td><td>Engineer</td><td>London</td></tr>
<tr><td>Grace Hopper</td><td>Engineer</td><td>New York</td></tr>
<tr><td>Mae Jemison</td><td>Scientist</td><td>Decatur</td></tr>
<tr><td>Katherine Johnson</td><td>Mathematician</td><td>Hampton</td></tr>
</tbody>
</table>
CSS
table { width: 100%; border-collapse: collapse; }
th { background: #f1f5f9; text-align: left; padding: 10px 12px; }
td { padding: 10px 12px; border-top: 1px solid #eef1f5; }
JavaScript
const filter = document.getElementById('filter');
const rows = document.querySelectorAll('#rows tr');
filter.addEventListener('input', () => {
const q = filter.value.toLowerCase();
rows.forEach(r => {
r.style.display = r.textContent.toLowerCase().includes(q) ? '' : 'none';
});
});
Show intensity with color
A grid where each cell's color reflects a value — like a GitHub contribution graph. Darker = higher. It's just a CSS grid of cells with different background shades.
Click a cell to read its intensity level.
HTML
<div class="heat"> <!-- one cell per value, shade by level (12 per row) --> <i style="background:#e0f2fe"></i><i style="background:#7dd3fc"></i><i style="background:#e0f2fe"></i><i style="background:#0284c7"></i><i style="background:#38bdf8"></i><i style="background:#e5e7eb"></i><i style="background:#7dd3fc"></i><i style="background:#0284c7"></i><i style="background:#e0f2fe"></i><i style="background:#38bdf8"></i><i style="background:#e5e7eb"></i><i style="background:#7dd3fc"></i> <i style="background:#38bdf8"></i><i style="background:#e5e7eb"></i><i style="background:#0284c7"></i><i style="background:#7dd3fc"></i><i style="background:#e0f2fe"></i><i style="background:#38bdf8"></i><i style="background:#0284c7"></i><i style="background:#e5e7eb"></i><i style="background:#7dd3fc"></i><i style="background:#e0f2fe"></i><i style="background:#38bdf8"></i><i style="background:#0284c7"></i> <i style="background:#7dd3fc"></i><i style="background:#0284c7"></i><i style="background:#e0f2fe"></i><i style="background:#e5e7eb"></i><i style="background:#38bdf8"></i><i style="background:#7dd3fc"></i><i style="background:#e0f2fe"></i><i style="background:#0284c7"></i><i style="background:#38bdf8"></i><i style="background:#7dd3fc"></i><i style="background:#e5e7eb"></i><i style="background:#e0f2fe"></i> </div> <div class="legend">Less <i style="background:#e5e7eb"></i><i style="background:#e0f2fe"></i><i style="background:#7dd3fc"></i><i style="background:#38bdf8"></i><i style="background:#0284c7"></i> More</div> <p class="out">Click a cell to read its intensity level.</p>
CSS
.heat {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 4px;
max-width: 380px;
}
.heat i { aspect-ratio: 1; border-radius: 3px; display: block; cursor: pointer; }
.heat i.sel { outline: 2px solid #0b1a33; outline-offset: 1px; } /* clicked cell */
.legend { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: .75rem; color: #6b7280; }
.legend i { width: 14px; height: 14px; border-radius: 3px; display: inline-block; }
.out { margin-top: 12px; font-size: .82rem; font-weight: 700; color: #0369a1; }
JavaScript — click a cell to read its level
const heat = document.querySelector('.heat');
const out = document.querySelector('.out');
// map each shade to an intensity level 0–4
const shades = ['#e5e7eb','#e0f2fe','#7dd3fc','#38bdf8','#0284c7'];
heat.querySelectorAll('i').forEach(cell => {
cell.addEventListener('click', () => {
heat.querySelector('i.sel')?.classList.remove('sel');
cell.classList.add('sel');
const hex = cell.getAttribute('style').match(/#[0-9a-f]{6}/i)[0].toLowerCase();
out.textContent = `Intensity level ${shades.indexOf(hex)} of 4`;
});
});
background.A month grid
Seven columns (one per weekday) with day numbers. Highlight today and grey out days from the previous/next month.
Click a day to select it.
HTML
<div class="calendar">
<div class="head"><span>S</span><span>M</span><span>T</span><span>W</span><span>T</span><span>F</span><span>S</span></div>
<div class="grid">
<span class="muted">30</span><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span><span>6</span>
<span>7</span><span>8</span><span>9</span><span>10</span><span>11</span><span class="today">12</span><span>13</span>
<span>14</span><span>15</span><span>16</span><span>17</span><span>18</span><span>19</span><span>20</span>
<span>21</span><span>22</span><span>23</span><span>24</span><span>25</span><span>26</span><span>27</span>
</div>
</div>
<p class="out">Click a day to select it.</p>
CSS
.calendar { max-width: 320px; }
.head, .grid {
display: grid;
grid-template-columns: repeat(7, 1fr); /* 7 weekdays */
gap: 4px;
}
.head span { text-align: center; font-size: .68rem; color: #94a3b8; font-weight: 700; padding-bottom: 4px; }
.grid span { aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
font-size: .8rem; color: #374151; border-radius: 6px;
background: #fff; border: 1px solid #eef1f5; }
.grid span.muted { color: #cbd5e1; }
.grid span.pick { cursor: pointer; }
.grid .today { background: #38bdf8; color: #fff; border-color: #38bdf8; font-weight: 700; }
.grid .sel { background: #0284c7; color: #fff; border-color: #0284c7; } /* clicked day */
.out { margin-top: 12px; font-size: .82rem; font-weight: 700; color: #0369a1; }
JavaScript — click a day to select it
const cal = document.querySelector('.grid'); // the day cells
const out = document.querySelector('.out');
cal.querySelectorAll('span:not(.muted)').forEach(day => {
day.classList.add('pick'); // show a pointer on real days
day.addEventListener('click', () => {
cal.querySelector('span.sel')?.classList.remove('sel');
day.classList.add('sel');
out.textContent = 'Selected: June ' + day.textContent;
});
});
A day's time slots
A two-column grid — times on the left, slots on the right — with events filling their slot. Great for agendas and booking views.
Click an event to see its time.
HTML
<div class="schedule"> <div class="time">9:00</div><div class="event">Team stand-up</div> <div class="time">10:00</div><div class="slot"></div> <div class="time">11:00</div><div class="event">Design review</div> <div class="time">12:00</div><div class="slot"></div> <div class="time">1:00</div><div class="event">Lunch & learn</div> </div> <p class="out">Click an event to see its time.</p>
CSS
.schedule {
display: grid;
grid-template-columns: 56px 1fr; /* time | slot */
gap: 6px; max-width: 420px; font-size: .82rem;
}
.time { color: #94a3b8; text-align: right; padding-top: 6px; }
.slot { background: #fff; border: 1px solid #eef1f5; border-radius: 6px; min-height: 34px; }
.event {
background: #e0f2fe;
border-left: 3px solid #38bdf8;
color: #0369a1; font-weight: 600;
border-radius: 6px; padding: 7px 10px;
}
.event.sel { outline: 2px solid #0369a1; outline-offset: 1px; } /* clicked event */
.out { margin-top: 12px; font-size: .82rem; font-weight: 700; color: #0369a1; }
JavaScript — click an event to read its time
const sched = document.querySelector('.schedule');
const out = document.querySelector('.out');
sched.querySelectorAll('.event').forEach(ev => {
ev.style.cursor = 'pointer';
ev.addEventListener('click', () => {
sched.querySelector('.event.sel')?.classList.remove('sel');
ev.classList.add('sel');
const time = ev.previousElementSibling.textContent; // the time cell to its left
out.textContent = time + ' — ' + ev.textContent;
});
});
Share of a whole
A pie chart shows parts of a total. A single conic-gradient draws every slice — no SVG, no library. Pair it with a legend so the slices are labeled.
HTML
<div class="pie" role="img" aria-label="Direct 45%, Search 25%, Social 18%, Referral 12%"></div> <div class="legend"> <span><i style="background:#38bdf8"></i> Direct — 45%</span> <span><i style="background:#9b7bff"></i> Search — 25%</span> <span><i style="background:#22c55e"></i> Social — 18%</span> <span><i style="background:#f59e0b"></i> Referral — 12%</span> </div>
CSS
.pie {
width: 140px; height: 140px; border-radius: 50%;
background: conic-gradient(
#38bdf8 0 45%, /* Direct */
#9b7bff 45% 70%, /* Search */
#22c55e 70% 88%, /* Social */
#f59e0b 88% 100% /* Referral */
);
}
color start% end% stop. Want a donut? Lay a smaller white circle on top of the center.Compare values with bars
Each bar is a div whose height is its value — no chart library needed. A flex row aligns them along a shared baseline.
Click a bar to read its value.
HTML
<div class="bars"> <div class="col"><span class="v">40</span><div class="bar" style="height:40%"></div><span class="x">Mon</span></div> <div class="col"><span class="v">75</span><div class="bar" style="height:75%"></div><span class="x">Tue</span></div> <div class="col"><span class="v">55</span><div class="bar" style="height:55%"></div><span class="x">Wed</span></div> <div class="col"><span class="v">90</span><div class="bar" style="height:90%"></div><span class="x">Thu</span></div> <div class="col"><span class="v">65</span><div class="bar" style="height:65%"></div><span class="x">Fri</span></div> </div> <p class="out">Click a bar to read its value.</p>
CSS
.bars { display: flex; align-items: flex-end; gap: 14px; height: 150px; max-width: 420px; }
.col { flex: 1; display: flex; flex-direction: column; align-items: center;
justify-content: flex-end; height: 100%; cursor: pointer; }
.bar { width: 100%; max-width: 42px; border-radius: 6px 6px 0 0;
background: linear-gradient(180deg,#38bdf8,#0284c7); } /* height = value % */
.v { font-size: .7rem; font-weight: 700; color: #0369a1; margin-bottom: 4px; }
.x { font-size: .72rem; color: #6b7280; margin-top: 6px; }
.col.sel .bar { background: linear-gradient(180deg,#f59e0b,#d97706); } /* highlight the clicked bar */
.out { margin-top: 12px; font-size: .82rem; font-weight: 700; color: #0369a1; }
JavaScript — click a bar to read its value
const bars = document.querySelector('.bars');
const out = document.querySelector('.out');
bars.querySelectorAll('.col').forEach(col => {
col.addEventListener('click', () => {
bars.querySelector('.col.sel')?.classList.remove('sel');
col.classList.add('sel');
const value = col.querySelector('.v').textContent;
const label = col.querySelector('.x').textContent;
out.textContent = label + ': ' + value;
});
});
Show a trend over time
An inline <svg> <polyline> connects data points into a trend line — lightweight and crisp at any size.
HTML
<svg viewBox="0 0 300 120" role="img" aria-label="Upward trend">
<polyline points="0,95 60,70 120,80 180,40 240,55 300,15"
fill="none" stroke="#38bdf8" stroke-width="3"/>
</svg>
CSS
svg { width: 100%; height: 150px; }
polyline { stroke-linejoin: round; stroke-linecap: round; } /* smooth corners */
A dense, structured table
A data grid is a table built for rows of records — clear column headers, zebra striping for scan-ability, and consistent alignment.
| ID | Name | Status | Value |
|---|---|---|---|
| 001 | Alpha | Active | $1,240 |
| 002 | Bravo | Pending | $880 |
| 003 | Charlie | Active | $2,010 |
| 004 | Delta | Closed | $540 |
Click a row to select it.
HTML
<table class="grid">
<thead><tr><th>ID</th><th>Name</th><th>Status</th><th>Value</th></tr></thead>
<tbody>
<tr><td>001</td><td>Alpha</td><td>Active</td><td>$1,240</td></tr>
<tr><td>002</td><td>Bravo</td><td>Pending</td><td>$880</td></tr>
<tr><td>003</td><td>Charlie</td><td>Active</td><td>$2,010</td></tr>
<tr><td>004</td><td>Delta</td><td>Closed</td><td>$540</td></tr>
</tbody>
</table>
<p class="out">Click a row to select it.</p>
CSS
.grid { width: 100%; border-collapse: collapse; }
.grid th { background: #0b1a33; color: #fff; text-align: left; padding: 9px 12px; }
.grid td { padding: 9px 12px; border-top: 1px solid #eef1f5; }
.grid tbody tr:nth-child(even) { background: #f8fafc; } /* zebra rows */
.grid tbody tr { cursor: pointer; }
.grid tbody tr.sel { background: #e0f2fe; } /* selected row */
.out { margin-top: 12px; font-size: .82rem; font-weight: 700; color: #0369a1; }
JavaScript — click a row to select it
const grid = document.querySelector('.grid'); // the table
const out = document.querySelector('.out');
grid.querySelectorAll('tbody tr').forEach(row => {
row.style.cursor = 'pointer';
row.addEventListener('click', () => {
grid.querySelector('tr.sel')?.classList.remove('sel');
row.classList.add('sel');
out.textContent = 'Selected: ' + row.cells[0].textContent + ' · ' + row.cells[1].textContent;
});
});
Click a header to sort
Click any column heading to sort the rows by it; click again to reverse the order. The arrow shows the current sort. Try sorting by Score — it sorts as numbers, not text.
| Name | Role | Score |
|---|---|---|
| Ada Lovelace | Engineer | 92 |
| Grace Hopper | Engineer | 88 |
| Mae Jemison | Scientist | 105 |
| Katherine Johnson | Mathematician | 97 |
HTML
<table id="sortTable">
<thead><tr>
<th class="sortable">Name</th>
<th class="sortable">Role</th>
<th class="sortable" data-type="num">Score</th> <!-- sort as numbers -->
</tr></thead>
<tbody>
<tr><td>Ada Lovelace</td><td>Engineer</td><td>92</td></tr>
<tr><td>Grace Hopper</td><td>Engineer</td><td>88</td></tr>
<tr><td>Mae Jemison</td><td>Scientist</td><td>105</td></tr>
<tr><td>Katherine Johnson</td><td>Mathematician</td><td>97</td></tr>
</tbody>
</table>
CSS — the table plus the sort arrow
table { width: 100%; border-collapse: collapse; background: #fff;
border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; font-size: .88rem; color: #374151; }
th { background: #f1f5f9; text-align: left; padding: 10px 12px; font-size: .7rem;
text-transform: uppercase; color: #64748b; letter-spacing: .04em; }
td { padding: 10px 12px; border-top: 1px solid #eef1f5; }
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable::after { content: " \2195"; opacity: .35; font-size: .85em; } /* idle */
th.sortable.asc::after { content: " \2191"; opacity: 1; color: #0284c7; } /* ascending */
th.sortable.desc::after { content: " \2193"; opacity: 1; color: #0284c7; } /* descending */
JavaScript
const table = document.getElementById('sortTable');
const tbody = table.tBodies[0];
const headers = [...table.querySelectorAll('th.sortable')];
headers.forEach(th => th.addEventListener('click', () => {
const asc = !th.classList.contains('asc');
headers.forEach(h => h.classList.remove('asc', 'desc'));
th.classList.add(asc ? 'asc' : 'desc');
const i = th.cellIndex, num = th.dataset.type === 'num';
[...tbody.rows].sort((a, b) => {
let x = a.cells[i].textContent, y = b.cells[i].textContent;
if (num) { x = parseFloat(x); y = parseFloat(y); }
return (x > y ? 1 : x < y ? -1 : 0) * (asc ? 1 : -1);
}).forEach(r => tbody.appendChild(r));
}));
Headline metrics at a glance
KPI (Key Performance Indicator) cards each show one big number, a label, and how it changed — the top row of most dashboards. A responsive grid lets them wrap on small screens.
HTML
<div class="kpis">
<div class="kpi">
<p class="label">Visitors</p>
<p class="value">12,480</p>
<p class="trend up">▲ 8.2% vs last week</p>
</div>
<div class="kpi">
<p class="label">Sign-ups</p>
<p class="value">342</p>
<p class="trend up">▲ 3.1%</p>
</div>
<div class="kpi">
<p class="label">Bounce rate</p>
<p class="value">41%</p>
<p class="trend down">▼ 2.4%</p>
</div>
<div class="kpi">
<p class="label">Avg. time</p>
<p class="value">3:18</p>
<p class="trend up">▲ 0:22</p>
</div>
</div>
CSS
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; }
.kpi { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; }
.value { font-size: 1.9rem; font-weight: 800; }
.trend.up { color: #16a34a; } .trend.down { color: #dc2626; } /* colour + arrow */
JavaScript — count each number up from zero
document.querySelectorAll('.kpi .value').forEach(el => {
const target = el.textContent;
if (!/^[\d,]+%?$/.test(target)) return; // skip non-numeric like "3:18"
const end = parseInt(target.replace(/,/g, ''), 10);
const suffix = target.endsWith('%') ? '%' : '';
let n = 0, step = Math.max(1, Math.round(end / 40));
const tick = setInterval(() => {
n = Math.min(end, n + step);
el.textContent = n.toLocaleString() + suffix;
if (n >= end) clearInterval(tick);
}, 25);
});
KPIs and a chart in one card
An analytics panel groups a few KPIs with a small chart into a single dashboard widget. Click a bar to see its value. Combine this with KPI cards and a sortable table to build a full dashboard.
Weekly traffic
Last 7 daysHTML
<div class="analytics">
<div class="head"><h4>Weekly traffic</h4><span>Last 7 days</span></div>
<div class="an-kpis">
<div class="an-kpi"><div class="v">12.4k</div><div class="k">Visitors</div></div>
<div class="an-kpi"><div class="v">+8.2%</div><div class="k">vs last week</div></div>
<div class="an-kpi"><div class="v" id="pick">—</div><div class="k">Selected day</div></div>
</div>
<div class="spark">
<i style="height:46%" data-v="1,210" title="Mon"></i>
<i style="height:62%" data-v="1,640" title="Tue"></i>
<i style="height:54%" data-v="1,420" title="Wed"></i>
<i style="height:78%" data-v="2,050" title="Thu"></i>
<i style="height:90%" data-v="2,380" title="Fri"></i>
<i style="height:40%" data-v="1,060" title="Sat"></i>
<i style="height:34%" data-v="900" title="Sun"></i>
</div>
</div>
CSS — the panel and bar “sparkline”
.analytics { background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; padding: 18px; max-width: 540px; }
.head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
.head h4 { color: #111; font-size: 1rem; margin: 0; }
.head span { font-size: .76rem; color: #6b7280; }
.an-kpis { display: flex; gap: 18px; margin-bottom: 16px; flex-wrap: wrap; }
.an-kpi .v { font-size: 1.4rem; font-weight: 800; color: #111; }
.an-kpi .k { font-size: .72rem; color: #6b7280; font-weight: 600; }
.spark { display: flex; align-items: flex-end; gap: 6px; height: 80px; }
.spark i { flex: 1; border-radius: 4px 4px 0 0; cursor: pointer;
background: linear-gradient(180deg, #7dd3fc, #0284c7); transition: height .3s; }
/* each bar's height is set inline: style="height:78%" */
JavaScript — click a bar
const bars = document.querySelectorAll('.spark i');
const picked = document.getElementById('pick');
bars.forEach(bar => bar.addEventListener('click', () => {
bars.forEach(b => b.style.opacity = '.5'); // dim the rest
bar.style.opacity = '1'; // highlight this one
picked.textContent = bar.dataset.v; // show that day's value
}));
Keep building
More data work in Tables, Data Viz & Forms, and Dashboard Lab — or browse the full Component Library.