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

awesome-magnetic-grid

v0.1.0

Published

A physics-driven magnetic repulsion grid React component. Cards spring away from the cursor with inverse-square force, 3D tilt on the closest card, variable card sizes, and a built-in detail overlay.

Downloads

13

Readme

awesome-magnetic-grid

A physics-driven magnetic repulsion grid React component. Cards spring away from the cursor using inverse-square repulsion force, snap back with Hooke's law, and the closest card gets a subtle 3D tilt toward the cursor — all running in a native RAF loop at 60fps, bypassing React's render cycle entirely for zero-jank animation.


Features

  • Magnetic repulsion physics — inverse-square force pushes cards away as the cursor approaches, with quadratic falloff at the field boundary
  • Spring return — each card snaps back to its rest position via Hooke's law once the cursor moves away
  • Frame-rate-independent damping — velocity decay is corrected per frame so behaviour is identical at 30fps or 144fps
  • 3D tilt — the card closest to the cursor gets a rotateX/rotateY tilt proportional to cursor position for depth
  • Proximity glow — each card's border and box-shadow intensity scales with cursor distance via a --prox CSS custom property set directly on the DOM node
  • Variable card sizessize: 1 (1×1), size: 2 (2 columns wide), size: 3 (2 rows tall); bin-packed automatically
  • Metric display — optional big-number stat with label, prominently displayed on the card face and in the detail overlay
  • Custom magnetic cursor — replaces the system cursor with a dot + ring + 4-directional field lines
  • Built-in detail overlay — click a non-flying card to open an animated full-detail panel; press Escape or click outside to close
  • onCardClick override — provide your own handler to skip the built-in overlay
  • Background grid — fine + major grid lines plus centre crosshairs for a technical HUD aesthetic
  • ESM + CJS dual build — works in Vite, Next.js, CRA, and any modern bundler
  • Full TypeScript types included

Installation

npm install awesome-magnetic-grid
# or
yarn add awesome-magnetic-grid
# or
pnpm add awesome-magnetic-grid

Simple Example

Watch the Demo

Peer dependencies — React ≥ 18 and ReactDOM ≥ 18 must already be installed.


Importing Styles

The component relies on CSS custom properties (--bg, --bg-card, --border, --text, --text-dim, --text-dimmer, --font-display, --font-mono, --font-body) and keyframe animations (pulseRing, overlayIn, panelIn). Import the stylesheet once at your app root:

import 'awesome-magnetic-grid/style.css';

For the full typographic effect, load these Google Fonts in your index.html <head>:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=DM+Mono:wght@300;400&family=DM+Sans:wght@300;400;500&display=swap"
  rel="stylesheet"
/>

Falls back to sans-serif/monospace without the fonts — the component still renders correctly.


Quick Start

import { AwesomeMagneticGrid } from 'awesome-magnetic-grid';
import 'awesome-magnetic-grid/style.css';

var cards = [
  { id: 1,  title: 'Velocity',   subtitle: 'Real-time stream processor', tag: 'CORE',    accent: '#00ff88', metric: '12ms',  metricLabel: 'Latency' },
  { id: 2,  title: 'Spectrum',   subtitle: 'Frequency analyser',          tag: 'AUDIO',   accent: '#7c3aed', metric: '96k',   metricLabel: 'Sample Rate' },
  { id: 3,  title: 'Axiom',      subtitle: 'Rule engine v3',              tag: 'LOGIC',   accent: '#f97316', size: 2 },
  { id: 4,  title: 'Nexus',      subtitle: 'Inter-service message bus',   tag: 'NETWORK', accent: '#38bdf8', metric: '2.4M',  metricLabel: 'msg/s', size: 3 },
  { id: 5,  title: 'Quasar',     subtitle: 'Distributed query planner',   tag: 'QUERY',   accent: '#e879f9' },
  { id: 6,  title: 'Forge',      subtitle: 'Build pipeline orchestrator', tag: 'CI/CD',   accent: '#facc15', metric: '4.1s',  metricLabel: 'Avg Build' },
  { id: 7,  title: 'Cipher',     subtitle: 'End-to-end encryption layer', tag: 'SEC',     accent: '#f43f5e' },
  { id: 8,  title: 'Orbit',      subtitle: 'Scheduler & job runner',      tag: 'OPS',     accent: '#22d3ee', metric: '99.97', metricLabel: '% Uptime' },
];

export default function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <AwesomeMagneticGrid cards={cards} />
    </div>
  );
}

Props

| Prop | Type | Default | Description | |---|---|---|---| | cards | GridCard[] | required | Array of card objects. See GridCard below. | | cols | number | 4 | Number of grid columns. | | cardW | number | 220 | Base card width in px. Wide cards (size: 2) are 2 × cardW + gap. | | cardH | number | 160 | Base card height in px. Tall cards (size: 3) are 2 × cardH + gap. | | gap | number | 12 | Gap between cards in px. | | radius | number | 280 | Magnetic repulsion field radius in px. Cards outside this range are unaffected. | | strength | number | 1.0 | Repulsion force multiplier. Higher = more aggressive push. | | spring | number | 0.12 | Spring stiffness (Hooke constant). Higher = snappier return to rest. | | damping | number | 0.72 | Velocity damping per frame (frame-rate corrected). Lower = more oscillation. | | onCardClick | (card: GridCard) => void | — | If provided, fires on card click and skips the built-in detail overlay. |

GridCard object

| Field | Type | Required | Description | |---|---|---|---| | id | number | ✅ | Unique identifier. Used as React key and shown as #001 in the overlay. | | title | string | ✅ | Main heading on the card and in the detail overlay. | | subtitle | string | ➖ | Secondary line shown below the title. | | tag | string | ➖ | Small monospaced badge shown top-left of the card. | | metric | string | ➖ | Large stat or number displayed prominently (e.g. '12ms', '99.9%'). | | metricLabel | string | ➖ | Small label below the metric (e.g. 'Latency', 'Uptime'). | | body | string | ➖ | Body paragraph shown only in the detail overlay. | | accent | string | ✅ | Hex neon accent colour. Drives glow, borders, the top accent bar, metric colour, and the overlay highlight. | | size | 1 \| 2 \| 3 | ➖ | 1 = 1×1 (default), 2 = 2 columns wide, 3 = 2 rows tall. Cards are bin-packed automatically. |


Examples

Example 1 — Minimal, all size-1 cards

import { AwesomeMagneticGrid } from 'awesome-magnetic-grid';
import 'awesome-magnetic-grid/style.css';

var cards = [
  { id: 1,  title: 'Alpha',   accent: '#00ff88' },
  { id: 2,  title: 'Beta',    accent: '#7c3aed' },
  { id: 3,  title: 'Gamma',   accent: '#f97316' },
  { id: 4,  title: 'Delta',   accent: '#38bdf8' },
  { id: 5,  title: 'Epsilon', accent: '#e879f9' },
  { id: 6,  title: 'Zeta',    accent: '#facc15' },
  { id: 7,  title: 'Eta',     accent: '#f43f5e' },
  { id: 8,  title: 'Theta',   accent: '#22d3ee' },
];

export default function MinimalDemo() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <AwesomeMagneticGrid cards={cards} cols={4} />
    </div>
  );
}

Example 2 — Mixed sizes, metrics, and tuned physics

Wide and tall cards mixed into a 4-column layout, with a stronger repulsion field and snappier spring return.

import { AwesomeMagneticGrid } from 'awesome-magnetic-grid';
import 'awesome-magnetic-grid/style.css';

var cards = [
  {
    id: 1, title: 'Revenue',    tag: 'FINANCE',
    metric: '$4.2M', metricLabel: 'This Quarter',
    subtitle: 'Up 18% from last quarter',
    accent: '#00ff88', size: 2,
  },
  {
    id: 2, title: 'Uptime',     tag: 'OPS',
    metric: '99.97', metricLabel: '% SLA',
    subtitle: '43 days without incident',
    accent: '#22d3ee', size: 3,
  },
  {
    id: 3, title: 'Users',      tag: 'GROWTH',
    metric: '1.4M', metricLabel: 'Active Monthly',
    accent: '#a855f7',
  },
  {
    id: 4, title: 'Latency',    tag: 'PERF',
    metric: '8ms',  metricLabel: 'p99',
    accent: '#f97316',
  },
  {
    id: 5, title: 'Deploys',    tag: 'CI/CD',
    metric: '312',  metricLabel: 'Last 30 Days',
    subtitle: 'Zero rollbacks',
    accent: '#facc15',
  },
  {
    id: 6, title: 'Errors',     tag: 'MONITOR',
    metric: '0.02', metricLabel: '% Error Rate',
    accent: '#f43f5e',
  },
  {
    id: 7, title: 'Storage',    tag: 'INFRA',
    metric: '18TB', metricLabel: 'Used / 40TB',
    accent: '#38bdf8',
  },
  {
    id: 8, title: 'API Calls',  tag: 'GATEWAY',
    metric: '84M',  metricLabel: 'Per Day',
    accent: '#e879f9', size: 2,
  },
];

export default function DashboardDemo() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <AwesomeMagneticGrid
        cards={cards}
        cols={4}
        cardW={220}
        cardH={160}
        radius={320}
        strength={1.4}
        spring={0.18}
        damping={0.68}
      />
    </div>
  );
}

Example 3 — onCardClick with a custom side panel

Bypasses the built-in overlay and renders a custom drawer instead. Uses a smaller card size and tighter physics for a compact feel.

import { useState } from 'react';
import { AwesomeMagneticGrid } from 'awesome-magnetic-grid';
import 'awesome-magnetic-grid/style.css';

var cards = [
  { id: 1,  title: 'Photon',   subtitle: 'Light-speed transport layer',  tag: 'NET',  accent: '#00ff88', body: 'Zero-copy packet forwarding with eBPF offload and DPDK ring buffers for sub-microsecond switching.' },
  { id: 2,  title: 'Prism',    subtitle: 'Spectral analytics engine',     tag: 'DATA', accent: '#7c3aed', body: 'Decomposes multi-dimensional metrics into frequency bands for anomaly isolation at any granularity.' },
  { id: 3,  title: 'Helios',   subtitle: 'Solar-powered edge node',       tag: 'EDGE', accent: '#f97316', body: 'Off-grid compute node harvesting 40W from photovoltaic cells, with supercapacitor burst storage.' },
  { id: 4,  title: 'Pulsar',   subtitle: 'Rotating event log',            tag: 'LOG',  accent: '#38bdf8', body: 'Append-only structured event store with magnetic-head inspired sequential read optimisation.' },
  { id: 5,  title: 'Flux',     subtitle: 'Real-time state sync',          tag: 'SYNC', accent: '#e879f9', body: 'CRDTs over WebTransport ensure eventual consistency across 150+ global edge PoPs in under 20ms.' },
  { id: 6,  title: 'Anchor',   subtitle: 'Distributed consensus',         tag: 'RAFT', accent: '#facc15', body: 'Modified Raft implementation with pre-vote phase to eliminate spurious leader elections.' },
];

var drawerStyle = {
  position: 'fixed',
  top: 0,
  right: 0,
  width: '360px',
  height: '100vh',
  background: '#0a0a0a',
  borderLeft: '1px solid rgba(255,255,255,0.08)',
  padding: '48px 32px',
  zIndex: 500,
  display: 'flex',
  flexDirection: 'column',
  gap: '20px',
  color: '#fff',
  fontFamily: 'sans-serif',
  overflowY: 'auto',
};

export default function CustomPanelDemo() {
  var [selected, setSelected] = useState(null);

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <AwesomeMagneticGrid
        cards={cards}
        cols={3}
        cardW={200}
        cardH={150}
        radius={240}
        strength={0.9}
        onCardClick={function(card) {
          setSelected(function(prev) { return prev && prev.id === card.id ? null : card; });
        }}
      />

      {selected && (
        <div style={drawerStyle}>
          {/* Close */}
          <button
            onClick={function() { setSelected(null); }}
            style={{
              alignSelf: 'flex-end', background: 'transparent',
              border: '1px solid rgba(255,255,255,0.15)',
              color: '#fff', borderRadius: 4,
              width: 28, height: 28, cursor: 'pointer', fontSize: 14,
            }}
          >✕</button>

          {/* Accent bar */}
          <div style={{ height: 2, borderRadius: 1, background: selected.accent, boxShadow: '0 0 12px ' + selected.accent }} />

          {/* Tag */}
          <span style={{
            alignSelf: 'flex-start',
            background: selected.accent + '18',
            border: '1px solid ' + selected.accent + '44',
            color: selected.accent,
            borderRadius: 2, padding: '3px 10px',
            fontSize: 10, fontWeight: 700,
            letterSpacing: '0.2em', textTransform: 'uppercase',
            fontFamily: 'monospace',
          }}>{selected.tag}</span>

          {/* Title */}
          <div style={{ fontSize: 36, fontWeight: 700, lineHeight: 1.1, letterSpacing: '0.04em' }}>
            {selected.title}
          </div>

          {/* Subtitle */}
          <div style={{ fontSize: 13, color: selected.accent, fontWeight: 500 }}>
            {selected.subtitle}
          </div>

          {/* Divider */}
          <div style={{ height: 1, background: 'rgba(255,255,255,0.08)' }} />

          {/* Body */}
          {selected.body && (
            <p style={{ fontSize: 13, lineHeight: 1.75, color: 'rgba(200,200,200,0.7)', fontWeight: 300 }}>
              {selected.body}
            </p>
          )}

          {/* ID */}
          <div style={{ marginTop: 'auto', fontSize: 10, color: 'rgba(255,255,255,0.2)', fontFamily: 'monospace', letterSpacing: '0.15em' }}>
            OBJECT #{String(selected.id).padStart(3, '0')}
          </div>
        </div>
      )}
    </div>
  );
}

Physics Tuning Guide

| Goal | Adjustment | |---|---| | More aggressive repulsion | Increase strength (try 1.52.0) | | Larger affected area | Increase radius (try 350450) | | Snappier return to rest | Increase spring (try 0.180.25) | | More oscillation / bounce | Decrease damping (try 0.600.68) | | Stiff, quick settle | Increase damping (try 0.800.88) | | Gentler, floatier feel | Decrease strength + spring, increase damping |


CSS Custom Properties

The stylesheet exposes these variables — override in your own :root:

:root {
  --bg:          #080808;
  --bg-card:     #101010;
  --border:      rgba(255, 255, 255, 0.08);
  --text:        #f0f0f0;
  --text-dim:    rgba(255, 255, 255, 0.45);
  --text-dimmer: rgba(255, 255, 255, 0.2);
  --font-display: 'Bebas Neue', sans-serif;
  --font-mono:    'DM Mono', monospace;
  --font-body:    'DM Sans', sans-serif;
}

License

MIT