Lightbox

Un componente lightbox de galería de imágenes y vídeos a pantalla completa, con @reelkit/react-lightbox.

Ver la demo en vivo →

Características

Imágenes y vídeo
Slides de vídeo incluidos
Gestos táctiles
Desliza para navegar
Deslizar para cerrar
Desliza hacia arriba para cerrar
Teclado
Flechas + Escape
Pantalla completa
API compatible con todos los navegadores
Transiciones
Deslizar, fundido, volteo, zoom
Precarga
Las imágenes contiguas se cargan antes
Botón de sonido
Silencio por slide
Estados de carga
Spinner y renderizado propio
Gestión de errores
Icono de error y renderizado propio
Render props
6 zonas de renderizado personalizables
Hooks
useVideoSlideRenderer + useFullscreen
Estado en la URL
Enlaces que se pueden compartir y guardar

Instalación

bash
npm install @reelkit/react-lightbox @reelkit/react lucide-react

No olvides importar los estilos:

typescript
import '@reelkit/react-lightbox/styles.css';
Iconos

Los controles por defecto usan lucide-react para los iconos. Si prefieres otra librería de iconos, usa renderControls y renderNavigation para poner los tuyos.

Inicio rápido

El componente LightboxOverlay muestra imágenes a pantalla completa. Pásale un array de objetos LightboxItem y controla su visibilidad con un índice que puede ser nulo.

tsx
import { useState } from 'react';
import { LightboxOverlay, type LightboxItem } from '@reelkit/react-lightbox';
import '@reelkit/react-lightbox/styles.css';

const images: LightboxItem[] = [
  {
    src: 'https://example.com/image1.jpg',
    title: 'Sunset',
    description: 'Beautiful sunset over the ocean',
  },
  {
    src: 'https://example.com/image2.jpg',
    title: 'Mountains',
  },
];

function App() {
  const [index, setIndex] = useState<number | null>(null);

  return (
    <>
      {images.map((img, i) => (
        <img
          key={i}
          src={img.src}
          onClick={() => setIndex(i)}
        />
      ))}
      <LightboxOverlay
        isOpen={index !== null}
        images={images}
        initialIndex={index ?? 0}
        onClose={() => setIndex(null)}
      />
    </>
  );
}

Demo en vivo

LightboxPage.tsx

Pulsa una miniatura para abrir el lightbox. Usa las flechas o desliza para navegar.

Slides de vídeo (opcionales)

El vídeo es totalmente opcional y admite tree-shaking: si solo usas imágenes, el bundle no crece nada. Importa useVideoSlideRenderer y conecta lo que devuelve a LightboxOverlay. El hook gestiona solo los estados de carga, el sonido y el ciclo de vida del vídeo.

tsx
import {
  LightboxOverlay,
  useVideoSlideRenderer,
  type LightboxItem,
} from '@reelkit/react-lightbox';
import '@reelkit/react-lightbox/styles.css';

const items: LightboxItem[] = [
  { src: '/photo.jpg', title: 'Photo' },
  {
    src: '/clip.mp4',
    type: 'video',
    poster: '/clip-thumb.jpg',
    title: 'Video Clip',
  },
];

function Gallery() {
  const [index, setIndex] = useState<number | null>(null);
  const isOpen = index !== null;
  const { renderSlide, renderControls, SoundProvider } =
    useVideoSlideRenderer(items, isOpen);

  return (
    <SoundProvider>
      {/* thumbnails… */}
      <LightboxOverlay
        isOpen={isOpen}
        images={items}
        initialIndex={index ?? 0}
        onClose={() => setIndex(null)}
        renderSlide={renderSlide}
        renderControls={renderControls}
      />
    </SoundProvider>
  );
}
Cómo funciona
  • El hook devuelve SoundProvider: envuelve el overlay con él para que funcione el silencio
  • Los vídeos se reproducen solos (en silencio por defecto) cuando el slide se activa
  • Se reutiliza un mismo elemento de vídeo entre slides para que el sonido no se corte en iOS
  • El botón de sonido aparece solo en los slides de vídeo, con un interruptor de silencio reactivo
  • Los elementos sin type: 'video' se renderizan como imágenes (compatible con versiones anteriores)

Personalización

Controles propios

Usa renderControls para sustituir el botón de cerrar, el contador y el botón de pantalla completa por defecto. Combínalo con los subcomponentes exportados:

tsx
import {
  LightboxOverlay,
  CloseButton,
  Counter,
  FullscreenButton,
} from '@reelkit/react-lightbox';

<LightboxOverlay
  isOpen={isOpen}
  images={images}
  onClose={handleClose}
  renderControls={({ onClose, activeIndex, count, isFullscreen, onToggleFullscreen }) => (
    <div style={{ position: 'absolute', top: 12, left: 16, right: 16, display: 'flex', justifyContent: 'space-between', zIndex: 10 }}>
      <Counter currentIndex={activeIndex} count={count} />
      <div>
        <FullscreenButton isFullscreen={isFullscreen} onToggle={onToggleFullscreen} />
        <CloseButton onClick={onClose} />
      </div>
    </div>
  )}
/>

Overlay de información propio

Usa renderInfo para sustituir el degradado por defecto con el título y la descripción, o pasa renderInfo={() => null} para ocultarlo del todo:

tsx
<LightboxOverlay
  isOpen={isOpen}
  images={images}
  onClose={handleClose}
  renderInfo={({ item, index }) => (
    <div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: 16, background: 'linear-gradient(transparent, rgba(0,0,0,0.8))', color: '#fff', zIndex: 10 }}>
      <h3>{item.title}</h3>
      <p>{item.description}</p>
    </div>
  )}
/>

Navegación propia

Usa renderNavigation para sustituir las flechas de anterior y siguiente por defecto:

tsx
<LightboxOverlay
  isOpen={isOpen}
  images={images}
  onClose={handleClose}
  renderNavigation={({ onPrev, onNext, activeIndex, count }) => (
    <div style={{ position: 'absolute', bottom: 24, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 12, zIndex: 10 }}>
      <button onClick={onPrev} disabled={activeIndex === 0}>Prev</button>
      <span>{activeIndex + 1} / {count}</span>
      <button onClick={onNext} disabled={activeIndex === count - 1}>Next</button>
    </div>
  )}
/>

Slide propio

Usa renderSlide para un contenido del slide totalmente propio. Devuelve null para usar el slide de imagen por defecto:

tsx
<LightboxOverlay
  isOpen={isOpen}
  images={images}
  onClose={handleClose}
  renderSlide={({ item, index, size, isActive, onReady, onError }) => {
    // Custom CTA on last slide
    if (index === images.length - 1) {
      return (
        <div style={{ width: size[0], height: size[1], display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff' }}>
          <h2>View all photos</h2>
        </div>
      );
    }
    return null; // default image slide
  }}
/>

Carga de contenido y gestión de errores

El lightbox sigue el estado de carga y de error de cada slide. Mientras el contenido se carga se muestra un spinner; con un medio que falla aparece un icono de imagen rota. Las URLs con error se guardan en caché, así que al volver se muestra el error al instante sin reintentar.

Callbacks del ciclo de vida

Si usas renderSlide, llama a estos callbacks para controlar el indicador de carga:

CallbackCuándo llamarlo
onReadyLa imagen se ha cargado o el vídeo ha empezado a reproducirse. Limpia los estados de carga y de error.
onWaitingEl vídeo está cargando a mitad de la reproducción. Muestra el indicador de carga.
onErrorEl contenido no se ha podido cargar. Muestra el overlay de error y guarda la URL en caché como rota.
tsx
// Inside renderSlide — wire callbacks to your custom media
renderSlide={({ item, index, size, isActive, onReady, onWaiting, onError }) => (
  <div style={{ width: size[0], height: size[1] }}>
    {item.type === 'video' ? (
      <video
        src={item.src}
        poster={item.poster}
        autoPlay={isActive}
        onCanPlay={onReady}
        onWaiting={onWaiting}
        onError={onError}
        style={{ width: '100%', height: '100%', objectFit: 'contain' }}
      />
    ) : (
      <img
        src={item.src}
        onLoad={onReady}
        onError={onError}
        style={{ width: '100%', height: '100%', objectFit: 'contain' }}
      />
    )}
  </div>
)}

Interfaz de carga y de error propia

Sustituye el spinner y el icono de error por defecto por componentes propios:

tsx
<LightboxOverlay
  isOpen={isOpen}
  images={images}
  onClose={() => setIsOpen(false)}
  renderLoading={({ item, activeIndex }) => (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 10,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      color: '#fff', fontSize: 14,
    }}>
      Loading image {activeIndex + 1}...
    </div>
  )}
  renderError={({ item, activeIndex }) => (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 10,
      display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      gap: 12, color: 'rgba(255,255,255,0.5)',
    }}>
      <span style={{ fontSize: 48 }}>!</span>
      <span>Failed to load content</span>
    </div>
  )}
/>

Estado en la URL

Ver la demo en vivo →

LightboxUrlOverlay es un componente aparte cuyo estado abierto vive en la barra de direcciones. Crea un controlador con useOverlayUrlState de @reelkit/react y pásalo como controller: la galería se abre sola cuando el parámetro nombra un slide y se cierra cuando desaparece. Los enlaces se pueden compartir y el botón de volver cierra la galería en lugar de salir de la página.

Claves incluidas

Puedes apuntar a los slides con una clave incluida: pasa con spread urlIndexKey (por posición) o urlStableIdKey (por un id estable) al controlador; las dos se reexportan desde @reelkit/react. Consulta la guía del estado en la URL y la API del core.

tsx
import { useOverlayUrlState, urlIndexKey, urlStableIdKey } from '@reelkit/react';
import { LightboxUrlOverlay } from '@reelkit/react-lightbox';
import { Link } from 'react-router-dom';

const photo = useOverlayUrlState({
  param: 'photo',
  ...urlIndexKey(() => images.length),
});

// Opening is a link — the href is the open action. No open flag, no handler:
// the overlay reads the URL and opens itself.
{images.map((image, i) => (
  <Link key={image.src} to={`?photo=${i}`}>
    <img src={image.src} />
  </Link>
))}

<LightboxUrlOverlay controller={photo} images={images} />

El hook recibe un objeto de opciones y devuelve un UrlStateController (con set, index y value). Guárdalo para controlarlo por código: set es la escritura de bajo nivel que usa el overlay internamente (al cambiar de slide y con set(null) para cerrar). También maneja el overlay por código: set(index) lo abre, igual que navegar al parámetro. Aun así, es mejor abrirlo con un enlace: el href se puede compartir, se abre en una pestaña nueva y el botón de volver lo cierra, todo gratis y sin manejador.

Las opciones completas de useOverlayUrlState (param, adapter, codec, locator) están en la referencia de la API de React.

LightboxUrlOverlay solo recibe controller (obligatorio), un onClose opcional y todas las props visuales y de comportamiento de LightboxOverlay (images, ariaLabel, transitionFn, las render props y demás), pero no isOpen.

  • Abrir cuesta una entrada en el historial. Pasar slides la sustituye, así que cien deslizamientos no añaden ninguna y volver una vez siempre sale de la galería. Volver cierra; no recorre las fotos.
  • Un enlace compartido como ?photo=3 abre la galería en ese slide. Al cerrar un enlace que llegó con la página, el parámetro se quita en el sitio en lugar de salir de tu web.
  • Volver solo cierra cuando abriste la galería desde dentro de la aplicación: el enlace añadió una entrada, así que volver regresa a la galería. Un enlace compartido abierto directamente en una pestaña nueva no tiene historial detrás, así que el botón de volver del navegador sale del sitio; el botón de cerrar o Escape quitan el parámetro en el sitio y te dejan en la galería. Los adaptadores de router incluidos confirman cuándo un enlace añadió una entrada en la misma página; un adaptador propio que no sepa cómo llegó la entrada también cierra en el sitio, lo que deja una copia de la página en el historial: volver una vez parece no hacer nada, pero nada se vuelve a abrir.
  • Un parámetro que no nombra ningún slide (un marcador desfasado, un valor editado a mano) se quita de la URL en lugar de dejar la barra de direcciones apuntando a un slide que no se puede abrir.

En una aplicación con router, pasa un adaptador. Escribir directamente en el historial deja desfasada la ubicación del propio router, y su siguiente navegación pierde el parámetro.

tsx
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';

const adapter = useReactRouterUrlAdapter();
const photo = useOverlayUrlState({
  param: 'photo',
  adapter,
  ...urlIndexKey(() => images.length),
});

<LightboxUrlOverlay controller={photo} images={images} />

Abrir es un enlace. Como el estado abierto vive en la URL, una miniatura es un enlace normal, sin manejador de clic, y el comportamiento propio del navegador sale gratis: abrir en una pestaña nueva, copiar la dirección, vista previa al pasar el ratón. En una aplicación con router usa el enlace del router para que todo siga en el cliente.

tsx
import { Link } from 'react-router-dom';

// The href is the open action — no onClick, no open flag.
{images.map((image, i) => (
  <Link key={image.src} to={`?photo=${i}`}>
    <img src={image.src} />
  </Link>
))}

<LightboxUrlOverlay controller={photo} images={images} />

Para enlaces compartibles, mejor una identidad estable. El índice es posicional, así que un ?photo=3 guardado abre otra imagen en cuanto la lista se reordena. urlStableIdKey usa como clave el id estable de cada elemento y recorre la lista actual; una sola llamada cubre el caso habitual.

tsx
const photo = useOverlayUrlState({
  param: 'photo',
  ...urlStableIdKey({ items: () => images }),
});

<LightboxUrlOverlay controller={photo} images={images} />

Pasa hashCodec: base64UrlCodec para codificar el id en base64url en la URL: es una ofuscación reversible, no un hash criptográfico.

Si usas otro campo como clave (un slug) o paginas un feed infinito con locateAsync, crea tú el codec y el locator:

tsx
const photo = useOverlayUrlState({
  param: 'photo',
  codec: { decode: (raw) => raw, encode: (id) => id },
  locator: {
    locate: (id) => images.findIndex((x) => x.slug === id),
    identify: (index) => images[index].slug,
  },
});

<LightboxUrlOverlay controller={photo} images={images} />

Galerías infinitas o paginadas. El locate síncrono solo puede responder por las imágenes ya cargadas: un enlace compartido a la imagen 400 de un feed que ha cargado 20 no encuentra nada. locateAsync es la alternativa y solo se llama cuando locate falla.

Atajo

¿Usas el id del elemento como clave? Sáltate el codec y el locator hechos a mano y pasa locateAsync directamente a urlStableIdKey({ items, locateAsync }) (carga cuando no lo encuentra y después devuelve el índice). La versión más completa de abajo es para usar otro campo como clave o para tener el control total.

tsx
const photo = useOverlayUrlState({
  param: 'photo',
  codec: { decode: (raw) => raw, encode: (id) => id },
  locator: {
    locate: (id) => images.findIndex((x) => x.id === id),
    identify: (index) => images[index].id,
    locateAsync: async (id) => {
      const loaded = await loadById(id); // or loadUntil(id) — fetch just that one, or page up to it
      if (!loaded) return null; // exhausted — link names no image
      setImages(loaded); // commit — the overlay renders from this state
      return loaded.findIndex((x) => x.id === id); // wherever it landed
    },
  },
});

<LightboxUrlOverlay controller={photo} images={images} />
  • Cómo cargas los datos es cosa tuya: carga páginas seguidas hasta el objetivo, o carga solo esa imagen y añádela. La URL usa la identidad como clave, no la posición, así que findIndex devuelve el sitio donde acabe el elemento.
  • Mientras locateAsync está pendiente, el lightbox sigue cerrado y el parámetro no se toca, así que el enlace directo sobrevive a la carga. null o un rechazo quitan el parámetro.
  • Una respuesta que llega después de que la URL haya cambiado, después de cerrar o después de desmontar se descarta: una carga lenta no puede abrir un slide que nadie ha pedido.
  • Mientras está pendiente no se renderiza nada; la página ya es dueña de ese estado de carga, así que renderiza tu propio esqueleto.
  • No hay tiempo límite: el lightbox no puede saber lo larga que es la galería. Resuelve con null cuando se agote la paginación, o el overlay seguirá cerrado indefinidamente.
  • Lo que devuelva locateAsync manda: es el índice de los datos que acaba de cargar y se usa tal cual, sin volver a leer images.

Referencia de la API

Props de LightboxOverlay

LightboxOverlayProps

PropTipoPor defectoDescripción
isOpenbooleanobligatorioControla la visibilidad del lightbox. Para que el estado abierto lo controle la URL, usa el componente aparte LightboxUrlOverlay; mira el estado en la URL más arriba.
imagesLightboxItem[]obligatorioArray de imágenes que mostrar
ariaLabelstring'Image gallery'Etiqueta accesible de la región del diálogo; los lectores de pantalla la anuncian al abrirse el lightbox
initialIndexnumber0Índice de la imagen inicial
transitionFnTransitionTransformFnslideTransitionFunción de transición entre slides. Importa una incluida (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) o pasa una propia. Si se omite, se usa slideTransition.
apiRefMutableRefObject<ReelApi>-Ref para acceder a la API de Reel
renderControls(props: ControlsRenderProps) => ReactNode-Controles propios; sustituyen el botón de cerrar, el contador y el botón de pantalla completa por defecto
renderNavigation(props: NavigationRenderProps) => ReactNode-Navegación propia; sustituye las flechas de anterior y siguiente por defecto
renderInfo(props: InfoRenderProps) => ReactNode-Overlay de información propio; sustituye el degradado por defecto con el título y la descripción. Devuelve null para ocultarlo.
renderSlide(props: SlideRenderProps) => ReactNode | null-Renderizado propio del slide. Recibe { item, index, size, isActive, onReady, onWaiting, onError }. Devuelve null para usar el slide por defecto.
renderLoading(props: { item: LightboxItem; activeIndex: number }) => ReactNode-Indicador de carga propio; sustituye el spinner por defecto
renderError(props: { item: LightboxItem; activeIndex: number }) => ReactNode-Indicador de error propio; sustituye el icono de error por defecto

Props de LightboxUrlOverlay

LightboxUrlOverlayProps

Acepta todas las props visuales y de comportamiento de arriba salvo isOpen, que se sustituye por controller. initialIndex se ignora aquí: la posición del controlador elige el slide, así que un valor pasado junto a él se sobrescribiría en cada apertura.

PropTipoPor defectoDescripción
controllerUrlStateControllerobligatorioControlador de useOverlayUrlState. Su posición decide si el overlay está abierto y qué slide muestra; el overlay escribe a través de él al cambiar de slide y al cerrar.

Callbacks

PropTipoDescripción
onClose() => voidSe llama al cerrar el lightbox. Obligatorio en LightboxOverlay (el estado abierto es tuyo, así que tienes que gestionar el cierre); opcional en LightboxUrlOverlay, donde la URL controla el cierre: pásalo solo para reaccionar después de cerrar.
onSlideChange(index: number) => voidSe llama después del cambio de slide

Props de Reel (reenviadas)

Estas props se reenvían al componente Reel que hay debajo.

PropTipoPor defectoDescripción
loopbooleanfalseActiva el bucle infinito
enableNavKeysbooleantrueActiva la navegación con teclado
enableWheelbooleantrueActiva la navegación con la rueda del ratón
wheelDebounceMsnumber200Duración del debounce de la rueda (ms)
transitionDurationnumber300Duración de la animación de transición (ms)
swipeDistanceFactornumber0.12Umbral del deslizamiento (0-1)
swipeToCloseDirection'up' | 'down''up'Dirección del gesto de deslizar para cerrar en móvil

Tipos

LightboxItem

typescript
interface LightboxItem {
  src: string;
  type?: 'image' | 'video';  // defaults to 'image'
  poster?: string;            // thumbnail for video items
  title?: string;
  description?: string;
  width?: number;
  height?: number;
}

ControlsRenderProps

typescript
interface ControlsRenderProps {
  item: LightboxItem;
  activeIndex: number;
  count: number;
  isFullscreen: boolean;
  onClose: () => void;
  onToggleFullscreen: () => void;
}
typescript
interface NavigationRenderProps {
  item: LightboxItem;
  activeIndex: number;
  count: number;
  onPrev: () => void;
  onNext: () => void;
}

SlideRenderProps

typescript
interface SlideRenderProps {
  item: LightboxItem;
  index: number;
  size: [number, number];
  isActive: boolean;
  onReady: () => void;
  onWaiting: () => void;
  onError: () => void;
}

InfoRenderProps

typescript
interface InfoRenderProps {
  item: LightboxItem;
  index: number;
}

Subcomponentes

Subcomponentes reutilizables para crear controles propios con renderControls.

CloseButton

Botón X de cerrar por defecto.

tsx
import { CloseButton } from '@reelkit/react-lightbox';

<CloseButton onClick={onClose} />

Counter

Píldora con el contador de imágenes, por ejemplo "1 / 3".

tsx
import { Counter } from '@reelkit/react-lightbox';

<Counter currentIndex={activeIndex} count={count} />

FullscreenButton

Botón para activar la pantalla completa (icono Maximize/Minimize).

tsx
import { FullscreenButton } from '@reelkit/react-lightbox';

<FullscreenButton isFullscreen={isFullscreen} onToggle={onToggleFullscreen} />

SoundButton

Botón de silencio para los slides de vídeo (icono Volume2/VolumeX). Se incluye automáticamente en el renderControls de useVideoSlideRenderer. Para usarlo por separado dentro de controles propios, accede al estado del sonido con useSoundState.

tsx
import { SoundButton } from '@reelkit/react-lightbox';
import { useSoundState } from '@reelkit/react';

// Inside a component wrapped in SoundProvider:
function CustomControls({ onClose }) {
  const soundState = useSoundState();

  return (
    <div>
      <SoundButton
        muted={soundState.muted.value}
        onToggle={soundState.toggle}
      />
      <button onClick={onClose}>Close</button>
    </div>
  );
}

Hooks

useVideoSlideRenderer

Hook para el vídeo opcional. Devuelve renderSlide, renderControls y SoundProvider: envuelve el overlay con SoundProvider y pasa las funciones de renderizado.

typescript
import { useVideoSlideRenderer } from '@reelkit/react-lightbox';

const { renderSlide, renderControls, SoundProvider, hasVideo } =
  useVideoSlideRenderer(items, isOpen);

// SoundProvider  — wrap LightboxOverlay in this for mute/unmute support
// renderSlide    — pass to LightboxOverlay's renderSlide prop
// renderControls — pass to LightboxOverlay's renderControls prop
//                  (includes Counter, FullscreenButton, SoundButton, CloseButton)
// hasVideo       — true if items contain at least one video
// isOpen param   — resets mute to true on close (enables autoplay on reopen)

useFullscreen

Se ha movido

useFullscreen se eliminó de @reelkit/react-lightbox. Impórtalo desde @reelkit/react.

Hook para gestionar el estado de pantalla completa, compatible con todos los navegadores.

tsx
import { useRef } from 'react';
import { useFullscreen } from '@reelkit/react';

function CustomLightbox() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isFullscreen, requestFullscreen, exitFullscreen, toggleFullscreen] =
    useFullscreen({ ref: containerRef });

  return (
    <div ref={containerRef}>
      <button onClick={toggleFullscreen}>
        {isFullscreen.value ? 'Exit Fullscreen' : 'Enter Fullscreen'}
      </button>
    </div>
  );
}

Transiciones

Pasa cualquier TransitionTransformFn con la prop transitionFn. Si importas solo la transición que usas, el bundler puede descartar el resto con tree-shaking. Si se omite, se usa slideTransition.

FunciónOrigenDescripción
slideTransition@reelkit/react-lightboxDeslizamiento horizontal estándar (por defecto)
lightboxFadeTransition@reelkit/react-lightboxFundido cruzado entre imágenes
flipTransition@reelkit/react-lightboxEfecto 3D de volteo de tarjeta
lightboxZoomTransition@reelkit/react-lightboxZoom de un tamaño menor al tamaño normal
tsx
import {
  LightboxOverlay,
  lightboxFadeTransition,
} from '@reelkit/react-lightbox';

<LightboxOverlay
  isOpen={isOpen}
  images={images}
  initialIndex={0}
  onClose={handleClose}
  transitionFn={lightboxFadeTransition}
/>

Función de transición propia

Escribe tu propia TransitionTransformFn y pásala con transitionFn. La firma es la misma que la de las transiciones del slider del core.

tsx
import { LightboxOverlay } from '@reelkit/react-lightbox';
import type { TransitionTransformFn } from '@reelkit/react';

const customFade: TransitionTransformFn = (offset, size) => ({
  transform: `translate3d(${offset * size[0]}px, 0, 0)`,
  opacity: 1 - Math.min(Math.abs(offset), 1),
});

<LightboxOverlay
  isOpen={isOpen}
  images={images}
  transitionFn={customFade}
  onClose={() => setIsOpen(false)}
/>

Clases CSS

Todos los elementos de la interfaz usan clases CSS normales (no CSS modules) a las que puedes apuntar con selectores más específicos en una hoja de estilos cargada después de @reelkit/react-lightbox/styles.css. Para cambiar colores, tamaños y z-index, es mejor usar las propiedades personalizadas de CSS de la sección Temas de más abajo.

ClaseComponenteDescripción
.rk-lightbox-overlayOverlayContenedor raíz (fondo a pantalla completa)
.rk-lightbox-spinnerOverlaySpinner de carga por defecto
.rk-lightbox-img-errorOverlayContenedor del estado de error (imagen o vídeo roto)
.rk-lightbox-img-error-textOverlayTexto del estado de error
.rk-lightbox-swipe-hintOverlayPista de deslizamiento en móvil
.rk-lightbox-controls-leftControlsContenedor de los controles de arriba a la izquierda
.rk-lightbox-btnControlsBotones de control (pantalla completa, etc.)
.rk-lightbox-closeControlsBotón de cerrar
.rk-lightbox-counterControlsChip del contador de imágenes
.rk-lightbox-navNavigationFlechas de navegación (las dos)
.rk-lightbox-nav-prevNavigationFlecha de anterior
.rk-lightbox-nav-nextNavigationFlecha de siguiente
.rk-lightbox-infoInfoContenedor del título y la descripción
.rk-lightbox-titleInfoTítulo de la imagen
.rk-lightbox-descriptionInfoDescripción de la imagen
.rk-lightbox-slideSlideContenedor del slide
.rk-lightbox-imgSlideElemento de imagen
.rk-lightbox-video-containerVideoSlideContenedor del slide de vídeo (opcional)
.rk-lightbox-video-elementVideoSlideElemento de vídeo (opcional)
.rk-lightbox-video-posterVideoSlideImagen del póster del vídeo (opcional)

Temas

Cada color, tamaño, z-index y transición vive en una propiedad personalizada de CSS. Sobrescribe una o varias en :root (o en cualquier ancestro del lightbox) para cambiar el tema sin tocar el código de los componentes.

TokenPor defectoControla
--rk-lightbox-overlay-bg#000Color del fondo a pantalla completa
--rk-lightbox-overlay-z9999z-index del overlay
--rk-lightbox-top-shade-height80pxAltura del degradado superior
--rk-lightbox-top-shade-bglinear-gradient(rgba(0,0,0,0.6), transparent)Color del degradado superior
--rk-lightbox-edge-padding16pxSeparación del borde de cerrar / navegación / controles de arriba a la izquierda
--rk-lightbox-controls-gap12pxEspacio entre los controles de arriba a la izquierda
--rk-lightbox-transition0.2sDuración de la transición de los botones al pasar el ratón
--rk-lightbox-blur8pxRadio del desenfoque de fondo de botones y chips
--rk-lightbox-btn-bgrgba(0, 0, 0, 0.5)Fondo por defecto de los botones de cerrar, navegación y pequeños
--rk-lightbox-btn-bg-hoverrgba(255, 255, 255, 0.2)Fondo al pasar el ratón de los botones de cerrar, navegación y pequeños
--rk-lightbox-btn-fg#fffColor del icono de los botones de cerrar, navegación y pequeños
--rk-lightbox-btn-size36pxTamaño de los botones pequeños (pantalla completa, etc.)
--rk-lightbox-close-size40pxTamaño del botón de cerrar
--rk-lightbox-nav-size48pxTamaño de las flechas de anterior y siguiente
--rk-lightbox-nav-opacity0.7Opacidad en reposo de las flechas de anterior y siguiente
--rk-lightbox-counter-fg#fffColor del texto del contador
--rk-lightbox-counter-bgrgba(0, 0, 0, 0.5)Fondo del chip del contador
--rk-lightbox-counter-size14pxTamaño de letra del contador
--rk-lightbox-counter-padding6px 12pxRelleno del chip del contador
--rk-lightbox-counter-radius20pxborder-radius del chip del contador
--rk-lightbox-spinner-size28pxAncho y alto del spinner por defecto
--rk-lightbox-spinner-trackrgba(255, 255, 255, 0.2)Color de la pista del spinner
--rk-lightbox-spinner-fg#fffColor del indicador del spinner
--rk-lightbox-spinner-duration0.8sDuración de la rotación del spinner
--rk-lightbox-error-fgrgba(255, 255, 255, 0.4)Color del icono y el texto de error
--rk-lightbox-error-text-size13pxTamaño de letra del mensaje de error
--rk-lightbox-info-bglinear-gradient(transparent, rgba(0,0,0,0.8))Degradado del fondo del pie
--rk-lightbox-info-padding24pxRelleno interior del pie
--rk-lightbox-title-size18pxTamaño de letra del título
--rk-lightbox-description-size14pxTamaño de letra de la descripción
--rk-lightbox-info-fg#fffColor del texto del pie
--rk-lightbox-hint-fgrgba(255, 255, 255, 0.5)Color del texto de la pista de deslizamiento
--rk-lightbox-hint-bgrgba(0, 0, 0, 0.3)Fondo del chip de la pista de deslizamiento
--rk-lightbox-hint-duration3sDuración total de la aparición y desaparición de la pista
--rk-lightbox-video-bg#000Fondo de las bandas detrás del <video>

Pega el fragmento de abajo en una hoja de estilos cargada después de @reelkit/react-lightbox/styles.css.

css
/* Brand the lightbox */
:root {
  --rk-lightbox-overlay-bg: #0f172a;
  --rk-lightbox-btn-bg: rgba(99, 102, 241, 0.65);
  --rk-lightbox-btn-bg-hover: rgba(168, 85, 247, 0.85);
  --rk-lightbox-nav-size: 56px;
  --rk-lightbox-counter-bg: rgba(99, 102, 241, 0.65);
  --rk-lightbox-info-bg: linear-gradient(
    transparent,
    rgba(99, 102, 241, 0.55) 60%,
    rgba(168, 85, 247, 0.85)
  );
}

Accesibilidad

La raíz del overlay es un diálogo modal (role="dialog", aria-modal="true"). Usa ariaLabel para cambiar lo que anuncia el lector de pantalla; por defecto es "Image gallery". Cada slide lleva role="group", aria-roledescription="slide" y aria-label="Image N of M".

El lightbox captura el foco al abrirse y lo devuelve al disparador al cerrarse. Tab y Mayús+Tab recorren los elementos enfocables de dentro; el foco que sale (un clic fuera, un foco por código) vuelve dentro. Está implementado con captureFocusForReturn y createFocusTrap de @reelkit/core.

Atajos de teclado

TeclaAcción
ArrowLeftImagen anterior
ArrowRightImagen siguiente
EscapeCierra el lightbox (o sale de la pantalla completa si está activa)