Lightbox

Enlarged media/content overlay.

html

<div class="lightbox-demo lightbox-demo--visible">
  <div class="lightbox" style="--accent:#D9C8F0;--width:420px;" role="dialog" aria-modal="true" aria-label="Mountain ridge at dawn">
    <button class="lightbox__close" type="button" aria-label="Close">
      <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M1 1l14 14M15 1L1 15" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg>
    </button>
    
    <button class="lightbox__nav lightbox__nav--prev" type="button" aria-label="Previous image">
      <svg width="10" height="16" viewBox="0 0 10 16" aria-hidden="true"><path d="M9 1L1 8l8 7" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </button>
    <button class="lightbox__nav lightbox__nav--next" type="button" aria-label="Next image">
      <svg width="10" height="16" viewBox="0 0 10 16" aria-hidden="true"><path d="M1 1l8 7-8 7" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </button>
    
    <div class="lightbox__media lightbox__media--contain" aria-hidden="true"></div>
    <p class="lightbox__caption">Mountain ridge at dawn</p>
  </div>
</div>
<style>
.lightbox-demo { position: relative; min-height: 380px; border-radius: 12px; background: #FAF7F2; overflow: hidden; display: none; }
.lightbox-demo--visible { display: grid; place-items: center; background: rgba(36,20,48,.7); }
.lightbox { position: relative; width: var(--width); box-sizing: border-box; background: #241430; color: #FAF7F2; border-radius: 14px; padding: 20px; box-shadow: 0 24px 48px rgba(36,20,48,.4); font: 16px/1.5 system-ui, sans-serif; }
.lightbox__media { width: 100%; aspect-ratio: 4 / 3; border-radius: 10px; background: linear-gradient(135deg, var(--accent), #BFD9F2); margin-bottom: 14px; }
.lightbox__media--cover { background-size: cover; }
.lightbox__media--contain { background-size: contain; }
.lightbox__caption { margin: 0; font-size: 14px; text-align: center; color: #FAF7F2cc; }
.lightbox__close { position: absolute; top: 12px; right: 12px; background: none; border: none; color: #FAF7F2; cursor: pointer; padding: 6px; border-radius: 6px; }
.lightbox__close:hover, .lightbox__close:focus-visible { background: var(--accent); color: #241430; outline: none; }
.lightbox__nav { position: absolute; top: 50%; transform: translateY(-50%); background: none; border: none; color: #FAF7F2; cursor: pointer; padding: 8px; border-radius: 6px; }
.lightbox__nav--prev { left: -4px; }
.lightbox__nav--next { right: -4px; }
.lightbox__nav:hover, .lightbox__nav:focus-visible { background: var(--accent); color: #241430; outline: none; }
</style>

tailwind

<div class="relative min-h-[380px] rounded-xl bg-[#FAF7F2] overflow-hidden grid place-items-center" style="background:rgba(36,20,48,.7);">
  <div role="dialog" aria-modal="true" aria-label="Mountain ridge at dawn" class="relative box-border bg-[#241430] text-[#FAF7F2] rounded-2xl p-5 shadow-2xl font-sans" style="width:420px;--accent:#D9C8F0;">
    <button type="button" aria-label="Close" class="absolute top-3 right-3 bg-transparent border-none text-[#FAF7F2] cursor-pointer p-1.5 rounded-md hover:bg-[var(--accent)] hover:text-[#241430] focus-visible:bg-[var(--accent)] focus-visible:text-[#241430] focus-visible:outline-none">
      <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M1 1l14 14M15 1L1 15" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg>
    </button>
    
    <button type="button" aria-label="Previous image" class="absolute top-1/2 -translate-y-1/2 -left-1 bg-transparent border-none text-[#FAF7F2] cursor-pointer p-2 rounded-md hover:bg-[var(--accent)] hover:text-[#241430] focus-visible:bg-[var(--accent)] focus-visible:text-[#241430] focus-visible:outline-none">
      <svg width="10" height="16" viewBox="0 0 10 16" aria-hidden="true"><path d="M9 1L1 8l8 7" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </button>
    <button type="button" aria-label="Next image" class="absolute top-1/2 -translate-y-1/2 -right-1 bg-transparent border-none text-[#FAF7F2] cursor-pointer p-2 rounded-md hover:bg-[var(--accent)] hover:text-[#241430] focus-visible:bg-[var(--accent)] focus-visible:text-[#241430] focus-visible:outline-none">
      <svg width="10" height="16" viewBox="0 0 10 16" aria-hidden="true"><path d="M1 1l8 7-8 7" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </button>
    
    <div class="lightbox-tw__media lightbox-tw__media--contain w-full aspect-[4/3] rounded-[10px] mb-3.5" style="background:linear-gradient(135deg,#D9C8F0,#BFD9F2);"></div>
    <p class="m-0 text-sm text-center text-[#FAF7F2]/80">Mountain ridge at dawn</p>
  </div>
</div>
<style>
.lightbox-tw__media--cover { background-size: cover; }
.lightbox-tw__media--contain { background-size: contain; }
</style>

react

export function Lightbox({
  caption = "Mountain ridge at dawn",
  visible = true,
  fit = "contain",
  accent = "#D9C8F0",
  width = 420,
  nav = true,
}) {
  if (!visible) {
    return <div style={{ position: "relative", minHeight: 380, borderRadius: 12, background: "#FAF7F2" }} />;
  }
  return (
    <div style={{ position: "relative", minHeight: 380, borderRadius: 12, overflow: "hidden",
      display: "grid", placeItems: "center", background: "rgba(36,20,48,.7)" }}>
      <div role="dialog" aria-modal="true" aria-label={caption} style={{ position: "relative", width, boxSizing: "border-box",
        background: "#241430", color: "#FAF7F2", borderRadius: 14, padding: 20, boxShadow: "0 24px 48px rgba(36,20,48,.4)",
        fontFamily: "system-ui, sans-serif" }}>
        <button type="button" aria-label="Close" style={{ position: "absolute", top: 12, right: 12, background: "none",
          border: "none", color: "#FAF7F2", cursor: "pointer", padding: 6, borderRadius: 6 }}>
          <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M1 1l14 14M15 1L1 15" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round"/></svg>
        </button>
        {nav && (
          <>
            <button type="button" aria-label="Previous image" style={{ position: "absolute", top: "50%", transform: "translateY(-50%)",
              left: -4, background: "none", border: "none", color: "#FAF7F2", cursor: "pointer", padding: 8, borderRadius: 6 }}>
              <svg width="10" height="16" viewBox="0 0 10 16" aria-hidden="true"><path d="M9 1L1 8l8 7" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
            <button type="button" aria-label="Next image" style={{ position: "absolute", top: "50%", transform: "translateY(-50%)",
              right: -4, background: "none", border: "none", color: "#FAF7F2", cursor: "pointer", padding: 8, borderRadius: 6 }}>
              <svg width="10" height="16" viewBox="0 0 10 16" aria-hidden="true"><path d="M1 1l8 7-8 7" stroke="currentColor" strokeWidth="1.6" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
          </>
        )}
        <div style={{ width: "100%", aspectRatio: "4 / 3", borderRadius: 10, marginBottom: 14,
          background: `linear-gradient(135deg, ${accent}, #BFD9F2)`, backgroundSize: fit === "cover" ? "cover" : "contain" }} aria-hidden="true" />
        <p style={{ margin: 0, fontSize: 14, textAlign: "center", color: "#FAF7F2cc" }}>{caption}</p>
      </div>
    </div>
  );
}