From 0d0b4a5cd14cf09a28dd8de18209198e6799085d Mon Sep 17 00:00:00 2001 From: GuiEpi Date: Fri, 11 Jul 2025 17:42:07 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20`normalizeColor`=20utilit?= =?UTF-8?q?y=20and=20support=20multiple=20formats=20for=20`defaultColor`?= =?UTF-8?q?=20prop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.tsx | 14 +++++------ src/types.ts | 2 +- src/utils.ts | 39 ++++++++++++++++++++++++++++++ website/src/pages/docs/cursor.mdx | 28 +++++++++++++++++++-- website/src/pages/docs/styling.mdx | 25 +++++++++++++++---- 5 files changed, 93 insertions(+), 15 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 45bdb75..c86f611 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { motion } from 'motion/react'; import { CursorProps, Position, CursorRule, CursorVariant, CursorType, CursorTheme } from './types'; -import { isMobile, isInteractive, isText } from './utils'; +import { isMobile, isInteractive, isText, normalizeColor } from './utils'; const Cursor: React.FC = ({ zIndex = 9999, @@ -30,11 +30,11 @@ const Cursor: React.FC = ({ } }, [currentVariant]); - const defaultColor = useMemo( - () => - customDefaultColor || (window.matchMedia('(prefers-color-scheme: dark)').matches ? '0 0% 98%' : '240 10% 3.9%'), - [customDefaultColor], - ); + const defaultColor = useMemo(() => { + const fallbackColor = window.matchMedia('(prefers-color-scheme: dark)').matches ? '0 0% 98%' : '240 10% 3.9%'; + const colorToNormalize = customDefaultColor || fallbackColor; + return normalizeColor(colorToNormalize); + }, [customDefaultColor]); const defaultVariants: Record = { default: { @@ -185,7 +185,7 @@ const Cursor: React.FC = ({ left: position.x, pointerEvents: 'none', zIndex, - backgroundColor: `hsl(var(--cursor-color, ${defaultColor}))`, + backgroundColor: `var(--cursor-color, ${defaultColor})`, transformOrigin: 'left center', overflow: 'hidden', // Important for the compression effect }} diff --git a/src/types.ts b/src/types.ts index ff80ad6..9698227 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,7 +47,7 @@ export interface CursorProps { theme?: CursorTheme; /** Whether to scale cursor on click */ scaleOnClick?: boolean; - /** Default color in HSL format */ + /** Default color (supports HSL, RGB, OKLCH, hex, named colors, etc.) */ defaultColor?: string; } diff --git a/src/utils.ts b/src/utils.ts index 961f198..9217b25 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,44 @@ import { INTERACTIVE_ELEMENTS, TEXT_ELEMENTS } from './constants'; +/** Normalize different color formats to CSS-compatible string */ +export const normalizeColor = (color: string): string => { + // Trim whitespace + const trimmedColor = color.trim(); + + // If it's already a CSS function (hsl, rgb, oklch, etc.), return as-is + if (/^(hsl|rgb|oklch|lab|lch|color|hwb|hsv|cmyk)\(/i.test(trimmedColor)) { + return trimmedColor; + } + + // If it's a hex color, return as-is + if (/^#[0-9a-fA-F]{3,8}$/.test(trimmedColor)) { + return trimmedColor; + } + + // If it's a named color, return as-is + if (/^[a-zA-Z]+$/.test(trimmedColor)) { + return trimmedColor; + } + + // If it looks like HSL values without function wrapper (e.g., "240 10% 3.9%") + if (/^\d+\s+\d+%\s+\d+%/.test(trimmedColor)) { + return `hsl(${trimmedColor})`; + } + + // If it looks like RGB values without function wrapper (e.g., "255 0 0") + if (/^\d+\s+\d+\s+\d+/.test(trimmedColor)) { + return `rgb(${trimmedColor})`; + } + + // If it looks like OKLCH values without function wrapper (e.g., "0.7 0.15 180") + if (/^\d*\.?\d+\s+\d*\.?\d+\s+\d+/.test(trimmedColor)) { + return `oklch(${trimmedColor})`; + } + + // Default: assume it's HSL format and wrap it + return `hsl(${trimmedColor})`; +}; + /** Check if the user is on a mobile device */ export const isMobile = (): boolean => { return /iPhone|iPad|iPod|Android|WebOS/i.test(navigator.userAgent); diff --git a/website/src/pages/docs/cursor.mdx b/website/src/pages/docs/cursor.mdx index b007a48..fd45436 100644 --- a/website/src/pages/docs/cursor.mdx +++ b/website/src/pages/docs/cursor.mdx @@ -36,7 +36,7 @@ This component will render the cursor. [defaultColor](#default-color-prop) string auto - Default HSL color value + Default color (supports multiple formats) @@ -96,11 +96,35 @@ If `false`, do not scale cursor when clicking. Defaults to `true`. ### `defaultColor` Prop -Change the default cursor color. The value should be in **HSL format**. By default, the cursor uses the HSL values from the Tailwind CSS color palette: +Change the default cursor color. The value supports **multiple color formats** including HSL, RGB, OKLCH, hex, and named colors. By default, the cursor uses the HSL values from the Tailwind CSS color palette: - Dark mode: `0 0% 98%` (zinc-50) - Light mode: `240 10% 3.9%` (zinc-950) +**Supported color formats:** + ```jsx +// HSL (with or without function wrapper) + + +// RGB (with or without function wrapper) + + + +// OKLCH (with or without function wrapper) + + + +// Hex colors + + + +// Named colors + + + +// Other CSS color functions + + ``` diff --git a/website/src/pages/docs/styling.mdx b/website/src/pages/docs/styling.mdx index 42ff253..637d2db 100644 --- a/website/src/pages/docs/styling.mdx +++ b/website/src/pages/docs/styling.mdx @@ -4,28 +4,43 @@ ### Using CSS Variables -You can style the cursor by setting the `--cursor-color` CSS variable. The value should be in **HSL format**. +You can style the cursor by setting the `--cursor-color` CSS variable. The value supports **multiple color formats** including HSL, RGB, OKLCH, hex, and named colors. ```css +/* HSL format (legacy support) */ :root { --cursor-color: 349, 80%, 59%; } + +/* Or any other supported color format */ +:root { + --cursor-color: #ff4081; /* Hex */ + --cursor-color: rgb(255, 64, 129); /* RGB */ + --cursor-color: oklch(0.7 0.15 330); /* OKLCH */ + --cursor-color: hotpink; /* Named color */ +} ``` ### Dark Mode Support -The cursor automatically detects system dark mode preferences and adjusts its color. You can override this behavior: +The cursor automatically detects system dark mode preferences and adjusts its color. You can override this behavior using any supported color format: ```css /* Light mode */ :root { - --cursor-color: 240, 10%, 3.9%; + --cursor-color: 240, 10%, 3.9%; /* HSL */ + /* Or use other formats: */ + /* --cursor-color: #0a0a0b; */ + /* --cursor-color: oklch(0.15 0.02 240); */ } /* Dark mode */ @media (prefers-color-scheme: dark) { :root { - --cursor-color: 0, 0%, 98%; + --cursor-color: 0, 0%, 98%; /* HSL */ + /* Or use other formats: */ + /* --cursor-color: #fafafa; */ + /* --cursor-color: oklch(0.98 0 0); */ } } ``` @@ -103,7 +118,7 @@ const theme: CursorTheme = { padding: '1em', // Optional: override color for this variant - backgroundColor: 'hsl(160, 100%, 50%)', + backgroundColor: 'hsl(160, 100%, 50%)', // Or any other format: '#00ff80', 'oklch(0.8 0.2 150)', etc. }, // Optional: content styling content: { From 43cba8d4d1533af609737b774a4f03c38856f033 Mon Sep 17 00:00:00 2001 From: GuiEpi Date: Fri, 11 Jul 2025 17:54:41 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20cursor=20color=20forma?= =?UTF-8?q?t=20to=20HSL=20in=20CSS=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/src/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/index.css b/website/src/index.css index f3a90ea..7059e39 100644 --- a/website/src/index.css +++ b/website/src/index.css @@ -29,7 +29,7 @@ --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; - --cursor-color: 349, 80%, 59%; + --cursor-color: hsl(349, 80%, 59%); } .dark { --background: 240 10% 3.9%; @@ -56,7 +56,7 @@ --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; - --cursor-color: 349, 80%, 59%; + --cursor-color: hsl(349, 80%, 59%); } } From 955b45cb490cb347b023e6475744685344bfe160 Mon Sep 17 00:00:00 2001 From: GuiEpi Date: Fri, 11 Jul 2025 18:25:47 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=A5=20Remove=20auto-detection=20lo?= =?UTF-8?q?gic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.tsx | 9 ++++--- src/types.ts | 2 +- src/utils.ts | 39 ------------------------------ website/src/pages/docs/cursor.mdx | 21 ++++++++-------- website/src/pages/docs/styling.mdx | 23 +++++------------- 5 files changed, 22 insertions(+), 72 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index c86f611..fcfc003 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { motion } from 'motion/react'; import { CursorProps, Position, CursorRule, CursorVariant, CursorType, CursorTheme } from './types'; -import { isMobile, isInteractive, isText, normalizeColor } from './utils'; +import { isMobile, isInteractive, isText } from './utils'; const Cursor: React.FC = ({ zIndex = 9999, @@ -31,9 +31,10 @@ const Cursor: React.FC = ({ }, [currentVariant]); const defaultColor = useMemo(() => { - const fallbackColor = window.matchMedia('(prefers-color-scheme: dark)').matches ? '0 0% 98%' : '240 10% 3.9%'; - const colorToNormalize = customDefaultColor || fallbackColor; - return normalizeColor(colorToNormalize); + const fallbackColor = window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'hsl(0 0% 98%)' + : 'hsl(240 10% 3.9%)'; + return customDefaultColor || fallbackColor; }, [customDefaultColor]); const defaultVariants: Record = { diff --git a/src/types.ts b/src/types.ts index 9698227..ea96c69 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,7 +47,7 @@ export interface CursorProps { theme?: CursorTheme; /** Whether to scale cursor on click */ scaleOnClick?: boolean; - /** Default color (supports HSL, RGB, OKLCH, hex, named colors, etc.) */ + /** Default color in valid CSS format (e.g., 'hsl(240 10% 3.9%)', '#ff4081', 'rgb(255 64 129)', 'oklch(0.7 0.15 330)') */ defaultColor?: string; } diff --git a/src/utils.ts b/src/utils.ts index 9217b25..961f198 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,44 +1,5 @@ import { INTERACTIVE_ELEMENTS, TEXT_ELEMENTS } from './constants'; -/** Normalize different color formats to CSS-compatible string */ -export const normalizeColor = (color: string): string => { - // Trim whitespace - const trimmedColor = color.trim(); - - // If it's already a CSS function (hsl, rgb, oklch, etc.), return as-is - if (/^(hsl|rgb|oklch|lab|lch|color|hwb|hsv|cmyk)\(/i.test(trimmedColor)) { - return trimmedColor; - } - - // If it's a hex color, return as-is - if (/^#[0-9a-fA-F]{3,8}$/.test(trimmedColor)) { - return trimmedColor; - } - - // If it's a named color, return as-is - if (/^[a-zA-Z]+$/.test(trimmedColor)) { - return trimmedColor; - } - - // If it looks like HSL values without function wrapper (e.g., "240 10% 3.9%") - if (/^\d+\s+\d+%\s+\d+%/.test(trimmedColor)) { - return `hsl(${trimmedColor})`; - } - - // If it looks like RGB values without function wrapper (e.g., "255 0 0") - if (/^\d+\s+\d+\s+\d+/.test(trimmedColor)) { - return `rgb(${trimmedColor})`; - } - - // If it looks like OKLCH values without function wrapper (e.g., "0.7 0.15 180") - if (/^\d*\.?\d+\s+\d*\.?\d+\s+\d+/.test(trimmedColor)) { - return `oklch(${trimmedColor})`; - } - - // Default: assume it's HSL format and wrap it - return `hsl(${trimmedColor})`; -}; - /** Check if the user is on a mobile device */ export const isMobile = (): boolean => { return /iPhone|iPad|iPod|Android|WebOS/i.test(navigator.userAgent); diff --git a/website/src/pages/docs/cursor.mdx b/website/src/pages/docs/cursor.mdx index fd45436..8e30f81 100644 --- a/website/src/pages/docs/cursor.mdx +++ b/website/src/pages/docs/cursor.mdx @@ -36,7 +36,7 @@ This component will render the cursor. [defaultColor](#default-color-prop) string auto - Default color (supports multiple formats) + Default color in valid CSS format @@ -96,24 +96,23 @@ If `false`, do not scale cursor when clicking. Defaults to `true`. ### `defaultColor` Prop -Change the default cursor color. The value supports **multiple color formats** including HSL, RGB, OKLCH, hex, and named colors. By default, the cursor uses the HSL values from the Tailwind CSS color palette: +Change the default cursor color using any **valid CSS color format**. By default, the cursor uses HSL colors from the Tailwind CSS color palette: -- Dark mode: `0 0% 98%` (zinc-50) -- Light mode: `240 10% 3.9%` (zinc-950) +- Dark mode: `hsl(0 0% 98%)` (zinc-50) +- Light mode: `hsl(240 10% 3.9%)` (zinc-950) -**Supported color formats:** +**Examples with explicit CSS color formats:** ```jsx -// HSL (with or without function wrapper) - +// HSL colors + -// RGB (with or without function wrapper) - +// RGB colors + -// OKLCH (with or without function wrapper) - +// OKLCH colors // Hex colors diff --git a/website/src/pages/docs/styling.mdx b/website/src/pages/docs/styling.mdx index 637d2db..bd6caf2 100644 --- a/website/src/pages/docs/styling.mdx +++ b/website/src/pages/docs/styling.mdx @@ -4,18 +4,13 @@ ### Using CSS Variables -You can style the cursor by setting the `--cursor-color` CSS variable. The value supports **multiple color formats** including HSL, RGB, OKLCH, hex, and named colors. +You can style the cursor by setting the `--cursor-color` CSS variable using any **valid CSS color format**. ```css -/* HSL format (legacy support) */ -:root { - --cursor-color: 349, 80%, 59%; -} - -/* Or any other supported color format */ :root { + --cursor-color: hsl(349 80% 59%); /* HSL */ --cursor-color: #ff4081; /* Hex */ - --cursor-color: rgb(255, 64, 129); /* RGB */ + --cursor-color: rgb(255 64 129); /* RGB */ --cursor-color: oklch(0.7 0.15 330); /* OKLCH */ --cursor-color: hotpink; /* Named color */ } @@ -23,24 +18,18 @@ You can style the cursor by setting the `--cursor-color` CSS variable. The value ### Dark Mode Support -The cursor automatically detects system dark mode preferences and adjusts its color. You can override this behavior using any supported color format: +The cursor automatically detects system dark mode preferences and adjusts its color. You can override this behavior using any valid CSS color format: ```css /* Light mode */ :root { - --cursor-color: 240, 10%, 3.9%; /* HSL */ - /* Or use other formats: */ - /* --cursor-color: #0a0a0b; */ - /* --cursor-color: oklch(0.15 0.02 240); */ + --cursor-color: hsl(240 10% 3.9%); } /* Dark mode */ @media (prefers-color-scheme: dark) { :root { - --cursor-color: 0, 0%, 98%; /* HSL */ - /* Or use other formats: */ - /* --cursor-color: #fafafa; */ - /* --cursor-color: oklch(0.98 0 0); */ + --cursor-color: hsl(0 0% 98%); } } ```