@junco/three
3D
Theme-aware scenes on React Three Fiber. Lazy loaded, paused off screen, with a still fallback.
npm i @junco/three three @react-three/fiber @react-three/drei// 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";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>| Prop | Default | Description |
|---|---|---|
colors | ["--primary", "--chart-2", "--chart-4"] | Up to four theme variables |
base | "--background" | The color the blobs sit on |
intensity | 0.62 | How strongly blobs cover the base, 0..1 |
clearCenter | 0.55 | Keeps the middle calm for text, 0..1 |
speed | 1 | Multiplied 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>| Prop | Default | Description |
|---|---|---|
count | 1400 | Halved on screens under 640px |
size | 3 | Particle size multiplier |
speed | 1 | Drift speed |
colors | ["--primary", "--muted-foreground"] | [accent, base] |
parallax | true | Leans 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"
/>| Prop | Default | Description |
|---|---|---|
points | sample cities | { lat, lng, label?, size? }[] |
arcs | sample routes | { from, to, label? }[] |
label | Announces it as an image, with a list of places | |
autoRotate | true | Stops with motion off |
interactive | true | Drag 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.
<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." }]}
/>| Prop | Default | Description |
|---|---|---|
src | Required. .glb or .gltf URL | |
label | Required. Describes the model | |
hotspots | [] | { id, position, label, content? }[] |
autoRotate | true | Pauses on a hotspot and with motion off |
enableZoom | false | So 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>| Prop | Default | Description |
|---|---|---|
maxTilt | 8 | Degrees, multiplied by --motion-scale |
glare | true | A light that follows the pointer |
hoverScale | 1.015 | |
perspective | 900 | px |
Size and fallbacks
| Entry | Size (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 itsposter, 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.