Image
Displays image resources, with an optional built-in preview feature.
Basic Usage
Basic image
Fit Mode
object-fit variants
cover (crop to fill)
contain (fit fully)
Set object-fit via fit, then fix the image container size with ::part(image) to achieve the crop effect.
Placeholder and Fallback
Loading placeholder
With placeholder, a light-gray placeholder is shown until the image finishes loading, then it switches to the image.
Load failure fallback
Default failure placeholder
Custom fallback image
When the image fails to load, an "Image failed to load" placeholder is shown by default; the fallback attribute can specify a fallback image URL, and if the fallback also fails, it falls back to the placeholder text.
Lazy Loading
Lazy-loading long list (loads image by image while scrolling)
With lazy, an image only starts loading when it enters the viewport; pair it with placeholder to show a "Loading" placeholder. Scroll down the list and watch the placeholder → loaded transition (images already in the viewport load immediately).
Preview
Click to preview (built-in overlay)
Click the image to open a full-screen preview overlay: the toolbar supports zoom in / out, rotate, flip horizontal / vertical, and download; after zooming in you can drag to pan and use the wheel to zoom. On touch screens, pinch with two fingers to zoom (anchored at the finger midpoint, which also pans as it moves) or drag with one finger to pan. Press Esc or click the mask to close. The close button is focused when opened, and focus is restored on close. Emits oas-preview (detail contains src).
When opened, the preview overlay is mounted to document.body (portal) so position: fixed keeps working even when the component sits inside a transform/filter ancestor. While portaled, ::part(preview-*) cannot pierce from the host — customize via CSS variables instead.
Gallery Preview
Gallery preview (multi-image paging)
Set preview-src-list (a JSON array of URLs) to enter gallery mode: use the side arrows, the toolbar paging buttons, or ←→ keys to page through, with an "n/total" counter. If an image fails to load, a failure placeholder (reusing the error slot content) is shown instead of a blank stage.
Infinite loop (infinite)
With infinite, paging wraps around: "next" on the last image returns to the first, and "prev" on the first image jumps to the last.
Thumbnail vs. Original
preview-src (separate thumbnail and original)
The list shows the src thumbnail, while the preview loads the high-resolution original from preview-src (the download link points to the original as well).
Controlled Preview
Controlled preview (preview-open + openPreview())
The external button calls openPreview() to open; when preview-open is present it controls the open state — internal open/close (click / Esc / close button) reflects back to the attribute and emits oas-preview-change (detail { open }), and adding/removing the attribute drives the overlay as well (two-way sync).
Custom Toolbar
Custom toolbar (slot=toolbar + oas-toolbar-render)
With a template[slot="toolbar"], the template content is cloned into the preview overlay toolbar area, replacing the default button group (the original template stays in the light DOM; without a template the default toolbar remains). oas-toolbar-render is emitted on clone and on every open: use element (the toolbar container) and actions (the viewer commands) from the detail to wire custom buttons to viewer commands. The buttons above are wired — try them.
The detail of oas-toolbar-render is { element, actions }: element is the cloned toolbar container (bind button events on it; re-binding is idempotent); the actions command set is listed below (prev/next work in gallery mode and are no-ops in single-image mode):
| Command | Description |
|---|---|
zoomIn / zoomOut | Zoom in / out one step (same transform state machine as the wheel and default buttons) |
rotateLeft / rotateRight | Rotate 90° counter-clockwise / clockwise |
flipX / flipY | Flip horizontally / vertically |
download | Trigger a download of the current preview image |
close | Close the preview overlay (equivalent to Esc / mask click) |
prev / next | Previous / next gallery image (respects the infinite boundary, same as default paging) |
Host wiring example:
el.addEventListener('oas-toolbar-render', (e) => {
const { element, actions } = e.detail
element.querySelector('#my-zoom-in').onclick = actions.zoomIn
})The shared preview of oas-image-group supports this as well: place a template[slot="toolbar"] directly inside the group and it is forwarded to the shared overlay. Custom toolbar buttons receive token-based base styles (readable in light/dark) and participate in the Tab focus trap.
Load Events
Load events (oas-load / oas-error)
Load success → oas-load
Load failure → oas-error
The main image emits oas-load on success and oas-error on final failure (both details contain src); during a fallback retry no oas-error is emitted — it fires only when the fallback also fails (detail.src is the fallback URL).
Custom Placeholder and Error Slots
Custom placeholder / error slots
slot="placeholder"
slot="error"
template[slot="placeholder"] / template[slot="error"] (or any element carrying the same slot attribute) is cloned into the corresponding placeholder area, taking precedence over the built-in text; a failed image inside a gallery preview reuses the error slot content as well.
Flip
Flip (flipX / flipY)
The toolbar provides "Flip horizontal" and "Flip vertical", composed into the same transform state machine as zoom, rotate, and pan — they compose without interfering with each other.
Drag to Pan and Wheel to Zoom
Drag to pan + wheel to zoom (large images)
After zooming in, press and drag on the preview image to pan (clamped to the visible bounds). Scroll the wheel inside the preview area to zoom (page scrolling is blocked). Zoom step and limits can be overridden with CSS variables (see the table below).
Photo Wall Composition
Photo wall (each image enters the same gallery)
A grid of thumbnails: clicking any one opens the shared gallery starting from that image (opening index matches the thumbnail src against the list).
Image Group (oas-image-group)
Image group (declarative photo wall → shared preview)
Put multiple oas-image elements inside oas-image-group and the container collects them into a shared gallery: clicking any image opens the preview from that image, and the side arrows / keyboard ←→ page through the whole set (n/total counter). A child's own preview click is taken over by the container as the shared gallery — no per-image preview-src-list needed.
Dynamically adding/removing children (or changing their src) syncs the gallery automatically; current optionally controls the current index — when present, clicks open at current and internal paging reflects back to the attribute; when absent, the attribute is not created and only oas-change is emitted (detail: { current, prev }, see the message feedback above); infinite is passed through to the shared preview.
Relation to preview-src-list: preview-src-list fits a data-array-driven single-entry gallery (one thumbnail representing the set); oas-image-group fits a declarative photo wall — every child image is naturally a gallery member, adding it to the DOM adds it to the set. Both share the same preview overlay capabilities (zoom/rotate/flip/download/Esc/mask/focus trap); children can still use preview-src for a dedicated preview URL, or preview-src-list to expand one child into multiple gallery entries. The group lays out as a wrapping flex row; the gap is customizable via the CSS variable --oas-image-group-gap.
Preview Zoom CSS Variables
| CSS Variable | Default | Description |
|---|---|---|
--oas-image-zoom-step | 0.5 | Zoom in/out step (shared by toolbar buttons and the wheel) |
--oas-image-zoom-min | 0.5 | Zoom lower bound |
--oas-image-zoom-max | 3 | Zoom upper bound |
Override them on the host element or at the theme level, e.g. style="--oas-image-zoom-max: 5".
Explicit Dimensions and Referrer Policy
width / height set explicit dimensions: numbers are treated as px, and any CSS size string (e.g. 50%) passes through as-is onto the inner img (the max-width: 100% responsive constraint is preserved); empty values clear the sizing back to auto layout. referrerpolicy passes through to the inner img to control the privacy policy (e.g. no-referrer); an empty value falls back to the browser default.
Explicit width / height
referrerpolicy pass-through
API
oas-image
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
alt | Alternative text | — | — |
fallback | Fallback image URL to switch to on load failure; when not set, shows the "Image failed to load" placeholder | string | — |
fit | object-fit value | string | — |
height | Explicit height (same rules as width) | string | — |
infinite | Loop around the first/last gallery image | boolean | — |
lazy | Lazy load: the image starts loading only when it enters the viewport (IntersectionObserver); loads immediately when already in the viewport; falls back to eager loading when unsupported | boolean | — |
placeholder | Show a light gray placeholder before the image finishes loading | boolean | — |
preview | Enable built-in preview: click to zoom + zoom/rotate/download + Esc to close + focus trap | boolean | — |
preview-open | Controlled preview open state (controlled when present, two-way with oas-preview-change; see also openPreview()) | boolean | — |
preview-src | Original image URL for preview (thumbnail/original separation; falls back to src) | string | — |
preview-src-list | Gallery preview: JSON array of URLs; prev/next paging + counter + keyboard ←→ after opening | string | — |
referrerpolicy | Referrer policy passed through to the inner img | string | — |
src | Image URL | string | — |
width | Explicit width (numbers become px; any CSS size string passes through; empty restores auto) | string | — |
Events
| Event | Description |
|---|---|
oas-error | Image failed finally (fallback chain exhausted), detail: { src } |
oas-load | Image loaded successfully, detail: { src } |
oas-preview | Preview overlay opened, detail: { src }; closing the overlay does not emit an event |
oas-preview-change | Preview open state changed, detail: { open } |
oas-preview-nav | Gallery page change/jump, detail: { index, src }; lets the oas-image-group container take over the index |
oas-toolbar-render | Custom toolbar render notification (fired on clone and on every preview open; host re-binding is idempotent), detail { element, actions }: element is the cloned toolbar container, actions is the viewer command set |
Slots
| Name | Description |
|---|---|
template[slot="error"] | Custom error placeholder content (shared by the main image and gallery preview failures) |
template[slot="placeholder"] | Custom loading placeholder content (gray block + text by default) |
template[slot="toolbar"] | Custom preview toolbar content: cloned into the overlay toolbar area, replacing the default button group (defaults remain when absent). Emits oas-toolbar-render on clone and on every open (detail { element, actions }); action commands: zoomIn/zoomOut/rotateLeft/rotateRight/flipX/flipY/download/close/prev/next (prev/next are no-ops in single-image mode) |
oas-image-group
Attributes
| Attribute | Description | Type | Default |
|---|---|---|---|
current | Current gallery index (optional controlled): when present, clicks open at current, internal paging reflects back to the attribute, and external changes drive the preview jump; when absent the attribute is not created and only oas-change is emitted | — | — |
infinite | Loop paging around the first/last image (passed through to the shared preview host) | boolean | — |
Events
| Event | Description |
|---|---|
oas-change | Gallery page changed, detail: { current, prev } |
oas-preview | Shared preview overlay opened, detail: { src } (src is the current image URL) |
Slots
| Name | Description |
|---|---|
| default | Gallery members (oas-image) |
template[slot="toolbar"] | Forwarded to the shared preview host: customizes the shared preview overlay toolbar (clone replaces the default button group + oas-toolbar-render command channel, same as oas-image) |
CSS Variables
| CSS Variable | Default |
|---|---|
--oas-image-group-gap | var(--oas-space-3) |