From oh-my-daily-skills
Adds Blobity canvas cursor effects with spring physics and color inversion to desktop landing pages using HTML, React, Vue 3/2, or Astro. Supports tooltips, light/dark themes, and touch-device skipping.
How this skill is triggered — by the user, by Claude, or both
Slash command
/oh-my-daily-skills:blobity-cursorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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`.
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.
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/[email protected]";
// 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>
CDN (no build tool):
<script type="module">
import Blobity from "https://esm.sh/[email protected]";
</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.
The mix-blend-mode: difference formula is |page_pixel - canvas_pixel|. This means:
Key rule: dark bg uses white color, light bg uses a dark color calculated from your target tint.
| 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 |
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.
| 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 |
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).
✗ cdn.jsdelivr.net/npm/blobity@latest/lib/blobity.min.js → wrong path
✗ cdn.blobity.dev/by.js → server down
✗ cdn.jsdelivr.net/npm/[email protected] → version doesn't exist
✓ esm.sh/[email protected] → works
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).
Always skip Blobity on touch devices — there's no mouse cursor to replace:
const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
if (isTouchDevice) return;
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
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
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.
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 },
);
| 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 |
npx claudepluginhub shiqkuangsan/oh-my-daily-skillsProvides 2024-2025 web design trends, principles, and CSS patterns for bold minimalism, performance optimization, accessibility, micro-interactions, and design systems.
Creates decorative accent animations like floating shapes, glowing orbs, animated borders, sparkle effects, and UI embellishments for visual polish in React interfaces.
Implements subtle animations and hover effects for interface feedback and delight using Tailwind CSS transition utilities.