tooyoung:blobity-cursor
Blobity Cursor
Add a canvas-based custom cursor effect to landing pages. The cursor follows the mouse with spring physics, expands to wrap interactive elements, and inverts colors via mix-blend-mode: difference.
Core mechanism: Blobity creates a <canvas> overlay (position: fixed, pointer-events: none, z-index: max) and draws a blob that follows the cursor using the Kinet spring physics engine. With invert: true, the canvas uses mix-blend-mode: difference to create a color-inversion effect.
Quick Start (HTML)
Minimal working example — copy into any HTML page:
<!-- Hide default cursor -->
<style>
body.blobity-active,
body.blobity-active a,
body.blobity-active button,
body.blobity-active [data-blobity],
body.blobity-active [data-blobity-tooltip] {
cursor: none !important;
}
</style>
<!-- Load Blobity via ESM CDN (npm: import Blobity from 'blobity') -->
<script type="module">
import Blobity from "https://esm.sh/blobity@0.2.3";
// Skip touch devices
if ("ontouchstart" in window || navigator.maxTouchPoints > 0) {
throw new Error("Touch device — skip Blobity");
}
const blobity = new Blobity({
licenseKey: "opensource",
invert: true,
zIndex: 50,
color: "#ffffff", // Canvas fill → difference with dark bg = light
dotColor: "#10b981", // Resting cursor dot color
radius: 6,
magnetic: false,
mode: "normal",
focusableElements: "a, button, [data-blobity], [data-blobity-tooltip]",
focusableElementsOffsetX: 5,
focusableElementsOffsetY: 4,
font: "'JetBrains Mono', monospace",
fontSize: 16,
fontWeight: 600,
fontColor: "#0d1117", // Tooltip text color on canvas
tooltipPadding: 12,
});
document.body.classList.add("blobity-active");
</script>
Add data-blobity-tooltip="Label text" to any element for tooltip mode:
<div class="card" data-blobity-tooltip="View details">...</div>
Installation
CDN (no build tool):
<script type="module">
import Blobity from "https://esm.sh/blobity@0.2.3";
</script>
Other CDNs (
cdn.jsdelivr.net,cdn.blobity.dev) have known 404/connection issues. Useesm.sh.
npm (with bundler):
# pnpm (recommended)
pnpm add blobity
# npm
npm install blobity
# yarn
yarn add blobity
import Blobity from "blobity";
Blobity has
reactandvueas optional peer dependencies. Ignore the warning if you're not using their bindings.
Theme Adaptation (Light/Dark)
The mix-blend-mode: difference formula is |page_pixel - canvas_pixel|. This means:
- Dark page + white canvas → light result (standard inversion) ✓
- Light page + dark canvas → light result (soft tint) ✓
- Light page + white canvas → black result (harsh) ✗
Key rule: dark bg uses white color, light bg uses a dark color calculated from your target tint.
Recommended Color Scheme
| Option | Dark Mode | Light Mode | Notes |
|---|---|---|---|
color |
#ffffff |
#190a11 |
Light mode produces soft mint #e6f5ee on white |
dotColor |
#10b981 |
#111827 |
Accent green / dark gray |
fontColor |
#0d1117 |
#000000 |
Tooltip text: dark→black on light bg, light→white on dark bg after difference |
Theme Switching Pattern
Watch for theme attribute changes and update Blobity options dynamically:
const isDark = () =>
document.documentElement.getAttribute("data-theme") !== "light";
// Alternative checks:
// document.documentElement.classList.contains('dark')
// window.matchMedia('(prefers-color-scheme: dark)').matches
const observer = new MutationObserver(() => {
blobity.updateOptions({
color: isDark() ? "#ffffff" : "#190a11",
dotColor: isDark() ? "#10b981" : "#111827",
fontColor: isDark() ? "#0d1117" : "#000000",
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme", "class"],
});
To calculate custom light-mode colors, see
references/color-math.md.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
licenseKey |
string | — | Use 'opensource' for open-source projects |
invert |
boolean | false |
Enable mix-blend-mode: difference on canvas |
color |
string | '#000000' |
Canvas fill color when hovering focusable elements |
dotColor |
string | '#000000' |
Resting cursor dot color |
radius |
number | 4 |
Dot radius in pixels |
magnetic |
boolean | true |
Whether cursor snaps to element center on hover |
mode |
string | 'normal' |
Cursor mode |
zIndex |
number | -1 |
Canvas z-index |
focusableElements |
string | 'a, button' |
CSS selector for interactive elements |
focusableElementsOffsetX |
number | 0 |
Horizontal padding when wrapping elements |
focusableElementsOffsetY |
number | 0 |
Vertical padding when wrapping elements |
font |
string | — | Tooltip font family |
fontSize |
number | 16 |
Tooltip font size |
fontWeight |
number | 400 |
Tooltip font weight |
fontColor |
string | '#000000' |
Tooltip text color on canvas |
tooltipPadding |
number | 4 |
Tooltip inner padding |
Tooltip Mode
Add data-blobity-tooltip to elements — cursor morphs into a text label instead of expanding:
<div class="step-card" data-blobity-tooltip="Step 1: Upload">...</div>
<a href="/docs" data-blobity-tooltip="Documentation">Docs</a>
Tooltip elements should be included in focusableElements via [data-blobity-tooltip] selector.
Light mode tooltip text visibility: tooltip background gets darkened by difference blend, so fontColor must also produce a light result after blending. Use #000000 for light mode (becomes white after |#fff - #000| = #fff).
Common Pitfalls
1. CDN 404 Errors
✗ cdn.jsdelivr.net/npm/blobity@latest/lib/blobity.min.js → wrong path
✗ cdn.blobity.dev/by.js → server down
✗ cdn.jsdelivr.net/npm/blobity@0.2.4 → version doesn't exist
✓ esm.sh/blobity@0.2.3 → works
2. Cursor Invisible or Pure Black on Light Background
This is a mix-blend-mode: difference color math issue. See the Theme Adaptation section — use a dark color value for light backgrounds (NOT white).
3. Touch Device Detection
Always skip Blobity on touch devices — there's no mouse cursor to replace:
const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
if (isTouchDevice) return;
4. SPA Page Navigation Cleanup
Blobity must be destroyed on route change to avoid canvas leaks:
// Astro view transitions
document.addEventListener(
"astro:before-swap",
() => {
observer.disconnect();
document.body.classList.remove("blobity-active");
blobity.destroy();
},
{ once: true },
);
// React Router / Vue Router
onUnmounted(() => {
blobity.destroy();
});
// Or useEffect cleanup in React
5. Peer Dependency Warnings
Blobity declares react and vue as peer deps. Safe to ignore if not using their framework bindings:
# pnpm
pnpm add blobity --no-strict-peer-dependencies
# npm (if needed)
npm install blobity --legacy-peer-deps
6. z-index Conflicts
Set zIndex high enough to overlay page content but below modals/dialogs. 50 works for most cases. If your site has a sticky header at z-index: 100+, either raise Blobity's value or accept the cursor rendering behind the header.
Scroll Bounce
Add a playful bounce effect when user scrolls:
let scrollTimeout = null;
window.addEventListener(
"scroll",
() => {
if (scrollTimeout) return;
scrollTimeout = setTimeout(() => {
blobity.bounce();
scrollTimeout = null;
}, 150);
},
{ passive: true },
);
References
| File | Content |
|---|---|
references/frameworks.md |
React hook, Vue 3 composable, Vue 2 mixin — complete templates |
references/color-math.md |
mix-blend-mode: difference color calculation, lookup table, reverse formula |
More from shiqkuangsan/oh-my-daily-skills
tooyoung:excalidraw-artist
Create Excalidraw hand-drawn style diagrams, including architecture, flowchart, swimlane/timeline, sequence, basic wireframe, ERD/data model, state machine, matrix/comparison table, tree/hierarchy, and CI/CD pipeline. Trigger words: draw diagram, architecture diagram, flowchart, swimlane, timeline, roadmap, Gantt, sequence diagram, excalidraw, ERD, data model, state machine, comparison table, matrix, tree, hierarchy, CI/CD pipeline
24tooyoung:chainlit-builder
Quickly build Chainlit AI chat demos and POCs using OpenAI-compatible chat completion patterns, including streaming, multi-turn memory, file upload, tool-call step visualization, and demo styling. Trigger words: chainlit, build demo, chat demo, conversation demo, Chainlit 演示, AI 聊天 demo, 对话式 POC
24tooyoung:threejs-builder
Create simple Three.js web apps with scene setup, lighting, geometries, materials, animations, OrbitControls, particles, and responsive rendering. Use for Three.js scenes, WebGL demos, 3D showcases, and interactive 3D web content. Trigger: threejs, Three.js, 3D scene, WebGL, 三维展示, 3D showcase, interactive 3D
23tooyoung:openclash-merger
将 vless+reality 等新协议配置转换为带 GEOSITE 规则的配置文件,支持 11 地区分组 + AI/媒体/游戏分流,可直接上传 OpenClash 使用。触发词:合并 OpenClash、转换订阅、Clash 配置
23tooyoung:nano-banana-builder
Build Next.js App Router image-generation apps using Gemini Nano Banana / Nano Banana Pro with AI SDK. Covers exact model names, Server Actions/API routes, conversational multi-turn image editing, storage, rate limiting, safety, and cost controls. Trigger: nano banana, Gemini image, AI 生图, 图片生成, text-to-image, image generation app, iterative image editor, multi-turn image editing
23tooyoung:easy-openrouter
Test individual LLM models through OpenRouter and compare observed latency, cost, token usage, and outputs. Includes model ID format, :nitro/:online modifiers, rankings/provider lookup, and simple manual comparison workflows. Trigger words: OpenRouter, test model, model ID, compare models, provider latency, throughput, cheapest provider, fastest provider, :nitro, :online
22