Highlight on Insert
stimeo--highlight
Marks a freshly inserted element for a moment so CSS can flash it. How it flashes is yours.
Marks a freshly inserted element for a short while, so CSS can flash it or fade it in off that mark. The mark clears itself after the time you set, so there is nothing to tidy up. There are two ways to use it: flash the element itself once, or watch a container and flash each element added to it, which suits pages where Turbo Stream appends rows. Visitors who ask for less motion get no flash, and the element simply appears. It carries no colours and no animation of its own; it only provides the mark.
- Item 1
- Item 2
Each new row is briefly highlighted, then fades back. The container runs in observe mode.
Keyboard
This component has no keyboard interactions of its own.
<%# Highlight-on-insert demo: the list runs in container (observe) mode, so the
controller highlights each <li> added to it. This catalog has no Turbo Stream
backend, so demo.js appends rows on click — exactly the mutation a Turbo Stream
append would make. The library only toggles data-highlight; demo.css owns the
flash/fade transition keyed on that hook. %>
<div class="highlight-demo">
<button
type="button"
class="demo-trigger"
data-highlight-add
data-item-label="<%= t("components.highlight.demo.item") %>">
<%= t("components.highlight.demo.add") %>
</button>
<ul
class="highlight-demo__list"
data-controller="stimeo--highlight"
data-stimeo--highlight-observe-value="true">
<li><%= t("components.highlight.demo.item") %> 1</li>
<li><%= t("components.highlight.demo.item") %> 2</li>
</ul>
<p class="highlight-demo__note"><%= t("components.highlight.demo.note") %></p>
</div>
/*
* Presentation-only styles for the highlight demo. The library only toggles
* data-highlight on each inserted row; this CSS owns the flash/fade: rows transition
* their background back to normal, and a highlighted row briefly shows the accent.
* Under reduced motion the controller never sets the hook, so no transition runs.
*/
.highlight-demo {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 28rem;
}
.highlight-demo__list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.highlight-demo__list li {
padding: 0.5rem 0.75rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
background: var(--surface-card);
transition: background-color 1s ease-out;
}
.highlight-demo__list li[data-highlight] {
background: var(--amber-50);
transition: none;
}
.highlight-demo__note {
margin: 0;
color: var(--color-text-muted);
}
// Highlight-on-insert demo (consumer-side JS).
//
// The list runs in container (observe) mode, so the controller highlights each <li>
// it gains on its own. This catalog has no Turbo Stream backend, so the button appends
// rows — the same mutation a Turbo Stream append makes — and the controller reacts.
document.querySelectorAll(".highlight-demo").forEach((root) => {
const list = root.querySelector(".highlight-demo__list");
const label = root.querySelector("[data-highlight-add]")?.dataset.itemLabel;
if (!list) return;
// Idempotent: Turbo can re-run this inline module on navigation; wire each root once
// so a click never appends two rows. The marker is a property, not an attribute:
// Turbo copies attributes into its page snapshot, so an attribute one comes back set
// on a restored page whose elements carry no listeners.
if (root.demoWired) return;
root.demoWired = true;
let count = list.children.length;
root.querySelector("[data-highlight-add]")?.addEventListener("click", () => {
const li = document.createElement("li");
li.textContent = `${label ?? "Item"} ${(count += 1)}`;
list.appendChild(li);
});
});
These demo styles use shared design tokens (light + dark). Copy the shared styles too, then toggle data-theme on your root element for dark mode.
The data-* attributes you add to your own HTML to wire this component. Put the data-controller below on a root element, then place its targets / values / actions inside that element.
On the root element
data-controller="stimeo--highlight"
Values
| Name | Description | Attribute |
|---|---|---|
duration
|
Milliseconds the data-highlight hook stays applied (default 1500). |
data-stimeo--highlight-duration-value |
observe
|
Container mode: watch for added element children and highlight each (default false). |
data-stimeo--highlight-observe-value |
Events
| Name | Description | Event |
|---|---|---|
start
|
Fires when an element starts being highlighted, with detail.element. |
stimeo--highlight:start |
end
|
Fires when the highlight is removed, with detail.element. |
stimeo--highlight:end |
State hooks
The library only manages these ARIA/data attributes and custom properties. Your CSS reads them to render the look — selectors like [aria-selected], [aria-expanded], or var(--stimeo--…) hook into this state.
| Hook | Target | Meaning |
|---|---|---|
data-highlight |
The highlighted element (self or an added child) | Present (set to true) while highlighted; removed after duration. |