starck
componentmacbook-mockupsource: ok · hit
MacBook Mockup
A responsive CSS MacBook frame with an accessible fullscreen mode.
npx shadcn add "https://ui.starck.studio/r/macbook-mockup.json"free
No selection tags yet — empty means no evidence, not lack of support.
Files1 · source cached 10/8/2026, 4:05:42 PM
registry/default/macbook-mockup.tsx
"use client"
import * as React from "react"
import { createPortal } from "react-dom"
type MacbookMockupProps = {
children: React.ReactNode
className?: string
expandable?: boolean
frameSrc?: string
label?: string
}
function CloseIcon() {
return (
<svg viewBox="0 0 20 20" fill="none" className="size-4" aria-hidden>
<path
d="m5 5 10 10M15 5 5 15"
stroke="currentColor"
strokeWidth="1.6"
strokeLinecap="round"
/>
</svg>
)
}
function Frame({
children,
frameSrc,
}: {
children: React.ReactNode
frameSrc: string
}) {
return (
<div className="relative mx-auto aspect-[1792/1165] w-full max-w-4xl select-none">
<div className="absolute [inset:11.7%_11.6%_11.6%] z-0 overflow-hidden bg-zinc-100">
{children}
</div>
{/* This is the same photographed silver frame used by SessionWatcher. */}
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={frameSrc}
alt=""
width={1792}
height={1165}
draggable={false}
className="pointer-events-none relative z-10 block size-full object-contain"
/>
</div>
)
}
function MacbookMockup({
children,
className = "",
expandable = true,
frameSrc = "https://ui.starck.studio/preview/macbook-frame.webp",
label = "Open fullscreen preview",
}: MacbookMockupProps) {
const [open, setOpen] = React.useState(false)
const triggerRef = React.useRef<HTMLButtonElement>(null)
const dialogRef = React.useRef<HTMLDivElement>(null)
const closeRef = React.useRef<HTMLButtonElement>(null)
React.useEffect(() => {
if (!open) return
const handleKey = (event: KeyboardEvent) => {
if (event.key === "Escape") setOpen(false)
if (event.key !== "Tab") return
const focusable = dialogRef.current?.querySelectorAll<HTMLElement>(
'button:not([disabled]), [tabindex]:not([tabindex="-1"])'
)
if (!focusable?.length) return
const first = focusable[0]
const last = focusable[focusable.length - 1]
if (event.shiftKey && document.activeElement === first) {
event.preventDefault()
last.focus()
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault()
first.focus()
}
}
const previous = document.body.style.overflow
const returnFocus = triggerRef.current
document.body.style.overflow = "hidden"
const frame = requestAnimationFrame(() => closeRef.current?.focus())
window.addEventListener("keydown", handleKey)
return () => {
cancelAnimationFrame(frame)
document.body.style.overflow = previous
window.removeEventListener("keydown", handleKey)
returnFocus?.focus()
}
}, [open])
const frame = <Frame frameSrc={frameSrc}>{children}</Frame>
return (
<>
<div className={`relative ${className}`}>
{frame}
{expandable ? (
<button
ref={triggerRef}
type="button"
aria-label={label}
onClick={() => setOpen(true)}
className="group absolute [inset:11.7%_11.6%_11.6%] z-20 cursor-zoom-in rounded-lg focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:outline-none"
>
<span className="absolute inset-0 grid place-items-center bg-black/0 transition-colors group-hover:bg-black/35 group-focus-visible:bg-black/35">
<span className="translate-y-1 rounded-full bg-white/95 px-3 py-1.5 text-xs font-medium text-zinc-950 opacity-0 shadow-lg transition-[opacity,transform] group-hover:translate-y-0 group-hover:opacity-100 group-focus-visible:translate-y-0 group-focus-visible:opacity-100">
Fullscreen
</span>
</span>
</button>
) : null}
</div>
{open && typeof document !== "undefined"
? createPortal(
<div
ref={dialogRef}
role="dialog"
aria-modal="true"
aria-label={label}
className="fixed inset-0 z-[100] grid place-items-center bg-white/96 p-5 backdrop-blur-xl"
onClick={() => setOpen(false)}
>
<button
ref={closeRef}
type="button"
aria-label="Close preview"
onClick={() => setOpen(false)}
className="absolute top-5 right-5 grid size-10 cursor-pointer place-items-center rounded-full bg-zinc-950/6 text-zinc-700 hover:bg-zinc-950/10"
>
<CloseIcon />
</button>
<div
className="w-full max-w-6xl"
onClick={(event) => event.stopPropagation()}
>
{frame}
</div>
</div>,
document.body
)
: null}
</>
)
}
export { MacbookMockup }
export type { MacbookMockupProps }