Feature 01

List of items: select one to reveal its description and media.

Full screen

feature-01.tsx

'use client'

import { useState } from 'react'

import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'

const items = [
  {
    title: 'Design',
    description:
      'Clean, accessible components that make your product feel modern and easy to use.',
    image:
      'https://images.unsplash.com/photo-1631125915800-4008fc1072ec?q=80&w=1133&auto=format&fit=crop',
  },
  {
    title: 'Components',
    description:
      'Pre-built blocks you can drop into any layout. Customize once, reuse everywhere.',
    image:
      'https://images.unsplash.com/photo-1517705008128-361805f42e86?q=80&w=1202&auto=format&fit=crop',
  },
  {
    title: 'Developer Experience',
    description:
      'Built with TypeScript, clear APIs, and documentation that gets you shipping faster.',
    image:
      'https://images.unsplash.com/photo-1579326882518-21eaa7893b02?q=80&w=1241&auto=format&fit=crop',
  },
  {
    title: 'Customization',
    description:
      'Themes, variants, and overrides. Make it look and behave exactly how you need.',
    image:
      'https://images.unsplash.com/photo-1544816155-12df9643f363?q=80&w=1062&auto=format&fit=crop',
  },
  {
    title: 'Performance',
    description:
      'Optimized for speed and accessibility. Less bundle size, smoother interactions.',
    image:
      'https://images.unsplash.com/photo-1572635196237-14b3f281503f?q=80&w=1212&auto=format&fit=crop',
  },
]

export function Feature01() {
  const [selectedIndex, setSelectedIndex] = useState(0)

  return (
    <section className="w-full">
      <div className="mx-auto w-full max-w-6xl px-6 py-12 sm:py-16">
        <div className="grid grid-cols-1 gap-10 md:grid-cols-2 md:items-center md:gap-14">
          <nav
            className="flex flex-col justify-center gap-1"
            aria-label="Features"
          >
            {items.map((item, index) => {
              const isSelected = index === selectedIndex

              return (
                <Button
                  key={item.title}
                  variant="ghost"
                  type="button"
                  onClick={() => setSelectedIndex(index)}
                  aria-current={isSelected ? 'true' : undefined}
                  className={cn(
                    'hover:bg-muted/50 h-auto w-full justify-start rounded-sm py-2.5 text-left font-normal whitespace-normal hover:opacity-100',
                    isSelected
                      ? 'text-foreground bg-muted/50'
                      : 'text-muted-foreground hover:text-foreground',
                  )}
                >
                  <span className="flex flex-col gap-1">
                    <span
                      className={cn('text-base', isSelected && 'font-medium')}
                    >
                      {item.title}
                    </span>
                    <span
                      className={cn(
                        'grid transition-[grid-template-rows] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]',
                        isSelected ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]',
                      )}
                    >
                      <span className="overflow-hidden">
                        <span
                          className={cn(
                            'text-muted-foreground block pb-0.5 text-sm transition-opacity duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]',
                            isSelected ? 'opacity-100' : 'opacity-0',
                          )}
                        >
                          {item.description}
                        </span>
                      </span>
                    </span>
                  </span>
                </Button>
              )
            })}
          </nav>

          <div className="relative min-h-[320px] w-full overflow-hidden rounded-lg outline outline-black/10 md:min-h-[420px] dark:outline-white/10">
            {items.map((item, index) => (
              <img
                key={item.title}
                src={item.image}
                alt={item.title}
                loading={index === 0 ? 'eager' : 'lazy'}
                decoding="async"
                className={cn(
                  'absolute inset-0 size-full object-cover grayscale transition-opacity duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]',
                  index === selectedIndex ? 'opacity-100' : 'opacity-0',
                )}
              />
            ))}
          </div>
        </div>
      </div>
    </section>
  )
}