Components / Motion / Particle Net
Particle Net beta
A constellation that drifts and connects: the classic particle network, tuned quiet enough for real heroes.
Built by a network of one-person studios
312 independent builders ship on the mold. The net behind this panel is the part; the sentence is the kind it carries.
<div class="k-net" data-net role="img" aria-label="A drifting particle network behind the headline">
<canvas aria-hidden="true"></canvas>
<div class="k-net-in">
<h3>Built by a network of one-person studios</h3>
<p>312 independent builders ship on the mold. The net behind this panel is the part; the sentence is the kind it carries.</p>
</div>
</div>/* KATA Particle Net — a quiet constellation. */
.k-net{position:relative;width:100%;background:var(--k-zinc-950);border-radius:14px;overflow:hidden;font-family:var(--k-font)}
.k-net canvas{position:absolute;inset:0;width:100%;height:100%}
.k-net-in{position:relative;z-index:1;padding:30px 26px;color:var(--k-on-color);max-width:430px}
.k-net-in h3{font-size:20px;font-weight:650;letter-spacing:-.02em}
.k-net-in p{font-size:13px;line-height:1.75;color:var(--k-zinc-400);margin-top:8px}
// KATA Particle Net — drift, connect, gather at the cursor.
document.querySelectorAll('[data-net]').forEach(function (stage) {
var canvas = stage.querySelector('canvas');
var ctx = canvas.getContext('2d');
var DPR = Math.min(3, window.devicePixelRatio || 1);
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var cs = getComputedStyle(stage);
var INK = cs.getPropertyValue('--k-zinc-400').trim() || '#a1a1aa';
var ACCENT = cs.getPropertyValue('--k-accent').trim() || '#0e9594';
var W, H, pts = [], mouse = { x: -9999, y: -9999 };
function size() {
W = stage.clientWidth; H = stage.clientHeight;
canvas.width = W * DPR; canvas.height = H * DPR; ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
var n = Math.min(78, Math.floor(W * H / 2600));
pts = [];
for (var i = 0; i < n; i++) pts.push({ x: Math.random() * W, y: Math.random() * H, vx: (Math.random() - .5) * .34, vy: (Math.random() - .5) * .34 });
}
function draw() {
ctx.clearRect(0, 0, W, H);
var i, j, p, q;
for (i = 0; i < pts.length; i++) {
p = pts[i];
p.x += p.vx; p.y += p.vy;
if (p.x < 0 || p.x > W) p.vx *= -1;
if (p.y < 0 || p.y > H) p.vy *= -1;
var dm = Math.hypot(mouse.x - p.x, mouse.y - p.y);
if (dm < 130 && dm > 1) { p.x += (mouse.x - p.x) * .0016; p.y += (mouse.y - p.y) * .0016; }
}
for (i = 0; i < pts.length; i++) for (j = i + 1; j < pts.length; j++) {
p = pts[i]; q = pts[j];
var d = Math.hypot(p.x - q.x, p.y - q.y);
if (d < 108) {
ctx.strokeStyle = INK;
ctx.globalAlpha = (1 - d / 108) * .42;
ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(q.x, q.y); ctx.stroke();
}
}
ctx.globalAlpha = 1;
pts.forEach(function (p, i) {
ctx.fillStyle = i % 9 === 0 ? ACCENT : INK;
ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, 7); ctx.fill();
});
}
function frame() { draw(); if (!reduced) requestAnimationFrame(frame); }
stage.addEventListener('pointermove', function (e) {
var r = stage.getBoundingClientRect();
mouse.x = e.clientX - r.left; mouse.y = e.clientY - r.top;
});
stage.addEventListener('pointerleave', function () { mouse.x = -9999; mouse.y = -9999; });
size();
window.addEventListener('resize', size);
frame();
});
Installation
CLI
node kata-ui.mjs add particle-net
Writes the part’s CSS and JS into your project. New to the CLI? See the CLI doc.
Manual
- Load
tokens.cssonce, before any part. - Copy the CSS tab into your stylesheet.
- Copy the HTML where it belongs. The part has a script — include it after the markup; it initializes itself.
Usage
A background for hero sections and empty states: points drift slowly, lines form between near neighbors, and the cursor gathers a small crowd. Density and speed are set low on purpose — a net you notice working is a net set wrong. Real content rides above it in the same panel.
Accessibility
The canvas is aria-hidden scenery behind real, high-contrast content. Under prefers-reduced-motion a single still constellation is drawn and left alone. If the text cannot be read with the net removed, the problem is the text, not the net.
Anatomy
The public surface of this part, read from its own source. Class names are the API — the CSS tab above is the full contract.
.k-net | class |
.k-net-in | class |
data-net | data hook |
aria-label | ARIA |
aria-hidden | ARIA |
Related parts
Accordion Gallery beta
Five scenes in a row; the one you touch opens up and tells its caption.
Aurora beta
A slow wash of color behind a hero — the accent and the core, drifting under glass.
Beam border beta
A single point of light travels the edge of a card. The beam is the core color — the one accent, in motion.
Blur In beta
Words arrive out of focus and sharpen one after another, like eyes adjusting.