Skip to content

@junco/three

3D

Theme-aware scenes on React Three Fiber. Lazy loaded, paused off screen, with a still fallback.

Install
npm i @junco/three three @react-three/fiber @react-three/drei
Lazy by default
// The root entry never imports three.js. Scenes download near the screen.
import { Globe, ShaderGradient } from "@junco/three";

// Or load one eagerly, for an above-the-fold hero:
import { ShaderGradient } from "@junco/three/shader-gradient";
Theme
Motion

Switch the theme to watch the scenes relight; motion off freezes them.

Shader gradient

A flowing mesh gradient built from your accent and chart colors, with a calm center so headlines stay readable.

Ship the hero in an afternoon

A flowing mesh gradient from your accent and chart colors.

<section className="relative isolate">
  <ShaderGradient className="absolute inset-0 -z-10" />
  <h1>Your headline</h1>
</section>
PropDefaultDescription
colors["--primary", "--chart-2", "--chart-4"]Up to four theme variables
base"--background"The color the blobs sit on
intensity0.62How strongly blobs cover the base, 0..1
clearCenter0.55Keeps the middle calm for text, 0..1
speed1Multiplied by the motion setting

Particle field

A slow, drifting field of soft particles for hero backgrounds. A quarter take your accent color.

Build something calm.

<div className="relative h-80">
  <ParticleField className="absolute inset-0" />
</div>
PropDefaultDescription
count1400Halved on screens under 640px
size3Particle size multiplier
speed1Drift speed
colors["--primary", "--muted-foreground"][accent, base]
parallaxtrueLeans toward the pointer

Globe

A dotted globe with location markers and animated arcs. Drag to rotate. Screen readers get the list of places and routes.

<Globe
  className="h-96"
  points={[{ lat: 39.3, lng: -76.6, label: "Baltimore" }]}
  arcs={[{ from: { lat: 39.3, lng: -76.6 }, to: { lat: 47.6, lng: -122.3 } }]}
  label="Our offices and the routes between them"
/>
PropDefaultDescription
pointssample cities{ lat, lng, label?, size? }[]
arcssample routes{ from, to, label? }[]
labelAnnounces it as an image, with a list of places
autoRotatetrueStops with motion off
interactivetrueDrag to rotate

Model viewer

A GLB/GLTF model with orbit controls, a local studio light rig and hotspots that are real buttons: Tab to them, Enter to open, Esc to close.

Loading 3D model
<ModelViewer
  className="h-[28rem]"
  src="/speaker.glb"
  label="A smart speaker in charcoal fabric"
  hotspots={[{ id: "ring", position: [0.3, 1.84, 0.3], label: "Ring light", content: "Glows in your accent color." }]}
/>
PropDefaultDescription
srcRequired. .glb or .gltf URL
labelRequired. Describes the model
hotspots[]{ id, position, label, content? }[]
autoRotatetruePauses on a hotspot and with motion off
enableZoomfalseSo the page scrolls over the model

Tilt card

A card that tilts toward the pointer, with a soft glare. Pure CSS transforms: no WebGL and no three.js. Touch never tilts.

Theme aware

Scenes read Junco tokens at runtime and relight when you switch modes.

Motion aware

data-motion and reduced motion slow or freeze every scene.

Lazy by default

three.js loads only when a scene scrolls near the screen.

import { TiltCard } from "@junco/three/tilt-card";

<TiltCard className="rounded-card border p-5">…</TiltCard>
PropDefaultDescription
maxTilt8Degrees, multiplied by --motion-scale
glaretrueA light that follows the pointer
hoverScale1.015
perspective900px

Size and fallbacks

EntrySize (not minified, peers excluded)
@junco/three~15 KB: Scene3D, lazy scenes, TiltCard, hooks
particle-field / shader-gradient~5 to 6 KB each + 2.5 KB shared canvas
globe / model-viewer~8 to 10 KB each + 2.5 KB shared canvas
three.js + React Three Fiber~250 KB gzipped, loaded once with the first scene

When a scene can't run

Without WebGL, in data-saver mode, or if loading fails, every scene shows its poster, your own fallback, or a still gradient made from the same theme colors. With reduced motion it renders one frozen frame, or pass reducedMotion="fallback" to skip downloading three.js entirely.