Data Table Row Animation
A real HTML table whose changed rows slide into place, then light up one by one; highlighted on hover.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Data has 28 effects, including 1 free. Explore the category →
3 usage examples



How it works
The component dresses an ordinary <table class="dtr-table">: <caption>, <th scope="col"> headers, rows in the <tbody>. Rows are 3 px apart (border-spacing), each cell has a very light background, the first cell is bold and the last is right-aligned. The dtr-up and dtr-down classes on a cell colour it green or red.
A MutationObserver watches the <tbody>: cell text rewritten, row added or content replaced. Rows touched within the same block of code are grouped, then they replay the dtr-slide animation together (coming in from 20 px to the left, 0.5 s) while an indigo glow (dtr-glow, 0.6 s) travels across their cells from row to row, 100 ms apart, in table order.
The table therefore needs no function: you update your cells as usual (td.textContent = …, tbody.innerHTML = …, insertRow) and only the changed rows animate. The script does not sort or move rows.
The first time the table scrolls into view, every row slides into place and the glow runs through them; until then they are hidden so the entrance can be seen.
Accessibility
- A real table: caption, column headers and cells are real elements; a screen reader announces the table name, the number of rows and each cell's header.
- Green and red backed by a sign:
dtr-upanddtr-downonly change the colour; always write "+" or "−" in the cell for people who cannot tell these hues apart. - Reduced motion: under
prefers-reduced-motion: reduce, no row slides or glows; they are visible from the start and new values simply appear. - Hover is decoration only: the indigo background on hover helps the eye follow a row, it triggers nothing; rows do not take focus.
Browser compatibility
HTML table, CSS animations, MutationObserver, IntersectionObserver. Measured in Chromium 151, Firefox 153 and WebKit 26.5: 150 ms after a change, the touched row is halfway (opacity around 0.5, offset around −10 px); two independent tables on one page, 390 px, white background, no JS error.
Without JavaScript the whole table shows, without animation. Without IntersectionObserver rows are visible straight away and only updates animate.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0249-root">
<table class="dtr-table">
<caption class="dtr-caption">Points this week</caption>
<thead>
<tr>
<th scope="col">Team</th>
<th scope="col">Points</th>
<th scope="col">Change</th>
</tr>
</thead>
<tbody>
<tr><td>Amber</td><td>1,284</td><td class="dtr-up">+2.4%</td></tr>
<tr><td>Cobalt</td><td>1,156</td><td class="dtr-up">+1.8%</td></tr>
<tr><td>Coral</td><td>982</td><td class="dtr-down">−0.5%</td></tr>
<tr><td>Lagoon</td><td>874</td><td class="dtr-up">+3.1%</td></tr>
<tr><td>Lilac</td><td>761</td><td class="dtr-down">−1.2%</td></tr>
</tbody>
</table>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| Table content (HTML) | 5 fictional teams, 3 columns | Put in your own headers and rows; any number of columns, the last one is right-aligned. |
dtr-up / dtr-down classes (HTML, on a cell) |
— | Colours the cell with --dtr-up or --dtr-down. |
--dtr-flash, --dtr-hover (CSS) |
20% indigo, 12% indigo | Colour of the glow after an update and of the hover highlight. |
--dtr-stagger (CSS) |
.1s | Delay between two rows animating together. |
--dtr-cell, --dtr-text, --dtr-head (CSS) |
3% white, 80% white, 40% white | Cell background, text and headers; replace them on a light background. |
--dtr-up, --dtr-down (CSS) |
#10b981, #ef4444 | Colours for rises and falls. |
--dtr-max-width (CSS) |
420px | Maximum table width; none to fill the container. |
--dtr-bg, --dtr-min-height (CSS) |
#0a0a0f, 300px | Root background and minimum height. |
FAQ
row.cells[1].textContent = '1,302') or replace the <tbody> content. The script spots the changed rows and animates them; there is nothing else to call. A row you do not touch is not animated.<tbody> in the new order; each row will replay its entrance.Intl.NumberFormat in the page language); digits all have the same width (tabular-nums), so columns do not shift when they change.