Variable Hover

A cool text proximity hover effect leveraging variable fonts.

Hover Me

Implementation

Component for the Variable Hover.

variable-hover.tsx - typescript
import { splitTextToLetters } from '@core/lib/utils'; // adjust import
import { variableFontHover } from '@core/lib/animations'; // adjust import
import { useEffect } from 'react';

export const VariableHover = ({
  text = "Hover Me"
}:{
  text?: string
}) => {

  useEffect(() => {
    variableFontHover({
      select:'[data-animate="font-weight"]',
      animationDuration: 0.5,
      maxDistance:300,
      minFontWeight:400,
      maxFontWeight:900
    })	 
  }, [])
  
  const letters = splitTextToLetters({
    text
  })

  return (
    <div>
      <h1 className='text-5xl font-regrade uppercase'>
        {letters.map((char, i)=>(
          <span key={i} className="char" data-animate="font-weight">{char}</span>
        ))}
      </h1>
    </div>
  );
};

Use a variable font.

To be able to achieve this effect, you MUST use a variable font. For this example im using Neue Regrade.
You can look it up if you dont have one, its free.

typography.css - css
@font-face {
  font-family: 'Regrade';
  src: url('/fonts/Neue_Regrade/NeueRegrade-Variable.ttf') format('truetype');
  font-weight: 100 900; 
  font-style: normal; 
}

Update tailwind.config.mjs.

Update tailwind config to use the font as a class. I used "font-regrade", you can see that being applied below.
Please update in accordance to your font of choice.

tailwind.config.mjs - javascript
/** @type {import('tailwindcss').Config} */
export default {
  content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
  theme: {
    extend: {
      fontFamily: {
        'regrade': ['Regrade', 'sans-serif']
      },
    },
  },
  plugins: [],
}

Install GSAP into your project.

GSAP is a lightweight animation library that will help animating things easier.
Run this command in the terminal with your package manager of choice to install GSAP, npm will be used as an example.

install-gsap - text
npm i gsap

Copy and paste the following code into your project.

Function that applies the animation using GSAP or you could customize this with vanillajs.

animations/variable-hover.ts - typescript
import gsap from 'gsap'

export function variableFontHover({
    select,
    animationDuration = 0.5,
    maxDistance = 300,
    minFontWeight = 500,
    maxFontWeight = 900
}:{
    select:string,
    animationDuration?: number,
    maxDistance?: number,
    minFontWeight?: number,
    maxFontWeight?: number,
}){
    let mm = gsap.matchMedia();
    
    mm.add("(min-width:992px)", () => {
        const fontWeightItems = document.querySelectorAll(select);
        
        fontWeightItems.forEach(item => {
            const text = item.textContent;
            item.textContent = '';
            text?.split('').forEach(char => {
                const span = document.createElement('span');
                span.classList.add('char');
                span.dataset.animate = 'font-weight';
                span.textContent = char;
                item.appendChild(span);
            });
        });

        document.addEventListener("mousemove", (e) => {
            const mouseX = e.pageX;
            const mouseY = e.pageY;

            fontWeightItems.forEach(item => {
                item.querySelectorAll(".char").forEach(char => {
                    const itemRect = char.getBoundingClientRect();
                    const itemCenterX = itemRect.left + itemRect.width / 2 + window.scrollX;
                    const itemCenterY = itemRect.top + itemRect.height / 2 + window.scrollY;

                    const distance = Math.sqrt(
                        Math.pow(mouseX - itemCenterX, 2) + Math.pow(mouseY - itemCenterY, 2)
                    );

                    let fontWeight = 
                        distance < maxDistance ? gsap.utils.mapRange(
                            0, 
                            maxDistance,
                            minFontWeight,
                            maxFontWeight,
                            Math.max(0, maxDistance - distance)
                        ) : minFontWeight;

                    gsap.to(char, {fontWeight, duration: animationDuration});
                });
            });
        });
    });
}

Copy and paste the following code into your project.

Some string utilities to help you.

utils/string.ts - typescript
export function splitTextToLetters({
    text
}:{
    text:string
}):string[]{
    return text.split('')
}

Update imports.

Change the import paths to match your project.