ComponentsAspect ratio 1203:753 default. Slot per imageSrc, videoSrc o children custom. Token bg-bg-alt + border-border.
MockDeviceShowcase
Esempio01
safari-mock.tsx 001Default · placeholder content002Custom URL · marketing site
components.lucaperullo.it/animated-beam
Animated Beam · 001
HTTP round-trip
003Compact · narrow embed tsxsrc/components/safari-mock.tsx
import type { ReactNode } from "react";
import { cn } from "@/lib/utils";
export type SafariMockProps = {
/** URL pill copy. */
url?: string;
/** Image to render in the content area. */
imageSrc?: string;
/** Video file to render in the content area. */
videoSrc?: string;
/** Custom content (overrides imageSrc/videoSrc). */
children?: ReactNode;
/** Aspect ratio width hint. Default 1203. */
width?: number;
/** Aspect ratio height hint. Default 753. */
height?: number;
className?: string;
};
/**
* <SafariMock/> — div-based Safari window chrome (traffic-light dots +
* URL pill) with a content slot. No SVG to keep the inner content
* editable. Uses design tokens; bg-fg / text-bg switch automatically with
* the theme.
*/
export function SafariMock({
url,
imageSrc,
videoSrc,
children,
width = 1203,
height = 753,
className,
}: SafariMockProps) {
return (
<div
className={cn(
"relative overflow-hidden rounded-lg border border-border bg-bg-alt shadow-sm",
className,
)}
style={{ aspectRatio: `${width} / ${height}` }}
>
<div className="flex h-9 items-center gap-2 border-b border-border bg-bg-alt px-3">
<div className="flex items-center gap-1.5">
<span className="h-3 w-3 rounded-full bg-red-500" />
<span className="h-3 w-3 rounded-full bg-amber-400" />
<span className="h-3 w-3 rounded-full bg-emerald-500" />
</div>
<div className="mx-auto flex h-6 max-w-[60%] flex-1 items-center justify-center rounded-md bg-bg px-3 font-mono text-[11px] text-fg-muted">
{url ?? "lucaperullo.it"}
</div>
<div className="w-12" aria-hidden />
</div>
<div className="absolute inset-0 top-9 bg-bg">
{videoSrc ? (
<video
src={videoSrc}
autoPlay
muted
loop
playsInline
className="h-full w-full object-cover"
/>
) : imageSrc ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={imageSrc}
alt=""
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full">{children}</div>
)}
</div>
</div>
);
}
Note — Top bar 36px h-9. URL pill mono 11px text-fg-muted, max-width 60% del bar. videoSrc autoplay muted loop playsInline.
Prompt LLM02
Incolla in Claude o ChatGPT per generare la tua variante. Include il contesto del brand, i token e i vincoli del progetto.
Sei un senior frontend engineer. Stai lavorando su un sito Next.js 16 + React 19 + Tailwind v4 in italiano, look chanhdai-inspired: colonna stretta 672px, Geist Sans + Geist Mono, hairline 1px, divisori a stripe diagonale, palette zinc.
Token CSS disponibili: --bg, --bg-alt, --fg, --fg-muted, --fg-soft, --border, --border-strong, --accent. Usa SEMPRE queste variabili tramite le utility tailwind generate (bg-bg, text-fg-muted, border-border, ecc.). Helper "cn" da "@/lib/utils". Niente librerie UI extra: solo lucide-react e tailwind-merge.
Genera un componente <SafariMock> div-based.
Props:
- url?: string.
- imageSrc?, videoSrc?, children?: ReactNode.
- width?: number (default 1203), height?: number (default 753).
- className?.
Implementazione:
- Server component. Wrapper relative overflow-hidden rounded-lg border border-border bg-bg-alt aspect-ratio width/height.
- Top bar h-9 border-b border-border bg-bg-alt: 3 traffic-light dots (red-500, amber-400, emerald-500), URL pill h-6 max-w-[60%] mx-auto rounded-md bg-bg font-mono 11px text-fg-muted.
- Content area absolute inset-0 top-9: video autoplay muted loop playsInline | img | children.
Output: file completo src/components/safari-mock.tsx.
Uso tipico03
<SafariMock url="esempio.com" imageSrc="/preview.png" />
Dipendenze04
Ti è servito? Dimmelo, oppure proponi il prossimo componente.