Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dev.void/llms.txt

Use this file to discover all available pages before exploring further.

StarfieldBackground is the ambient backdrop that fills the entire viewport on every page of dev.void. It paints hundreds of tiny white dots on an HTML <canvas>, animates them scrolling upward to simulate drifting through space, and layers a radial gradient over the top to keep the centre of the screen slightly lighter than the edges. The component is positioned behind every other element and never intercepts mouse or touch events.

How it works

The component mounts a <canvas> element via a useRef and wires up all animation logic inside a single useEffect. On mount — and again on every window resize — it recalculates the canvas dimensions to match window.innerWidth × window.innerHeight and repopulates the star array.

Star generation

The number of stars is proportional to the viewport area:
const count = Math.floor(canvas.width * canvas.height / 3000);
Each star object stores:
FieldRangePurpose
x0 → canvas.widthHorizontal position
y0 → canvas.heightVertical position
size0.1 → 1.6Radius in pixels
speed0.1 → 0.6Upward drift per frame
opacity0 → 1Current alpha value

Animation loop

Each frame the canvas is cleared with clearRect, then every star is drawn as a filled circle using arc. After drawing, two mutations happen:
  1. Drifty is decremented by speed. When a star drifts off the top (y < 0), it is repositioned to the bottom at a random x.
  2. Twinkleopacity shifts by a random ±0.05 per frame, clamped to [0.1, 1.0], producing a subtle natural shimmer.
The loop runs via requestAnimationFrame and is cancelled on unmount through the useEffect cleanup function.
Because star density is computed from the viewport area, resizing the window triggers a full re-generation of the star field rather than stretching or cropping. This keeps visual density consistent across screen sizes.

Positioning and layering

The outer wrapper is fixed inset-0 z-0 pointer-events-none bg-space-950. Being fixed means the star field stays in place while the user scrolls — pages slide over it rather than scrolling past it. pointer-events-none ensures every click and touch falls through to content layers above. Inside the wrapper there are two children:
  1. Gradient overlay — an absolute inset-0 <div> with a radial gradient (from-space-800/50 via-space-950 to-space-950). It softens the centre of the sky so that brighter content like hero text reads cleanly against it.
  2. Canvasabsolute inset-0 opacity-60, the star-animation surface. Reducing opacity to 60 % lets the deep-space background colour bleed through and prevents the stars from feeling overpowering on bright monitors.
// Simplified structure
<div className="fixed inset-0 z-0 pointer-events-none bg-space-950">
  <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,...)]
                  from-space-800/50 via-space-950 to-space-950" />
  <canvas ref={canvasRef} className="absolute inset-0 opacity-60" />
</div>

Usage

Drop <StarfieldBackground /> as the first child of your root layout wrapper so that every subsequent sibling naturally layers on top of it:
import { StarfieldBackground } from "./components/StarfieldBackground";

function App() {
  return (
    <div className="relative min-h-screen bg-space-950 text-slate-200">
      <StarfieldBackground />
      {/* All other content goes here */}
    </div>
  );
}
Do not set overflow: hidden on a parent element that wraps StarfieldBackground. Because the canvas is fixed, it escapes normal document flow and clipping on an ancestor will not hide it — but it may create stacking-context conflicts that cause other fixed elements (like AuroraNav or CometCursor) to render in the wrong order.

Customisation

Adjust star density

Change the divisor in the density formula. A smaller value (e.g. 1500) produces a denser field; a larger value (e.g. 6000) gives a sparse, minimalist sky.
// In StarfieldBackground.js
const count = Math.floor(
  canvas.width * canvas.height / 3000 // ← change this
);

Change drift speed

Edit the speed range in the star-generation loop. Math.random() * 0.5 + 0.1 gives 0.1–0.6 px per frame. Multiply the upper bound to make the field feel faster, or reduce it for a near-static night sky.
speed: Math.random() * 0.5 + 0.1, // ← adjust multiplier

Twinkle intensity

The twinkle effect shifts opacity by (Math.random() - 0.5) * 0.1 per frame. Increase the multiplier for more dramatic flickering or set it to 0 to disable twinkling entirely.

Canvas opacity

The <canvas> element carries opacity-60 via Tailwind. Increase it toward opacity-100 for a brighter, more dramatic starfield, or decrease it for a subtler backdrop that keeps the focus on content.

Accessibility

The component renders no text content and carries no semantic role — it is purely decorative. It already respects the prefers-reduced-motion media query via the global CSS rule in main.css, which sets animation-duration: 0.01ms for users who have requested reduced motion. The requestAnimationFrame loop itself is not a CSS animation, however, so if you need to fully honour reduced-motion preferences you should read window.matchMedia("(prefers-reduced-motion: reduce)").matches before starting the loop and skip the animation if it is true.

Build docs developers (and LLMs) love