Components / Diamond Gallery
Diamond Gallery
A unique image gallery with 4 images arranged in a diamond pattern. Features hover effects with scale, blur, and grayscale transitions.
Installation
Install the component using the shadcn CLI:
npx shadcn@latest add ChinmayNoob/fern-ui/diamond-galleryPreview
Hover over each image to see the focus effect.Images can optionally be wrapped in links for navigation.
Usage
tsx
import DiamondGallery from "@/components/diamond-gallery/diamond-gallery"
export function Example() {
return (
<div className="w-96 h-96">
<DiamondGallery
images={[
{ image: '/images/photo1.jpg', isLink: true, href: '/', target: '_blank' },
{ image: '/images/photo2.jpg', isLink: false },
{ image: '/images/photo3.jpg', isLink: false },
{ image: '/images/photo4.jpg', isLink: true, href: '/about' },
]}
/>
</div>
)
}Props
| Prop | Type | Description |
|---|---|---|
| images | ImageItem[] | Array of 4 images to display |
ImageItem Type
| Property | Type | Required | Description |
|---|---|---|---|
| image | string | Yes | Image source URL |
| isLink | boolean | No | Wrap image in anchor tag |
| href | string | No | Link destination URL |
| target | string | No | Link target (_blank, _self, etc.) |
| rel | string | No | Link rel attribute |
Features
- Diamond-shaped layout with 4 rotated images
- Hover effect: focused image scales up to 100%
- Other images blur and turn grayscale on hover
- Smooth 500ms transitions
- Optional link wrapping for each image
- Responsive - uses parent container dimensions
- CSS-only animations (no JS animation library)
Image Positions
Images are positioned in a diamond pattern:
images[0]- Top centerimages[1]- Left centerimages[2]- Right centerimages[3]- Bottom center
Notes
- Requires exactly 4 images for proper layout
- Parent container should have defined width/height
- Uses
aspect-squarefor consistent proportions - Images are rotated 45° and counter-rotated inside
Source Code
tsx
'use client'
import { useState } from 'react'
interface ImageItem {
image: string
isLink?: boolean
href?: string
target?: string
rel?: string
}
interface DiamondGalleryProps {
images: ImageItem[]
}
const DiamondGallery = ({ images }: DiamondGalleryProps) => {
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
const positions = [
'top-0 left-1/2 transform -translate-x-1/2 -translate-y-[5%]',
'left-0 top-1/2 transform -translate-y-1/2 -translate-x-[5%]',
'right-0 top-1/2 transform -translate-y-1/2 translate-x-[5%]',
'bottom-0 left-1/2 transform -translate-x-1/2 translate-y-[5%]'
]
return (
<div className='relative aspect-square h-full'>
{images.slice(0, 4).map((img: ImageItem, idx: number) => {
const imageElement = (
<img
src={img.image}
alt={`Gallery image ${idx + 1}`}
className='size-full object-cover -rotate-45 scale-150'
onMouseEnter={() => setHoveredIndex(idx)}
onMouseLeave={() => setHoveredIndex(null)}
/>
)
return (
<div
className={`absolute w-1/2 h-1/2 rotate-45 rounded-lg transition-all duration-500 overflow-hidden
${positions[idx]}
${hoveredIndex === idx ? 'scale-100 z-1' : 'scale-75 z-0'}
${hoveredIndex !== null && hoveredIndex !== idx
? 'filter blur-sm grayscale'
: ''
}`}
key={idx}
>
{img.isLink ? (
<a
href={img.href}
target={img.target}
rel={img.rel}
className='block size-full'
>
{imageElement}
</a>
) : (
imageElement
)}
</div>
)
})}
</div>
)
}
export default DiamondGallery;


