@nirajrajput-dev/image-dominant-hover
v1.0.0
Published
React component that replicates YouTube's thumbnail hover effect with dominant color extraction
Maintainers
Readme
@niraj/image-dominant-hover
A lightweight React component library that replicates YouTube's thumbnail hover effect by extracting dominant colors from images and applying them as smooth background transitions.
✨ Features
- 🎨 Automatic Color Extraction - Canvas-based pixel sampling for accurate dominant color detection
- ⚡ Performance Optimized - Memory-based caching for instant color retrieval
- 🎬 Smooth Animations - Configurable transition effects that feel natural
- ♿ Accessible - WCAG AA compliant with proper ARIA attributes
- 📱 Responsive - Works seamlessly on desktop and mobile devices
- 🎯 TypeScript First - Full type definitions included
- 🪶 Lightweight - Zero dependencies (React is a peer dependency)
📦 Installation
npm install @niraj/image-dominant-hover🚀 Quick Start
import { ImageCard } from "@niraj/image-dominant-hover";
function App() {
return (
<ImageCard
src="https://example.com/image.jpg"
alt="Beautiful landscape"
title="Mountain View"
description="A stunning mountain landscape"
/>
);
}📖 API Reference
ImageCard Props
| Prop | Type | Default | Description |
| ------------------------ | --------------------------- | ----------- | ---------------------------------------- |
| src | string | required | Image source URL |
| alt | string | required | Alt text for accessibility |
| title | string | undefined | Optional title displayed below image |
| description | string | undefined | Optional description text |
| width | number \| string | 300 | Card width (px or CSS string) |
| height | number \| string | 180 | Image height (px or CSS string) |
| className | string | "" | Additional CSS classes |
| transitionDuration | number | 300 | Transition duration in milliseconds |
| onClick | () => void | undefined | Click handler (makes card interactive) |
| onColorExtracted | (color: RGBColor) => void | undefined | Callback when color extraction completes |
| onColorExtractionError | (error: Error) => void | undefined | Callback when extraction fails |
RGBColor Type
interface RGBColor {
r: number; // 0-255
g: number; // 0-255
b: number; // 0-255
}🎯 Usage Examples
Basic Usage
import { ImageCard } from "@niraj/image-dominant-hover";
<ImageCard src="/images/sunset.jpg" alt="Sunset over ocean" />;With Custom Dimensions
<ImageCard
src="/images/portrait.jpg"
alt="Portrait photo"
width={400}
height={600}
/>With Click Handler
<ImageCard
src="/images/product.jpg"
alt="Product showcase"
title="Premium Headphones"
description="Experience audio perfection"
onClick={() => console.log("Card clicked!")}
/>With Callbacks
<ImageCard
src="/images/artwork.jpg"
alt="Digital artwork"
onColorExtracted={(color) => {
console.log(`Dominant color: rgb(${color.r}, ${color.g}, ${color.b})`);
}}
onColorExtractionError={(error) => {
console.error("Color extraction failed:", error);
}}
/>Custom Transition Speed
<ImageCard
src="/images/nature.jpg"
alt="Nature scene"
transitionDuration={500} // Slower transition
/>Responsive Width
<ImageCard
src="/images/banner.jpg"
alt="Banner image"
width="100%"
height="auto"
/>🛠️ Utility Functions
The library also exports utility functions for advanced use cases:
extractDominantColor
Extract dominant color from any image URL:
import { extractDominantColor } from "@niraj/image-dominant-hover";
const color = await extractDominantColor("https://example.com/image.jpg");
console.log(color); // { r: 128, g: 64, b: 192 }rgbToString
Convert RGB object to CSS string:
import { rgbToString } from "@niraj/image-dominant-hover";
const cssColor = rgbToString({ r: 255, g: 128, b: 0 });
console.log(cssColor); // "rgb(255, 128, 0)"calculateBrightness
Calculate perceived brightness (0-1):
import { calculateBrightness } from "@niraj/image-dominant-hover";
const brightness = calculateBrightness({ r: 128, g: 128, b: 128 });
console.log(brightness); // ~0.215 (WCAG relative luminance)Cache Management
import { getCacheSize, clearColorCache } from "@niraj/image-dominant-hover";
console.log(getCacheSize()); // Number of cached colors
clearColorCache(); // Clear all cached colors🎨 Styling
The component uses inline styles by default, but you can customize appearance using the className prop:
<ImageCard src="/images/photo.jpg" alt="Photo" className="my-custom-card" />.my-custom-card {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border: 2px solid #e0e0e0;
}
.my-custom-card:hover {
transform: scale(1.05);
}⚙️ How It Works
- Image Load: Component renders with the provided image
- Color Extraction: Canvas API samples center region pixels (asynchronously)
- Caching: Extracted color is cached in memory for instant future use
- Hover Effect: On hover, background smoothly transitions to the dominant color
- Performance: Subsequent hovers use cached color (no re-extraction)
Color Extraction Algorithm
- Samples from the center 50% of the image (most representative area)
- Skips transparent, very bright, and very dark pixels (noise reduction)
- Uses pixel averaging for accurate color representation
- Limits canvas size to 200×200px for optimal performance
🌐 Browser Support
- Chrome/Edge: ✅ Latest 2 versions
- Firefox: ✅ Latest 2 versions
- Safari: ✅ iOS 14+, macOS latest 2 versions
- Modern browsers with ES2020+ support
Note: Canvas API and crossOrigin support required for external images.
📝 CORS Considerations
For external images, ensure the server sends proper CORS headers:
Access-Control-Allow-Origin: *Images without CORS headers will fail color extraction. Use same-origin images or configure your CDN appropriately.
🤝 Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
📄 License
MIT © Niraj Rajput
🔗 Links
- Demo (Coming soon)
- GitHub Repository
- npm Package
- Report Issues
🙏 Acknowledgments
Inspired by YouTube's thumbnail hover effect. Built with React, TypeScript, and Canvas API.
