magnettype

Per-word axis
variation.

npm ↗
GitHub
TypeScript·Zero dependencies·React + Vanilla JS

CSS applies font-variation-settings to an entire element. Magnet Type applies them word by word — driven by cursor proximity. Words inside the field attract toward a peak axis value; words outside hold at rest. Move your mouse through a paragraph and watch each word respond independently.

Live demo — move your cursor through the text

Mode
Weight High600
Weight Low300
Radius120
FalloffMagnet

Typography has always been a conversation between the reader and the page. The characters on a printed sheet are fixed — their weight, their spacing, their axis values locked at the moment of setting. But type on screen can breathe, respond, move.

Variable fonts make every letterform a dial. The weight axis can thicken a stroke from hairline to slab; the width axis can compress a condensed face or expand it to full measure. These were tools for the typesetter, applied once at design time. Magnet Type applies them continuously, in real time, driven by where the cursor happens to be.

Move your cursor through the paragraph above. Words in the field attract toward their peak weight, then settle back as the cursor passes. The effect is subtle — not animation, not distraction — just a living texture that responds to presence. The text remains entirely readable throughout.

Move your cursor through the text. Each word responds to proximity independently — words inside the radius attract toward the peak weight, words outside hold at rest. Try switching between attract and repel, or between linear and quadratic falloff.

How it works

The cursor field

Each word gets a span. On every animation frame, the distance from the cursor to each word's center is measured. Words within the radius receive a font-variation-settings value interpolated between rest and peak — closer words get more of the peak value.

Attract and repel

In attract mode, nearby words approach the peak axis value and far words hold at rest. In repel mode, the logic inverts — words near the cursor stay at rest while words in the outer ring of the field approach peak. Each creates a different reading texture.

Field mode vs legibility mode

Field mode runs a requestAnimationFrame loop driven by cursor position. Legibility mode is static — it wraps visually confusable characters (il1I, rn, 0O) in spans with a boosted wdth axis, improving disambiguation at small sizes without cursor interaction.

Performance via rAF batching

The field loop reads word positions with getBoundingClientRect in a single batch pass, then writes all font-variation-settings in a second pass. No layout thrashing. The loop is cancelled on unmount and restarted when options change.

Usage

TypeScript + React · Vanilla JS

Drop-in component

import { MagnetTypeText } from '@liiift-studio/magnettype'

<MagnetTypeText mode="field" axes={{ wght: [300, 600] }} radius={120}>
  Your paragraph text here...
</MagnetTypeText>

Hook — attach to any element

import { useMagnetType } from '@liiift-studio/magnettype'

const ref = useMagnetType({ mode: 'field', axes: { wght: [300, 600] }, radius: 120 })
<p ref={ref}>{children}</p>

Vanilla JS

import { startMagnetType, getCleanHTML } from '@liiift-studio/magnettype'

const el = document.querySelector('p')
const original = getCleanHTML(el)
const stop = startMagnetType(el, original, { axes: { wght: [300, 600] }, radius: 120 })
// call stop() to cancel the rAF loop and restore markup

Options

OptionDefaultDescription
mode'field''field' — cursor proximity drives per-word font-variation-settings. 'legibility' — static per-character wdth boost for confusable characters.
axes{ wght: [300, 500] }Map of axis tag → [restValue, peakValue]. restValue applies at full distance; peakValue when cursor is directly over the word.
radius120Pixel radius over which the field effect fades. Words beyond this distance receive restValue.
falloff'quadratic''linear' — strength decreases linearly with distance. 'quadratic' — decreases as distance², giving a tighter hot zone.
magnetMode'attract''attract' — words near cursor approach peakValue. 'repel' — words near cursor stay at restValue; far words approach peakValue.
wdthBoost6wdth axis units added to confusable characters in legibility mode. Risk-proportional — highest-risk characters receive the full boost.