Field Web

ProBackgroundspowered by @vectorvesper/motion

A background of contour lines that deform around the pointer like a magnetic field bending a topographic map. Ships a companion node component that pulls the nearest contours toward any element you mark, so buttons and cards sit in their own dent in the terrain.

Field Web

Included with membership

Members install Field Web with one command and get the full source, the usage code and the Framer remix, licensed for client work. One membership covers the whole library and the CLI.

When to use it

A page that needs texture behind it rather than an image — a landing page, a pricing table, a login. It sits underneath your content and reacts without demanding attention, which is what separates it from a hero effect. The node component is the reason to choose it over a static pattern: controls can sit in the field and visibly disturb it.

  • Put your content in a positioned child above it — the contours are the background layer, not a container for layout.
  • Wrap the one or two elements that should disturb the field in FieldWebNode. That contrast is the point; wrapping everything flattens it.
  • Raise magneticReach rather than magneticStrength if the effect feels too local — reach decides how many lines respond, strength only how far they bend.
  • Give the wrapper a height. It fills its container and has nothing of its own to size it.

Avoid:

  • Do not put small text directly over the thick contour lines. They move, so a line can cross a word at exactly the wrong moment.
  • Do not raise density far past the default expecting more detail — every line is a path resimulated each frame, and the component already reduces it on weaker devices.
  • Do not nest one inside another. Both would claim the same pointer and the nodes would resolve against the wrong field.
  • Do not use FieldWebNode outside a FieldWeb; it reads the field through context and has nothing to attach to on its own.

Props

A backdrop of contour lines, like a topographic map, that bends around the pointer as though it were magnetised. The lines drift gently on their own and pull toward the cursor as it crosses them, then settle back. Anything you wrap in the companion node gets its own dent in the terrain.

<FieldWeb />

The contour field. Fills its container and paints its own background, so give it a height and put your content inside it.

PropTypeDefaultDescription
densitynumber28How many contour lines are drawn. Reduced automatically on constrained devices — to 75% on a mid tier and to 14 lines on the lowest — so this is the ceiling rather than a fixed count.
theme"obsidian" | "paper""obsidian"Background and line colours together. Obsidian is near-black with light contours; paper inverts it.
backgroundColorstringfrom the themeOverrides the theme's background.
thickLineColorstringfrom the themeColour of the major index contours — the heavier lines that appear every few steps.
thinLineColorstringfrom the themeColour of the ordinary contours in between.
thickLineWidthnumber1.4Stroke width of the major contours, in pixels.
thinLineWidthnumber0.85Stroke width of the ordinary contours, in pixels.
magneticStrengthnumber110Furthest a contour bends toward the pointer.
magneticReachnumber260Radius of influence in pixels. This is the prop that changes how the field reads — widen it for a slow swell, narrow it for a sharp dimple.
tensionnumber0.08Spring stiffness pulling each point back to its resting contour.
dampingnumber0.90How quickly the lines stop moving. Lower values let them ripple after the pointer has passed.
childrenReact.ReactNode—Rendered above the field. Give it its own stacking context and position.
classNamestring""Applied to the wrapper.
styleReact.CSSProperties—Inline styles merged onto the wrapper.
disableAdaptiveQualitybooleanfalseKeeps the full contour density on every device. By default the runtime thins the lines on lower tiers, so a density of 28 becomes 21 and then 14. Set this when the density is the design and you would rather lose frames than lines.
mount"auto" | "eager""auto"When to start drawing. The default waits until the page has frame headroom to spare. Set "eager" to start immediately — worth it when the field is the background of the first screen, since a contour mesh that fades in late reads as a loading artefact rather than a design. Note this defaults the opposite way from the components that build a 3D scene: those start eagerly and take "auto" to defer. Both mean the same thing by "auto" — let the runtime pick the moment — the default just matches what each component already did. Neither setting takes it off the shared frame loop or overrides the reduced-motion preference.

<FieldWebNode />

Wraps an element so the field dents around it and the element itself leans toward the pointer. Takes everything a div takes, plus the props below. Must be inside a FieldWeb.

PropTypeDefaultDescription
children*React.ReactNode—The element that sits in the field.
strengthnumber20How far the element itself moves toward the pointer, in pixels.
reachnumber150How close the pointer must come before the element responds.
scalenumber1.05How much the element grows while the pointer is on it.
classNamestring""Applied to the node wrapper.
styleReact.CSSProperties—Inline styles merged onto the node wrapper.

<useFieldWeb />

hook

Reads the field from inside it, so you can register your own beacon rather than using FieldWebNode. Only useful within a FieldWeb.

It reads the visitor's reduced-motion preference and stops both kinds of movement: the contours no longer drift on their own, and the pointer no longer bends them. The terrain is still drawn — you keep the texture and lose the animation.
Every contour is a path whose points are resimulated each frame, so density is the cost. It already steps down on constrained devices, dropping to 75% of your value on a mid tier and to fourteen lines on the lowest, so treat density as a ceiling rather than a target.
Client component. It measures its own box and reads the pointer, and waits for a quiet main thread before starting.
It draws on the shared frame loop rather than starting one of its own, so putting several animated components on a page costs one callback, not one each, and the scheduler can set decorative work aside when frames get tight. Everything it measures — where the field sits, and where each marked node sits inside it — is read in a pass that runs before anything writes. That ordering matters here more than in most components: the contours are redrawn every frame, so a measurement taken after a write would force the browser to recalculate the whole layout, once per node, every frame.
Back to all components