# Image

**`sonic-image`** wraps a responsive `<img>` with aspect ratio, `object-fit: cover`, optional rounded corners and a load transition. The host shows a placeholder background (`--sc-placeholder-bg`) until the file loads. With no `loading`, the image is `lazy`; set `loading="eager"` for above-the-fold images.

## Rounded

With no `rounded`, corners are square. Boolean `rounded` uses the theme token `--sc-rounded`; `full` draws a circle when paired with `ratio="1/1"`.

<sonic-code>
  <template>
    <div class="grid gap-6 grid-cols-3 lg:grid-cols-4 items-end text-center text-xs">
      <div>
        <sonic-image ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">default</p>
      </div>
      <div>
        <sonic-image rounded="sm" ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">sm</p>
      </div>
      <div>
        <sonic-image rounded ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">rounded</p>
      </div>
      <div>
        <sonic-image rounded="md" ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">md</p>
      </div>
      <div>
        <sonic-image rounded="lg" ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">lg</p>
      </div>
      <div>
        <sonic-image rounded="xl" ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">xl</p>
      </div>
      <div>
        <sonic-image rounded="full" ratio="1/1" src="/img/concorde/plane_04.jpg" alt="Concorde on the apron"></sonic-image>
        <p class="mt-2">full</p>
      </div>
    </div>
  </template>
</sonic-code>

## Aspect ratio

`ratio` sets CSS `aspect-ratio` on the image (default `auto`). The image always fills the host width.

<sonic-code>
  <template>
    <div class="grid gap-8 grid-cols-2 items-center">
      <sonic-image src="/img/concorde/plane_12.jpg" alt="Concorde at the gate"></sonic-image>
      <sonic-image ratio="16/9" src="/img/concorde/plane_12.jpg" alt="Concorde at the gate"></sonic-image>
      <sonic-image ratio="1/1" src="/img/concorde/plane_12.jpg" alt="Concorde at the gate"></sonic-image>
      <sonic-image ratio="2/3" src="/img/concorde/plane_12.jpg" alt="Concorde at the gate"></sonic-image>
    </div>
  </template>
</sonic-code>

## Object position

`objectPosition` maps to CSS `object-position` (default `center center`). Use it on wide crops when the subject sits off-center.

<sonic-code>
  <template>
    <div class="grid gap-8 grid-cols-2 items-center">
      <sonic-image ratio="11/4" src="/img/concorde/plane_11.jpg" alt="Concorde flying over a field"></sonic-image>
      <sonic-image objectPosition="center top" ratio="11/4" src="/img/concorde/plane_11.jpg" alt="Concorde flying over a field"></sonic-image>
    </div>
  </template>
</sonic-code>

## Cover

`cover` stretches the image over a **positioned** parent (`position: relative` with a height). `ratio` is ignored while `cover` is set.

<sonic-code>
  <template>
    <div class="h-96 relative rounded-lg overflow-hidden">
      <sonic-image cover src="/img/concorde/plane_10.jpg" alt="Concorde fleet parked in a star"></sonic-image>
    </div>
  </template>
</sonic-code>

## Load transition

Set `transition` to `fade` or `fade-scale-out`. The image stays transparent until the browser fires `load`, then animates in. **Changing `src` on an existing element does not replay the effect**: mount a new `sonic-image` instead (galleries, infinite scroll).

<sonic-code>
  <template>
    <div class="flex flex-wrap gap-2 mb-4">
      <sonic-button
        size="sm"
        onclick="(function () {
          var host = document.getElementById('sonic-image-transition-host');
          var img = document.createElement('sonic-image');
          img.transition = 'fade';
          img.ratio = '1/1';
          img.rounded = 'md';
          img.loading = 'eager';
          img.style.width = '8rem';
          var pics = ['01', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13'];
          img.src = '/img/concorde/plane_' + pics[host.childElementCount % pics.length] + '.jpg';
          host.appendChild(img);
        })()"
      >
        Add image (fade)
      </sonic-button>
      <sonic-button
        size="sm"
        variant="outline"
        onclick="(function () {
          var host = document.getElementById('sonic-image-transition-host');
          var img = document.createElement('sonic-image');
          img.transition = 'fade-scale-out';
          img.ratio = '1/1';
          img.rounded = 'md';
          img.loading = 'eager';
          img.style.width = '8rem';
          var pics = ['01', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13'];
          img.src = '/img/concorde/plane_' + pics[host.childElementCount % pics.length] + '.jpg';
          host.appendChild(img);
        })()"
      >
        Add image (fade-scale-out)
      </sonic-button>
      <sonic-button
        size="sm"
        variant="ghost"
        onclick="document.getElementById('sonic-image-transition-host').replaceChildren()"
      >
        Clear
      </sonic-button>
    </div>
    <div id="sonic-image-transition-host" class="flex flex-wrap gap-2 min-h-[5rem]"></div>
  </template>
</sonic-code>

## Accessibility

Always set a meaningful `alt`. Use `alt=""` only for decorative images.

<sonic-code>
  <template>
    <sonic-image
      rounded="md"
      ratio="16/9"
      alt="Crowd gathered around Concorde at night"
      src="/img/concorde/plane_07.jpg"
    ></sonic-image>
  </template>
</sonic-code>

## API reference

| Attribute | Values | Description |
|----------|---------|-------------|
| `src` | URL | Image URL passed to `<img src>` |
| `alt` | string | Accessible description; `""` only for decorative images |
| `loading` | `lazy` (default) · `eager` | Native lazy loading |
| `ratio` | CSS ratio (default `auto`) | `aspect-ratio` of the image; ignored when `cover` is set |
| `rounded` | `none` (default) · boolean · `base` · `sm` · `md` · `lg` · `xl` · `full` | Corner radius from theme tokens |
| `objectPosition` | CSS position (default `center center`) | Maps to `object-position` |
| `imageRendering` | CSS value (default `auto`) | Maps to `image-rendering` |
| `cover` | boolean | Absolutely fills a positioned parent |
| `transition` | `fade` · `fade-scale-out` | Entrance animation after `load`; not replayed on `src` change |

No slots.
