Safari
safariFrames screenshots in a Safari-style browser window with an address bar.
Usage
Basic usage
Use Safari shell to wrap any content (webpage screenshots, component previews, etc.).
<Safari url="hulian.design" style={{ width: 375 }}>
<img src="/screenshot.png" alt="" />
</Safari>Customize the address bar
Set address bar text via url.
<Safari url="app.hulian.design/dashboard" style={{ width: 375 }}>
<img src="/screenshot.png" alt="" />
</Safari>Picture screenshot
Pass imageSrc to directly render web page screenshots (priority to children).
<Safari url="hulian.design" imageSrc="/screenshot.png" style={{ width: 375 }} />When to Use
Wrap webpage screenshots/live content into a macOS Safari-style window frame for landing page display and document illustration. It only has a minimalist top bar with traffic lights + address bar; if you want tabs + forward/back/refresh toolbar, use Chrome, if you want command line style, use Terminal, and if you want mobile phone/tablet body, use iPhone/Tablet.
Import
import { Safari } from "@hulianui/ui"Props
Inherits ComponentPropsWithoutRef<"div">; className, style, and other div props are forwarded. Use style={{ width }} or a width utility to size the frame.
| Name | Type | Default | Description |
|---|---|---|---|
| url | string | "hulian.design" | Address bar text. |
| imageSrc | string | - | The image address of the content area, taking precedence over children. |
| headerExtra | ReactNode | - | Tool entry at the trailing edge of the chrome (share, download, and the like). When omitted the cell stays the w-12 spacer that keeps the address capsule centered, byte for byte; when provided the cell is handed over, with its width floored at the spacer width. See "Live content". |
Slots
| Slot | Type | Description |
|---|---|---|
| children | ReactNode | Custom node in content area (rendered when imageSrc is not passed). |
Usage Guidelines
- The shell has no default width. Set one through
styleorclassName; otherwise it shrinks to its content. - When both
imageSrcandchildrenare provided,imageSrctakes precedence and children are ignored.
Live content (not just screenshots)
The content area sits on the height chain: the root is a column flex container and the content area is min-h-0 flex-1. So "shell fills its parent, content takes the height left over by the chrome" only needs a height on the root. Embedded live pages, native views (Electron's WebContentsView), and scrollable panels all rely on this:
<div style={{ height: 500 }}>
<Safari url="zwfw.example.gov.cn" className="h-full" headerExtra={<DownloadButton />}>
<div ref={viewportRef} className="h-full" /> {/* measured and fed to setBounds in the main process */}
</Safari>
</div>Screenshot usage is unaffected: an auto-height column flex container is still sized by its content, and min-h-0 does not collapse it to zero (verified in Chromium).
The trailing cell in the chrome is an empty w-12 spacer by default; it exists so the address capsule stays centered relative to the traffic lights. Passing headerExtra hands that cell over, with its width floored at the spacer width: narrower content keeps the symmetry exactly, wider content grows the cell, since an off-center capsule beats a clipped button.
Related
Playground
<Safari url="hulian.design">
<img src="/screenshot.png" />
</Safari>