npx claudepluginhub shiqkuangsan/oh-my-daily-skillsThis skill uses the workspace's default tool permissions.
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`.
Builds static Neobrutalism landing pages with Blobity cursor, design tokens, theme toggle, i18n, 3D card tilt, and responsive components using pure HTML/CSS/JS, no build tools.
Provides 2024-2025 web design trends, principles, and CSS patterns for bold minimalism, performance optimization, accessibility, micro-interactions, and design systems.
Implements Tailwind CSS micro-interactions: hover card lifts, image scales, border glows, group hovers, and staggered animations for responsive UI feedback.
Share bugs, ideas, or general feedback.
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/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>
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.
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/blobity@0.2.4 → version doesn't exist
✓ esm.sh/blobity@0.2.3 → 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 |