---
title: "How to Create a Text Pressure Effect in Framer"
description: "Build a text pressure effect in Framer — variable font weight that responds to cursor proximity, with a working code override and a free component."
canonical_url: "https://framerhub.io/blog/text-pressure-effect-framer"
last_updated: "2027-01-13T00:00:00.000Z"
---

Text pressure is the effect where letters in a headline appear to "react" to your cursor — getting heavier, wider, or both as you move across them, then relaxing back as you move away. It's a distinctive, tactile way to make a static headline feel alive without relying on movement or color, and it's built entirely on a font capability most designers don't realize they already have access to: variable fonts. Here's how it actually works and how to build it in Framer.

## The Font Requirement Nobody Mentions First

Before any code, there's a prerequisite that determines whether this effect is even possible with your chosen typeface: you need a **variable font** with a weight (`wght`) or width (`wdth`) axis. A variable font isn't a single fixed-weight file — it's a font that encodes a continuous range between, say, weight 100 and weight 900, letting you set any value in between (450, 620, whatever) rather than only picking from preset cuts like Regular or Bold. Standard, non-variable font families can't do this; you'd only be able to hard-swap between whatever fixed weights you have files for, which produces a stepped, jerky result instead of a smooth one.

Check whether your brand typeface has a variable version before planning around this effect. Many widely used typefaces (Inter, Manrope, Recoleta Variable, and increasingly more of Google Fonts' catalog) ship variable versions, but plenty of premium and custom typefaces still don't. If yours doesn't, either pick a variable alternative for this specific headline or use a different effect — a hard weight-swap version of "text pressure" without a true variable axis looks noticeably worse than no effect at all.

A quick way to check: open the font in a type-testing tool or your OS font viewer and look for a single "Variable" entry with a weight slider, rather than a list of separately named static cuts (Light, Regular, Medium, Bold, Black). If you only see the static list, you're working with a standard font family, not a variable one, and this specific effect isn't achievable with it as-is.

## Text Pressure vs. Other Hover Typography Effects

Text pressure sits in a family of hover-driven typography effects, and it's worth knowing where it fits before committing to it over an alternative:

<table>
<thead>
  <tr>
    <th>
      Effect
    </th>
    
    <th>
      Mechanism
    </th>
    
    <th>
      Feel
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Text pressure
    </td>
    
    <td>
      Variable font weight/width interpolation by cursor distance
    </td>
    
    <td>
      Tactile, physical, subtle
    </td>
  </tr>
  
  <tr>
    <td>
      Letter scramble/decode
    </td>
    
    <td>
      Characters swap randomly before settling on the real text
    </td>
    
    <td>
      Techy, glitchy, attention-grabbing
    </td>
  </tr>
  
  <tr>
    <td>
      Text shimmer
    </td>
    
    <td>
      A light gradient sweeps across static-weight text
    </td>
    
    <td>
      Premium, polished, low-key
    </td>
  </tr>
  
  <tr>
    <td>
      Magnetic hover
    </td>
    
    <td>
      The whole text block shifts toward the cursor
    </td>
    
    <td>
      Playful, whole-element motion
    </td>
  </tr>
</tbody>
</table>

Text pressure is the right choice specifically when you want the *letterforms themselves* to feel interactive, rather than the block of text moving as a unit or a color effect passing over it — it's a more literal, tactile metaphor than the alternatives.

## How the Effect Actually Works

At a technical level, text pressure works by:

1. Splitting the headline into individual letter (or word) spans.
2. Tracking cursor position relative to each span.
3. Calculating a "pressure" value per letter based on distance from the cursor (closer = higher pressure = heavier weight).
4. Applying that value to the letter's `font-variation-settings` CSS property in real time.

The math is simple distance-to-value mapping, but the implementation detail that matters most is performance: recalculating and re-rendering every letter's style on every mouse movement, for a headline with 20+ letters, needs to be efficient or the whole page will visibly stutter.

## Building It With a Code Override

Here's a working implementation that splits text into letters and maps cursor distance to font weight using a variable font's `wght` axis.

```jsx
import { useRef, useState, useCallback } from "react"
import { addPropertyControls, ControlType } from "framer"

export default function TextPressure(props) {
  const { text, fontFamily, minWeight, maxWeight, radius, fontSize, color } = props
  const containerRef = useRef(null)
  const [weights, setWeights] = useState([])
  const letters = (text || "PRESSURE").split("")

  const handleMouseMove = useCallback(
    (e) => {
      const spans = containerRef.current.querySelectorAll("span")
      const newWeights = Array.from(spans).map((span) => {
        const rect = span.getBoundingClientRect()
        const cx = rect.left + rect.width / 2
        const cy = rect.top + rect.height / 2
        const dist = Math.hypot(e.clientX - cx, e.clientY - cy)
        const pressure = Math.max(0, 1 - dist / radius)
        return Math.round(minWeight + pressure * (maxWeight - minWeight))
      })
      setWeights(newWeights)
    },
    [radius, minWeight, maxWeight]
  )

  const handleMouseLeave = useCallback(() => {
    setWeights(letters.map(() => minWeight))
  }, [minWeight, letters.length])

  return (
    <div
      ref={containerRef}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
      style={{ display: "flex", cursor: "default" }}
    >
      {letters.map((char, i) => (
        <span
          key={i}
          style={{
            fontFamily,
            fontSize,
            color,
            fontVariationSettings: `"wght" ${weights[i] || minWeight}`,
            transition: "font-variation-settings 0.1s ease-out",
            whiteSpace: "pre",
          }}
        >
          {char}
        </span>
      ))}
    </div>
  )
}

addPropertyControls(TextPressure, {
  text: { type: ControlType.String, defaultValue: "PRESSURE" },
  fontFamily: { type: ControlType.String, defaultValue: "Recoleta Variable" },
  minWeight: { type: ControlType.Number, defaultValue: 300, min: 100, max: 900 },
  maxWeight: { type: ControlType.Number, defaultValue: 800, min: 100, max: 900 },
  radius: { type: ControlType.Number, defaultValue: 200, min: 50, max: 500 },
  fontSize: { type: ControlType.Number, defaultValue: 64, min: 16, max: 160 },
  color: { type: ControlType.Color, defaultValue: "#000000" },
})
```

Make sure the variable font you're using is actually loaded with its full axis range available — a `@font-face` declaration that only exposes a narrow `wght` range (say, 400–500) will make the pressure effect barely noticeable no matter how you tune `minWeight` and `maxWeight` in the component. Load the font with its full documented range and let the component's controls do the constraining if you want a subtler effect.

### Why `font-variation-settings`, Not a Class Swap

The critical technical choice here is animating `font-variation-settings` directly rather than toggling between CSS classes tied to fixed weights. `font-variation-settings` accepts any numeric value within the font's axis range, so a letter can sit at weight 437 or 612 — values that don't correspond to any named "Regular" or "Bold" cut. That continuous range is what makes the transition feel like pressure rather than a snap between two states. It also means the short `transition` on that property in the snippet above does real work smoothing consecutive mouse-move updates into what reads as fluid motion.

## Tuning the Feel

Three settings do most of the work in how the effect feels:

- **Radius.** A smaller radius (100-150px) creates a tight, localized effect where only letters right under the cursor react — dramatic and precise. A larger radius (250-350px) creates a broader, gentler wave across more letters at once — softer, more atmospheric.
- **Weight range.** A narrower range (400 to 600) feels subtle and refined. A wide range (200 to 900, if your font supports it) is bold and highly visible, appropriate for a hero statement rather than supporting text.
- **Transition duration.** Keep this short (0.08-0.15s) — text pressure is meant to feel immediate and responsive to cursor movement, not delayed. A longer transition here reads as laggy rather than smooth, unlike slower transitions that work fine for hover effects that aren't tracking continuous movement.

## Drop-In Option: The TextPressure Component

FramerHub's [TextPressure component](/components/text-pressure) implements this exact effect with font family, weight range, radius, and typography exposed as property panel controls — and it's free, so if you're evaluating whether this effect fits your project before committing to anything else from the [component library](/components), it's a zero-risk way to try it. Drop it into a hero section, connect a variable font, and tune the radius and weight range to match your brand's typographic personality.

## Where This Effect Works Best

- **Single bold word or short phrase headlines**, where each letter is large enough for the weight change to be visually obvious.
- **Editorial and design-forward portfolio sites**, where typography itself is a core part of the visual identity.
- **Above-the-fold hero statements** that benefit from a tactile, interactive first impression.

It's a poor fit for body copy or any text a visitor needs to read quickly and literally — the shifting letterforms, however subtle, add friction to reading comprehension at paragraph length. Reserve it for short, prominent text that's meant to be looked at and played with, not read start to finish.

## Common Mistakes When Implementing Text Pressure

- **Using a variable font but only exposing a narrow axis range.** If the loaded font file's `wght` axis only spans 400-500, no amount of tuning `minWeight`/`maxWeight` in the component will produce a dramatic effect — the font file itself is the bottleneck, not your code.
- **Splitting text into letters with display: inline-block without preserving whitespace.** Spaces between words can collapse when you split a string into individual `<span>` elements naively — use `whiteSpace: "pre"` on each span, as in the snippet above, so word spacing survives the split.
- **Forgetting to test with the actual production headline.** A short test word like "PRESSURE" behaves differently than a full multi-word headline — longer text has more letters simultaneously competing for cursor proximity, which can make the effect feel busier than it did in an isolated test.
- **Applying the effect to every headline on the page.** Like most cursor-reactive effects, text pressure loses its impact fast when it's not reserved for a single, prominent moment — usually just the hero headline.

If you're pairing this with other motion on the page — a hover effect on the CTA button below it, for instance — our [Framer animations and hover effects guide](/blog/framer-animations-hover-effects) covers how to keep multiple animated elements from competing with each other on the same screen, and our broader [component library](/components) has button and card hover patterns designed to complement a typography-forward hero like this one without fighting it for attention.

## Accessibility and Fallbacks

Since this is a hover-driven, cursor-tracking effect, it simply won't trigger for keyboard-only or touch-device visitors — make sure the resting weight (your `minWeight`) still reads clearly and looks intentional on its own, since a meaningful share of visitors will only ever see that static state. There's no accessibility harm in the effect itself (it doesn't affect the actual text content or its semantics), but as with any continuous hover animation, respecting `prefers-reduced-motion` by disabling the weight interpolation for motion-sensitive visitors is good practice — show the resting weight only, rather than the full cursor-tracking version.

## FAQ

**What is a text pressure effect?**
It's a typography effect where individual letters in a headline get heavier or wider as the cursor passes near them, mimicking pressure applied to the letterforms, then relax back to normal weight as the cursor moves away.

**Does text pressure require a variable font?**
Yes — the effect relies on interpolating a font's weight or width axis in real time, which only variable fonts support. A standard font family with fixed weight files (Regular, Bold, etc.) can't be animated smoothly between those states.

**Will a text pressure effect work with my brand font?**
Only if your brand font has a variable version with a weight (wght) or width (wdth) axis. Many popular typefaces now ship variable versions, but not all — check the font's specimen page or your font provider before committing to build the effect around it.

**Does this effect work on mobile?**
Not in its cursor-driven form, since there's no hover on touch devices. Most implementations simply display the text at its resting weight on mobile, since the effect is a hover embellishment rather than content that needs to work identically everywhere.

**Is a text pressure effect good for SEO?**
It doesn't hurt SEO as long as the underlying text is real, selectable, crawlable text and not an image or canvas rendering — search engines read the actual characters regardless of the variable font animation applied on top.
