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-gallery

Preview

Hover over each image to see the focus effect.Images can optionally be wrapped in links for navigation.

Imagen de galería 1
Imagen de galería 2
Imagen de galería 3
Imagen de galería 4

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

PropTypeDescription
imagesImageItem[]Array of 4 images to display

ImageItem Type

PropertyTypeRequiredDescription
imagestringYesImage source URL
isLinkbooleanNoWrap image in anchor tag
hrefstringNoLink destination URL
targetstringNoLink target (_blank, _self, etc.)
relstringNoLink 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 center
  • images[1] - Left center
  • images[2] - Right center
  • images[3] - Bottom center

Notes

  • Requires exactly 4 images for proper layout
  • Parent container should have defined width/height
  • Uses aspect-square for 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;