` with an
+inline style and no semantics of its own. Any ARIA attributes belong on the
+content inside. Pass `as` to change the element when a different semantic
+wrapper is needed (e.g. `as="figure"`).
+
+## Questions
+
+::: faq
+
+??? Why use `AspectRatio` when I can just set `aspect-ratio` in CSS?
+
+You can, and for static layouts it's fine. Reach for `AspectRatio` when the
+ratio is dynamic, when you want the intent to be visible at the call site,
+or when the same frame wraps more than one piece of content (placeholder,
+image, fallback) and all three need to share the reservation.
+
+??? Does the child need `w-full h-full`?
+
+If the child should fill the frame, yes. `aspect-ratio` only reserves the
+box — it doesn't size the child. Using `object-cover` on media is the usual
+pairing. Absolute positioning (`position: absolute; inset: 0`) on the child
+also works and is what layered compositions (image + placeholder + fallback)
+typically use.
+
+??? Can I combine `AspectRatio` with `Image.Root`?
+
+Yes — put `AspectRatio` on the outside. `Image.Root` then participates in
+the reserved frame without needing its own width/height, and the placeholder
+and fallback sit inside the same box. See the [responsive example](#compose-with-image).
+
+??? Does this affect SSR?
+
+No. `aspect-ratio` is an inline style that renders identically on server and
+client, so there's no hydration mismatch and no layout shift on hydration.
+
+??? Browser support?
+
+CSS `aspect-ratio` is supported in all modern browsers (Chrome 88+,
+Firefox 89+, Safari 15+). Legacy fallbacks (padding-bottom hack) are not
+provided — if you need to support older browsers, add your own fallback.
+
+:::
+
+
diff --git a/apps/docs/src/pages/components/semantic/avatar.md b/apps/docs/src/pages/components/semantic/avatar.md
index fe1a60eb2..03efedb6e 100644
--- a/apps/docs/src/pages/components/semantic/avatar.md
+++ b/apps/docs/src/pages/components/semantic/avatar.md
@@ -13,6 +13,8 @@ features:
level: 2
related:
- /components/primitives/atom
+ - /components/semantic/image
+ - /composables/system/use-image
---
# Avatar
@@ -82,6 +84,20 @@ flowchart TD
FallbackReg -->|"Selected by default
until image loads"| FallbackVisible
```
+> [!TIP]
+> For single-source content images with placeholder and error fallback, use [Image](/components/semantic/image) instead. Avatar specializes in identity / profile UIs with priority-based multi-source fallback.
+
+### Loading state slot props
+
+`Avatar.Image` exposes the underlying loading state from `useImage` via slot props. Use these for custom transitions, retry UI, or status indicators.
+
+| Slot prop | Purpose |
+|-----------|---------|
+| `status` | Current state: `'idle' \| 'loading' \| 'loaded' \| 'error'` |
+| `isLoaded` | True when the image has loaded successfully |
+| `isError` | True when the image failed to load |
+| `retry` | Reset the image and re-attempt loading |
+
### Priority System
When multiple images are present, the `priority` prop determines display order. Higher priority images are preferred when loaded:
diff --git a/apps/docs/src/pages/components/semantic/image.md b/apps/docs/src/pages/components/semantic/image.md
new file mode 100644
index 000000000..a0de5e627
--- /dev/null
+++ b/apps/docs/src/pages/components/semantic/image.md
@@ -0,0 +1,256 @@
+---
+title: Image - Headless Image Component with Placeholder and Fallback
+meta:
+- name: description
+ content: Headless Vue 3 image component with state-driven placeholder, error fallback, retry support, and optional IntersectionObserver-based lazy loading.
+- name: keywords
+ content: image, lazy loading, placeholder, fallback, fetchpriority, srcset, picture, Vue 3, headless, retry
+features:
+ category: Component
+ label: 'C: Image'
+ github: /components/Image/
+ renderless: false
+ level: 2
+related:
+ - /composables/system/use-image
+ - /components/semantic/avatar
+ - /composables/system/use-intersection-observer
+---
+
+# Image
+
+Headless image component with state-driven placeholder and error fallback. Tracks the loading lifecycle and exposes `retry()`, with optional IntersectionObserver-driven lazy loading.
+
+
+
+## Usage
+
+`Image.Root` owns the loading state machine via `useImage`. `Image.Img` renders the image element and reports load and error events to the context. `Image.Placeholder` is shown while idle or loading; `Image.Fallback` is shown on error.
+
+::: example
+/components/image/basic
+:::
+
+## Anatomy
+
+```vue Anatomy playground collapse
+
+
+
+
+
+
+
+
+
+
+
+```
+
+> [!INFO]
+> `src` is the only native image attribute that doesn't live on `Image.Img` because it drives the load state machine — the `idle → loading → loaded | error` lifecycle can't function without knowing the URL to track, so `Image.Root` owns it. Every other image attribute (`alt`, `width`, `height`, `srcset`, `sizes`, `crossorigin`, `referrerpolicy`, `decoding`, `loading`, `fetchpriority`) is a pure rendering concern and lives on `Image.Img`, where it describes the element that actually renders it.
+
+## Architecture
+
+`Image.Root` calls `useImage` to manage status, and optionally wires up `useIntersectionObserver` when the `lazy` prop is set. Children consume the root context via `useImageRoot`.
+
+```mermaid "Image lifecycle"
+stateDiagram-v2
+ [*] --> idle: lazy = true
+ [*] --> loading: lazy = false
+ idle --> loading: enters viewport
+ loading --> loaded: img.onload
+ loading --> error: img.onerror
+ loaded --> loading: src changes
+ error --> loading: retry / src changes
+```
+
+### Lazy loading strategies
+
+The component supports two distinct lazy loading mechanisms — pick the one that matches your requirements.
+
+| Strategy | How | When to use |
+| - | - | - |
+| Native | `loading="lazy"` on `Image.Img` | Most below-the-fold images. Zero JS, browser-managed. |
+| Observer | `lazy` prop on `Image.Root` | When you need precise control over when the source is set — e.g. blur-up transitions, prefetch, or custom intersection thresholds. |
+
+You can combine both: use the observer for state control and `loading="lazy"` as a fallback for browsers without IntersectionObserver support.
+
+## Examples
+
+::: example
+/components/image/BlurUpImage.vue 1
+/components/image/observer.vue 2
+
+### Blur-up LQIP with observer loading
+
+Wrap `Image.Root` in a reusable component that drives a fade-in transition from a low-quality placeholder (LQIP) to the full-resolution image. A tiny blurred thumbnail ships in the initial HTML (or as a small base64 string), visible immediately; the full image is withheld by the `lazy` prop until `Image.Root` intersects the viewport, at which point it loads and fades in over the blur.
+
+```mermaid "Blur-up transition"
+flowchart LR
+ Scroll["User scrolls
container in"]
+ Observer["IntersectionObserver
fires (once)"]
+ Status["status:
idle → loading"]
+ Load["img.onload"]
+ DS["data-state
= 'loaded'"]
+ Fade["CSS fade
opacity 0 → 1"]
+
+ Scroll --> Observer --> Status --> Load --> DS --> Fade
+```
+
+Reach for this pattern when you have many below-the-fold images and want both perceived performance (something visible instantly) and actual bandwidth savings (only load what's seen). It shines on photo galleries, article covers, and image-heavy marketing pages where the blur-up effect is part of the aesthetic, not just a loading trick.
+
+Three pieces make it work:
+
+- **`lazy` prop on `Image.Root`** — defers setting the real `src` until the container scrolls into view via `useIntersectionObserver`. Pair with `root-margin` (e.g. `"200px"`) to start loading a bit before the container actually intersects.
+- **LQIP inside `Image.Placeholder`** — the blurred preview sits in the reserved frame (typically `absolute inset-0`). It's decorative, so `Image.Placeholder` auto-sets `aria-hidden="true"`.
+- **`data-state`-driven opacity** — the real `Image.Img` uses `opacity-0 transition-opacity data-[state=loaded]:opacity-100`, so the fade-in is CSS-only and doesn't require threading slot props through the template.
+
+Gotcha: the `lazy` prop needs a wrapper element to observe — it warns and skips observation when combined with `renderless`. If you need a lazy `
`, observe a parent yourself with `useIntersectionObserver` and gate the source manually via `useImage`'s `eager` option.
+
+| File | Role |
+|------|------|
+| `BlurUpImage.vue` | Reusable component with LQIP placeholder, `lazy` intersection loading, and CSS-driven fade-in |
+| `observer.vue` | Entry point rendering a scrollable list of blur-up images to demonstrate the viewport trigger |
+
+:::
+
+::: example
+/components/image/PictureImage.vue 1
+/components/image/picture.vue 2
+
+### Picture element with format negotiation
+
+Use `renderless` mode on both `Image.Root` and `Image.Img` to drop their wrapper elements and compose them directly inside a native `` element. The browser walks the `` list and picks the first format it supports (WebP, AVIF, JPEG XL, etc.); `Image.Img` renders the fallback `
` that older browsers use. All loading state is still tracked by the surrounding `Image.Root`, so `Image.Placeholder` and `Image.Fallback` behave identically to the compound form — no extra wiring needed.
+
+Reach for this pattern when you're serving modern image formats for bandwidth or quality gains while keeping a universal fallback, or when you need full control over DOM structure (e.g., embedding inside a `` with a ``). It's the only way to use `Image.Root`'s state machine with a native `` without losing either the format negotiation or the state-driven placeholder/fallback pattern.
+
+Two details worth knowing:
+
+- **`renderless` on both** — `Image.Root` must go renderless to avoid wrapping `` in an extra ``, and `Image.Img` must go renderless so you can place the inner `
![]()
` as `
`'s last child (where the browser expects it).
+- **`lazy` doesn't work here** — observer-driven lazy loading relies on a wrapper element to measure against. Combine native `loading="lazy"` on the inner `
` with `fetchpriority` hints for equivalent deferred loading, or wrap the whole composition in an outer element you observe manually.
+
+The `` children are fully browser-driven — `Image.Img` never sees their URLs. It just reports the final `
` element's `load`/`error` events back to the context.
+
+| File | Role |
+|------|------|
+| `PictureImage.vue` | Reusable wrapper that emits a `` with typed `` children; both `Image.Root` and `Image.Img` go renderless to drop wrapper elements |
+| `picture.vue` | Entry point passing WebP and fallback sources to demonstrate format negotiation |
+
+:::
+
+## Recipes
+
+### Hero image with high priority
+
+Set `loading="eager"` and `fetchpriority="high"` on hero images to optimize LCP. Always include `width` and `height` to prevent layout shift.
+
+```vue
+
+
+
+
+
+
+
+
+```
+
+### Retry on error
+
+The `retry()` function is available from both `Image.Root` and `Image.Fallback` slot props.
+
+```vue
+
+
+
+ Loading...
+
+
+
+
+
+```
+
+## Styling
+
+Every Image sub-component exposes `data-state` reflecting the current status (`idle`, `loading`, `loaded`, or `error`). Prefer styling against these data attributes with CSS over threading slot props — the transitions stay CSS-only and the template stays declarative.
+
+```vue
+
+
+
+```
+
+```css
+/* Or with plain CSS */
+[data-state='loaded'] { opacity: 1; }
+[data-state='loading'] { animation: pulse 1s infinite; }
+[data-state='error'] { border-color: red; }
+```
+
+Slot props (`status`, `isLoaded`, etc.) remain available for the rare cases where logic has to branch in the template, but reach for CSS + data attributes first.
+
+## Accessibility
+
+| Element | ARIA / behavior |
+| - | - |
+| `Image.Img` | `role="img"`, accepts `alt` for accessible name |
+| `Image.Placeholder` | `aria-hidden="true"` — placeholder is decorative |
+| `Image.Fallback` | `role="img"` — provide alternate text inside the slot |
+
+Always pass `width` and `height` props on `Image.Img` to reserve layout space and prevent Cumulative Layout Shift while the image loads.
+
+## FAQ
+
+::: faq
+
+??? When should I use Avatar vs Image?
+
+`Avatar` is for identity / profile UIs with priority-based multi-source fallback (e.g. high-res then low-res then initials). `Image` is for general content images with state-driven placeholder and fallback for a single source.
+
+??? Should I use the `lazy` prop or the `loading="lazy"` attribute?
+
+Use native `loading="lazy"` for most cases — it's declarative, zero-JS, and browser-managed. Use the `lazy` prop on `Image.Root` only when you need to control exactly when the source is set, such as for blur-up transitions, prefetching, or custom intersection thresholds.
+
+??? How do I prevent layout shift?
+
+Always set `width` and `height` on `Image.Img`. The browser uses these to reserve space before the image loads, eliminating Cumulative Layout Shift.
+
+??? Can I use a `` element with multiple `` formats?
+
+Yes — set `renderless` on both `Image.Root` and `Image.Img`, then compose them inside a native `` element. See the picture example.
+
+??? How do I fade in once the image loads?
+
+Style against the `data-state` attribute that every Image sub-component exposes. Using data attributes keeps the transition CSS-only — no slot-prop threading required.
+
+```vue
+
+
+
+```
+
+The `data-state` attribute holds the current status (`idle`, `loading`, `loaded`, or `error`). The blur-up example uses this pattern.
+
+??? Why isn't `src` on `Image.Img`?
+
+`src` lives on `Image.Root` because it owns the state machine — the source is what the state is about. `Image.Img` accepts the rendering attributes (`alt`, `width`, `height`, `srcset`, `sizes`, etc.) that belong to the rendered element.
+
+:::
+
+
diff --git a/apps/docs/src/pages/composables/index.md b/apps/docs/src/pages/composables/index.md
index 9be9fee96..498b8662b 100644
--- a/apps/docs/src/pages/composables/index.md
+++ b/apps/docs/src/pages/composables/index.md
@@ -262,6 +262,7 @@ Browser API wrappers with automatic lifecycle cleanup.
| [useClickOutside](/composables/system/use-click-outside) | Detect clicks outside an element |
| [useEventListener](/composables/system/use-event-listener) | Handle DOM events with automatic cleanup |
| [useHotkey](/composables/system/use-hotkey) | Handle hotkey combinations and sequences |
+| [useImage](/composables/system/use-image) | Image loading state machine with deferred loading and retry |
| [useIntersectionObserver](/composables/system/use-intersection-observer) | Intersection Observer API for visibility detection |
| [useLazy](/composables/system/use-lazy) | Defer rendering until first activation for dialogs, menus, and tooltips |
| [useMediaQuery](/composables/system/use-media-query) | Reactive CSS media query matching |
diff --git a/apps/docs/src/pages/composables/system/use-image.md b/apps/docs/src/pages/composables/system/use-image.md
new file mode 100644
index 000000000..5951c0f63
--- /dev/null
+++ b/apps/docs/src/pages/composables/system/use-image.md
@@ -0,0 +1,180 @@
+---
+title: useImage - Image Loading State Composable for Vue 3
+meta:
+- name: description
+ content: Reactive image loading state machine with idle, loading, loaded, and error states. Supports deferred loading via an eager gate and retry semantics.
+- name: keywords
+ content: useImage, image loading, lazy loading, intersection observer, Vue 3, composable, state machine, retry
+features:
+ category: Composable
+ label: 'E: useImage'
+ github: /composables/useImage/
+ level: 2
+related:
+ - /composables/system/use-intersection-observer
+ - /composables/system/use-lazy
+ - /components/semantic/image
+ - /components/semantic/avatar
+---
+
+# useImage
+
+Tracks image loading state as a reactive state machine with idle, loading, loaded, and error states.
+
+
+
+## Usage
+
+The `useImage` composable owns the loading lifecycle for a single image source.
+Bind the returned `source`, `onLoad`, and `onError` to a plain image element.
+
+```vue collapse
+
+
+
+
+
+```
+
+## Architecture
+
+```mermaid "useImage state machine"
+stateDiagram-v2
+ [*] --> idle: eager = false
+ [*] --> loading: eager = true
+ idle --> loading: eager flips true
+ loading --> loaded: onLoad
+ loading --> error: onError
+ loaded --> loading: src changes
+ error --> loading: retry / src changes
+```
+
+## Reactivity
+
+| Property/Method | Reactive | Notes |
+| - | :-: | - |
+| `status` | | Readonly ShallowRef of `'idle' \| 'loading' \| 'loaded' \| 'error'` |
+| `isIdle` / `isLoading` / `isLoaded` / `isError` | | Readonly boolean refs derived from `status` |
+| `source` | | Gated `src` — `undefined` while idle, otherwise the current source |
+| `onLoad` / `onError` | | Bind to image `load` / `error` events |
+| `retry` | | Reset back to `loading` and re-attempt |
+
+## Examples
+
+::: example
+/composables/use-image/basic
+
+### Basic usage
+
+Wire `useImage` directly to a plain `
` element. The composable returns `source`, `onLoad`, `onError`, and four state refs — bind them to the element and you have the full load lifecycle for ~10 lines of setup.
+
+Reach for raw `useImage` when the image lives in markup the `Image` compound can't produce — hand-rolled ``, a canvas-adjacent element, third-party wrappers, or anywhere you want the state machine without v0's DOM. For typical content images, prefer `Image.Root` + `Image.Img` which packages the same composable with built-in placeholder and fallback slots.
+
+The example surfaces the `status` ref as a live label and includes buttons that swap between a working URL and a known-broken URL, so the transitions `idle → loading → loaded | error` play out on click.
+
+:::
+
+::: example
+/composables/use-image/useLazyImage.ts 1
+/composables/use-image/LazyImage.vue 2
+/composables/use-image/lazy.vue 3
+
+### Compose with useIntersectionObserver
+
+Wrap `useImage` and `useIntersectionObserver` in a small custom composable to build a reusable viewport-driven lazy loader. The observer returns a reactive `isIntersecting` flag; pipe that into `useImage`'s `eager` option and the source is withheld — status stays `idle`, no network request is made — until the target element scrolls into view.
+
+```mermaid "Reactive signal pipeline"
+flowchart LR
+ Target["target
ref<HTMLElement>"]
+ Observer["useIntersectionObserver
{ once: true }"]
+ Signal["isIntersecting
Ref<boolean>"]
+ Image["useImage
{ eager }"]
+ Source["source
Ref<string | undefined>"]
+
+ Target --> Observer --> Signal --> Image --> Source
+```
+
+Reach for this pattern when the built-in `` isn't a fit: when you're not using the `Image` compound at all, when you need the observer target to be a different element than the image container, or when you want to share one observer across several images. The composable becomes the single owner of both "has it entered the viewport" and "what's the load status" — callers just destructure `{ target, source, onLoad, onError, isLoaded }` and wire them up.
+
+Three things make this composition work:
+
+- **`once: true` on the observer** — once the element intersects, the observer disconnects. `isIntersecting` stays `true` thereafter, so the image loads once and doesn't regress if the user scrolls it back off-screen.
+- **`rootMargin`** — start loading slightly before intersection (e.g. `"200px"`) so the image is typically loaded by the time it's actually visible. The default `"0px"` fires exactly at viewport entry, which can produce a visible blank frame on fast scrolling.
+- **`eager: isIntersecting`** — the observer's reactive flag drives `useImage`'s gate directly. No manual `watch`, no imperative calls — Vue's reactivity handles the state transition.
+
+Under the hood `` does exactly this; the custom composable exists so you can apply the same pattern without the compound component.
+
+| File | Role |
+|------|------|
+| `useLazyImage.ts` | Custom composable combining `useImage` and `useIntersectionObserver` — returns `{ target, ...image }` for consumers |
+| `LazyImage.vue` | Presentational component binding the returned `target` to its root, `source` to the `
`, and handlers to `load`/`error` |
+| `lazy.vue` | Entry point rendering several lazy images in a scrolling container to demonstrate the viewport trigger |
+
+:::
+
+::: example
+/composables/use-image/RetryableImage.vue 1
+/composables/use-image/retry.vue 2
+
+### Retry on error
+
+Build a reusable image component that surfaces a retry button when loading fails. Calling `retry()` resets the status back to `loading` (or `idle` if `eager` is currently `false`) without changing the `src` — the browser re-attempts the same request, which handles the common case of transient network failures, flaky CDNs, or images that aren't in cache yet on the second attempt.
+
+Reach for this pattern anywhere a failed image shouldn't be a dead end: user-uploaded content that might take a moment to propagate through a CDN, photos behind a request-signed URL that can expire, or any UX where a "try again" button is friendlier than leaving a broken-image icon on screen. Track an `attempts` counter alongside `retry` when you want to cap retries or show progress ("Attempt 3 of 3") — `useImage` doesn't manage retry bookkeeping itself, which keeps it headless.
+
+A few details worth knowing:
+
+- **`retry()` is idempotent relative to `src`** — it doesn't change the source, just rewinds the state machine. If the image fails deterministically (404, CORS error), retry loops without progress; a fallback source or a cap is the caller's responsibility.
+- **Works with reactive `src` changes** — swapping `src` also resets the state machine automatically, so you typically call `retry()` only when you want to re-attempt the *same* URL. Set a new URL via the reactive ref if you want to try a different source.
+- **Pairs with the `status` ref** — the example conditionally renders the button via `isError`, but you can style around `data-state="error"` for CSS-only treatments (e.g., a red border that appears on error).
+
+Not limited to user-facing retries — the same pattern works for programmatic retries with backoff: watch `isError`, schedule a timer, call `retry()`.
+
+| File | Role |
+|------|------|
+| `RetryableImage.vue` | Wraps `useImage`, tracks an `attempts` counter, and renders a retry button inside an `isError` branch. Simulates a flaky network: each click has a 25% chance of swapping in the real source (success) or re-requesting the broken one (another failure) |
+| `retry.vue` | Entry point rendering a single `RetryableImage` — click Retry repeatedly to watch the state cycle between `loading` and `error` until a retry happens to land on success |
+
+:::
+
+## FAQ
+
+::: faq
+
+??? Why is the option called `eager` instead of `lazy`?
+
+`eager` is a reactive gate that mirrors the semantics of HTML's `loading="eager"` attribute — when `eager` is `true`, the image starts loading. When `false`, the source is withheld and status stays `idle`. Using `eager` lets you write expressive bindings like `eager: isIntersecting`.
+
+??? Does `useImage` use IntersectionObserver internally?
+
+No. `useImage` is the state machine; viewport detection is a separate concern. Compose it with `useIntersectionObserver` (or any other reactive gate) by passing the result to the `eager` option.
+
+??? What about native `loading="lazy"`?
+
+Native browser-level lazy loading is a separate mechanism. The browser still fires `load` and `error` events, so `useImage` works correctly with or without it. Reach for the `eager` gate only when you need to control exactly when the source is set.
+
+??? What happens when `src` changes?
+
+Status resets to `loading` (or `idle` if `eager` is `false`) and the source updates. Already-loaded or errored states do not persist across source changes.
+
+??? Why does `source` return `undefined` while idle?
+
+So you can bind it directly to an `
` element without the browser starting a request. The browser ignores `
` elements without a `src`, which is exactly what you want during the deferred phase.
+
+:::
+
+
diff --git a/apps/docs/src/typed-router.d.ts b/apps/docs/src/typed-router.d.ts
index e2e120c93..e9255f08a 100644
--- a/apps/docs/src/typed-router.d.ts
+++ b/apps/docs/src/typed-router.d.ts
@@ -195,6 +195,13 @@ declare module 'vue-router/auto-routes' {
Record,
| never
>,
+ '/components/primitives/aspect-ratio': RouteRecordInfo<
+ '/components/primitives/aspect-ratio',
+ '/components/primitives/aspect-ratio',
+ Record,
+ Record,
+ | never
+ >,
'/components/primitives/atom': RouteRecordInfo<
'/components/primitives/atom',
'/components/primitives/atom',
@@ -279,6 +286,20 @@ declare module 'vue-router/auto-routes' {
Record,
| never
>,
+ '/components/semantic/carousel': RouteRecordInfo<
+ '/components/semantic/carousel',
+ '/components/semantic/carousel',
+ Record,
+ Record,
+ | never
+ >,
+ '/components/semantic/image': RouteRecordInfo<
+ '/components/semantic/image',
+ '/components/semantic/image',
+ Record,
+ Record,
+ | never
+ >,
'/components/semantic/pagination': RouteRecordInfo<
'/components/semantic/pagination',
'/components/semantic/pagination',
@@ -636,6 +657,13 @@ declare module 'vue-router/auto-routes' {
Record,
| never
>,
+ '/composables/system/use-image': RouteRecordInfo<
+ '/composables/system/use-image',
+ '/composables/system/use-image',
+ Record,
+ Record,
+ | never
+ >,
'/composables/system/use-intersection-observer': RouteRecordInfo<
'/composables/system/use-intersection-observer',
'/composables/system/use-intersection-observer',
@@ -1143,6 +1171,12 @@ declare module 'vue-router/auto-routes' {
views:
| never
}
+ 'src/pages/components/primitives/aspect-ratio.md': {
+ routes:
+ | '/components/primitives/aspect-ratio'
+ views:
+ | never
+ }
'src/pages/components/primitives/atom.md': {
routes:
| '/components/primitives/atom'
@@ -1215,6 +1249,18 @@ declare module 'vue-router/auto-routes' {
views:
| never
}
+ 'src/pages/components/semantic/carousel.md': {
+ routes:
+ | '/components/semantic/carousel'
+ views:
+ | never
+ }
+ 'src/pages/components/semantic/image.md': {
+ routes:
+ | '/components/semantic/image'
+ views:
+ | never
+ }
'src/pages/components/semantic/pagination.md': {
routes:
| '/components/semantic/pagination'
@@ -1521,6 +1567,12 @@ declare module 'vue-router/auto-routes' {
views:
| never
}
+ 'src/pages/composables/system/use-image.md': {
+ routes:
+ | '/composables/system/use-image'
+ views:
+ | never
+ }
'src/pages/composables/system/use-intersection-observer.md': {
routes:
| '/composables/system/use-intersection-observer'
diff --git a/packages/0/README.md b/packages/0/README.md
index 67fcd3e30..d6736ad84 100644
--- a/packages/0/README.md
+++ b/packages/0/README.md
@@ -81,6 +81,7 @@ import { ... } from '@vuetify/v0/date' // Date adapter and utilities
| Component | Description |
|-----------|-------------|
+| [AspectRatio](https://0.vuetifyjs.com/components/primitives/aspect-ratio) | Fixed width-to-height ratio container via CSS `aspect-ratio` |
| [Atom](https://0.vuetifyjs.com/components/primitives/atom) | Polymorphic base element with dynamic `as` prop and renderless mode |
| [Portal](https://0.vuetifyjs.com/components/primitives/portal) | Teleport wrapper with automatic z-index stacking via useStack |
| [Presence](https://0.vuetifyjs.com/components/primitives/presence) | Animation-agnostic mount lifecycle with lazy mounting and exit timing |
@@ -138,6 +139,7 @@ import { ... } from '@vuetify/v0/date' // Date adapter and utilities
| [Avatar](https://0.vuetifyjs.com/components/semantic/avatar) | Image/fallback avatar with priority loading |
| [Breadcrumbs](https://0.vuetifyjs.com/components/semantic/breadcrumbs) | Navigation breadcrumbs with overflow detection and truncation |
| [Carousel](https://0.vuetifyjs.com/components/semantic/carousel) | Scroll-snap slide navigation with multi-slide display and drag/swipe |
+| [Image](https://0.vuetifyjs.com/components/semantic/image) | Image with placeholder, error fallback, and lazy loading |
| [Pagination](https://0.vuetifyjs.com/components/semantic/pagination) | Page navigation with semantic `