Saltar al contenido
ssntl/ui

Media card

Texto sobre una imagen con un velo oscuro, para que se lea sobre cualquier foto.

A quiet place to work

Studio retreat · 3 days in the valley

Instalación

$ pnpm dlx shadcn@latest add @ssntl/media-card

Uso

tsx
import { MediaCard, MediaCardContent, MediaCardImage } from "@/components/ui/media-card"

<MediaCard>
  <MediaCardImage src="/retiro.webp" alt="" />
  <MediaCardContent>
    <p className="text-heading">Un lugar tranquilo para trabajar</p>
    <p className="text-caption text-muted-foreground">Retiro de estudio · 3 días</p>
  </MediaCardContent>
</MediaCard>
  • El texto sobre imágenes nunca depende de la foto: el contenido va sobre un velo negro que se desvanece hacia arriba.
  • El contenido cambia al tema oscuro, así el texto y el texto atenuado quedan claros sobre cualquier imagen.
  • Las imágenes decorativas llevan alt=""; describe la foto solo cuando aporta información.
  • Deja el texto abajo, donde el velo es más oscuro; nunca sobre una cara o el sujeto.

Código

components/ui/media-card.tsx
import * as React from "react"

import { cn } from "@/lib/utils"

// Text over an image only reads when it doesn't depend on the image. The
// content sits on a scrim, a gradient from black that fades out upwards, and
// switches to the dark theme so its text and muted text stay light on any
// picture, bright or busy. A 1px pure black (white in dark) outline at 10%
// gives the image a consistent edge on any surface.
function MediaCard({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="media-card"
      className={cn(
        "relative isolate grid min-h-80 content-end overflow-hidden rounded-lg bg-muted outline -outline-offset-1 outline-black/10 dark:outline-white/10",
        className
      )}
      {...props}
    />
  )
}

function MediaCardImage({
  className,
  alt = "",
  ...props
}: React.ComponentProps<"img">) {
  return (
    // eslint-disable-next-line @next/next/no-img-element
    <img
      data-slot="media-card-image"
      alt={alt}
      className={cn("absolute inset-0 -z-10 size-full object-cover", className)}
      {...props}
    />
  )
}

function MediaCardContent({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="media-card-content"
      data-theme="dark"
      className={cn(
        "grid gap-1 bg-linear-to-t from-black/80 via-black/50 to-transparent p-6 pt-24 text-foreground",
        className
      )}
      {...props}
    />
  )
}

export { MediaCard, MediaCardContent, MediaCardImage }