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.
Preview
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;


