Live preview needs JavaScript. The code below works without any dependencies.
Prompt for your AI assistant
Paste this into Claude, Cursor or ChatGPT. The settings update as you move the controls above.
Build "Rule sweep list" for my site using the Enrichr Library element at https://enrichr.dev/elements/rule-sweep-list.html.
Read the machine-readable spec and source at https://enrichr.dev/elements/rule-sweep-list.json (markup, CSS, JavaScript, parameters).
Requirements: vanilla JavaScript and CSS, no new dependencies; keep the behaviour of the source unchanged; respect prefers-reduced-motion; work on touch screens; keep it accessible.
Settings:
- items: Work, Studio, Journal, Contact
- speed: 450
- color: #ffffff
- line: #c6f26b
Verify: reduced motion shows no movement, it works at 375px wide, there are no console errors, and scrolling stays smooth.The same spec as JSON, for tools that read it directly: /elements/rule-sweep-list.json.
Code
Mount it on an element that contains the markup. It returns an object with update(options) and destroy().
HTML
<ul class="ef-rule"></ul>CSS
.ef-rule { list-style: none; margin: 0; padding: 0; }
.ef-rule__row { position: relative; display: flex; align-items: baseline; gap: 1rem; padding: 1rem 0; font-size: clamp(1.3rem, 3.4vw, 2.2rem); font-weight: 600; cursor: pointer; outline: none; }
.ef-rule__row::before { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: currentColor; opacity: 0.22; }
.ef-rule__row::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--ef-line, currentColor); transform: scaleX(0); transform-origin: right center; transition: transform var(--ef-dur, 450ms) cubic-bezier(0.7, 0, 0.2, 1); }
.ef-rule__row:hover::after, .ef-rule__row:focus-visible::after { transform: scaleX(1); transform-origin: left center; }
.ef-rule__n { font-size: 0.5em; opacity: 0.6; font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) { .ef-rule__row::after { transition: none; } }JavaScript
// Rule sweep list: a line sweeps across a row on hover or keyboard focus, and exits the other side.
// mount(root, { items (comma separated), color, line (colour of the sweep), speed (ms) })
function mount(root, o) {
var ul = root.querySelector(".ef-rule");
var opts = {};
function render() {
ul.textContent = "";
ul.style.color = opts.color;
ul.style.setProperty("--ef-line", opts.line);
ul.style.setProperty("--ef-dur", opts.speed + "ms");
opts.items.split(",").map(function (s) { return s.trim(); }).filter(Boolean).forEach(function (label, i) {
var li = document.createElement("li");
li.className = "ef-rule__row";
li.tabIndex = 0;
var n = document.createElement("span");
n.className = "ef-rule__n";
n.textContent = String(i + 1).padStart(2, "0");
var t = document.createElement("span");
t.textContent = label;
li.appendChild(n);
li.appendChild(t);
ul.appendChild(li);
});
}
function update(next) {
opts = Object.assign({ items: "Work, Studio, Journal, Contact", color: "#ffffff", line: "#c6f26b", speed: 450 }, opts, next || {});
render();
}
update(o);
return { update: update, destroy: function () {} };
}
// Usage
var fx = mount(document.querySelector("#my-root"), {"items":"Work, Studio, Journal, Contact","speed":450,"color":"#ffffff","line":"#c6f26b"});How it works
The line is a pseudo-element scaled to zero with its origin on the right. On hover the scale goes to one and the origin flips to the left, so the line grows in from the left. When the pointer leaves, the origin is back on the right, so it shrinks away to the right: one continuous sweep, no JavaScript on hover.
Accessibility and performance
Rows are focusable and the sweep also runs on keyboard focus. Reduced motion turns the transition off, so the line simply appears.
A single transform transition per row, no layout work. About 0.8 KB of JavaScript, only used to build the rows.
| Section | Navigation and lists |
|---|---|
| Techniques | Pointer interaction |
| Stack | Vanilla JS, CSS |
| Works with | React, Next.js, Vanilla |
| Dependencies | None |
| Status | tested |
Find this technique on a real site
Want to see this technique in the wild? Check any site with the free tools, then build your own version from this page.
Check which animation library a site uses · Extract a site's design tokens · Browse site breakdowns
Licence and provenance
Free to use in personal and client projects during the preview. Final licence terms will be published here before any paid tier starts; ask before reselling the code as a collection.
This is original code. The technique is a common one that we studied by measuring award-winning sites with Enrichr; no third-party code, shaders, fonts or images are used.
More elements
FAQ
Why does the line leave on the other side?
The transform-origin changes between the hover and resting states. The element is scaling down from the right edge, which looks like the line travelling through.
Is it keyboard accessible?
Yes. Each row is focusable and :focus-visible triggers the same sweep.
Can the rows be links?
Yes. Swap the li content for anchors and keep the row class; the effect is on the row, not the content.