npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@8bitbish/flux-hover

v0.3.1

Published

Premium directional magnetic hover background effect for React

Readme

FluxHover

A directional magnetic hover background effect for React. The highlight enters from the direction the cursor approached, follows the cursor softly while hovered, and exits toward where the cursor leaves.

Built with React, TypeScript, and Framer Motion.

Live demo →


Install

npm install @8bitbish/flux-hover framer-motion

react, react-dom, and framer-motion are peer dependencies.


Quick start

import { FluxHover } from '@8bitbish/flux-hover';

function NavButton() {
  return (
    <FluxHover background="rgba(255,255,255,0.08)" style={{ borderRadius: 10 }}>
      <button>Features</button>
    </FluxHover>
  );
}

The blob automatically inherits the borderRadius of the wrapper element — set it once via style or a CSS class and you're done.


Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | — | Content to wrap | | background | string | "rgba(255,255,255,0.09)" | CSS background of the hover blob (color, gradient, etc.) | | pressColor | string | — | Color overlaid on the blob on pointer-down. Accepts any CSS color including rgba. | | intensity | number | 0.25 | Magnetic movement strength 0–1. Higher = more travel | | duration | number | 0.22 | Enter/exit animation duration in seconds | | opacity | number | 1 | Peak opacity of the blob 0–1 | | scale | number | 1 | Scale of the blob at rest | | enterDelay | number | 40 | Delay in ms before hover activates. Cancels if cursor leaves first — useful for segmented controls | | clip | boolean | false | Clip blob to wrapper bounds (overflow: hidden). Blob slides in from the entry edge | | focusVisible | boolean | true | Show blob on :focus-visible (keyboard nav) | | disabled | boolean | false | Disable all hover and press effects | | as | keyof JSX.IntrinsicElements | "div" | Render wrapper as a different element | | className | string | — | Extra class on the wrapper element | | style | CSSProperties | — | Inline styles on the wrapper element. Use this to set borderRadius. |

Any extra props (e.g. href, onClick) are forwarded to the underlying element.


Examples

Ghost button

<FluxHover
  background="rgba(255,255,255,0.07)"
  pressColor="rgba(255,255,255,0.12)"
  style={{ borderRadius: 10 }}
>
  <button>Click me</button>
</FluxHover>

Tinted button

<FluxHover
  background="rgba(99,102,241,0.22)"
  pressColor="rgba(99,102,241,0.2)"
  enterDelay={0}
  style={{ borderRadius: 10 }}
>
  <button style={{ color: '#a5b4fc' }}>Get started</button>
</FluxHover>

Segmented control

<div style={{ display: 'inline-flex', gap: 4, padding: 4, background: '#111', borderRadius: 11, overflow: 'hidden' }}>
  {['All', 'Design', 'Engineering'].map((tab) => (
    <FluxHover key={tab} style={{ borderRadius: 7 }}>
      <button>{tab}</button>
    </FluxHover>
  ))}
</div>

Card hover

<FluxHover
  as="article"
  background="rgba(99,102,241,0.08)"
  intensity={0.4}
  enterDelay={0}
  style={{ padding: 24, border: '1px solid #1e1e1e', borderRadius: 16 }}
>
  <h3>Card title</h3>
  <p>Some content</p>
</FluxHover>

FluxHoverProvider

Set project-wide defaults once. Every FluxHover in the tree inherits them, and any prop passed directly to an instance overrides the default.

import { FluxHoverProvider, FluxHover } from '@8bitbish/flux-hover';

<FluxHoverProvider defaults={{
  background:  "rgba(255,255,255,0.09)",
  pressColor:  "rgba(255,255,255,0.15)",
  duration:    0.22,
  intensity:   0.25,
  enterDelay:  40,
}}>
  <App />
</FluxHoverProvider>

// Inherits all provider defaults
<FluxHover style={{ borderRadius: 7 }}>
  <button>Tab</button>
</FluxHover>

// Overrides just what differs
<FluxHover background="rgba(99,102,241,0.22)" enterDelay={0} style={{ borderRadius: 10 }}>
  <button>Get started</button>
</FluxHover>

useFluxHover hook

For lower-level control, the hook is exported directly:

import { useFluxHover } from '@8bitbish/flux-hover';
import { motion, useTransform } from 'framer-motion';

function CustomButton() {
  const {
    wrapperRef,
    blobX, blobY,
    blobOpacity, blobScale,
    pressScale, pressOverlay,
    onPointerEnter, onPointerMove, onPointerLeave,
    onPointerDown, onPointerUp,
    onFocus, onBlur,
  } = useFluxHover({ intensity: 0.25, duration: 0.22 });

  const combinedScale = useTransform(() => blobScale.get() * pressScale.get());

  return (
    <div
      ref={wrapperRef}
      style={{ position: 'relative', display: 'inline-flex', borderRadius: 8 }}
      onPointerEnter={onPointerEnter}
      onPointerMove={onPointerMove}
      onPointerLeave={onPointerLeave}
      onPointerDown={onPointerDown}
      onPointerUp={onPointerUp}
      onFocus={onFocus}
      onBlur={onBlur}
    >
      <motion.span
        aria-hidden
        style={{
          position: 'absolute', inset: 0,
          borderRadius: 'inherit',
          background: 'rgba(255,255,255,0.09)',
          overflow: 'hidden',
          pointerEvents: 'none',
          x: blobX, y: blobY,
          opacity: blobOpacity,
          scale: combinedScale,
        }}
      >
        <motion.span
          aria-hidden
          style={{
            position: 'absolute', inset: 0,
            background: 'rgba(255,255,255,0.15)',
            opacity: pressOverlay,
          }}
        />
      </motion.span>
      <span style={{ position: 'relative', zIndex: 1 }}>
        Custom content
      </span>
    </div>
  );
}

Customization notes

Subtle default feel — defaults are tuned for modern dark UIs. Small background opacity (0.07–0.12) and moderate intensity (0.25) produce the most polished result.

Segmented controls — use enterDelay={40} to debounce fast sweeps, preventing the effect from firing when the cursor briefly crosses an item.

Standalone buttons — pass enterDelay={0} to override the default delay for elements that should respond instantly.

Gradient backgrounds — any CSS background value works:

background="linear-gradient(135deg, rgba(99,102,241,0.25), rgba(168,85,247,0.25))"

Reduced motion — FluxHover automatically detects prefers-reduced-motion and skips all animations, falling back to an instant appear/disappear.

Focus visible — keyboard users get the same hover highlight on :focus-visible. Pass focusVisible={false} to opt out.


License

MIT