# Bubble

A chat message bubble with left and right alignment, semantic color variants, and optional previews for image, audio, file and link attachments. The bubble is only the message surface. Avatars, sender rows and message lists are composed around it with the layout utilities and other components.

Part of the Harmonia Alpine.js component library. Every directive uses the `x-h-` prefix.

## Usage

Every part of the bubble is optional. Place any combination of `x-h-bubble-header`, `x-h-bubble-content`, `x-h-bubble-footer`, attachment previews and `x-h-bubble-reactions` inside an `x-h-bubble` element. Use `data-align="right"` for sent messages and the default left alignment for received ones. A `<time>` element inside the header is automatically de-emphasized.

## Directives

`x-h-bubble` is the root. The directives compose one component and must be nested as shown in the Examples below (the library throws at runtime when a required ancestor is missing):

- `x-h-bubble`
- `x-h-bubble-header`
- `x-h-bubble-content`
- `x-h-bubble-footer`
- `x-h-bubble-image`
- `x-h-bubble-gallery`
- `x-h-bubble-gallery-more`
- `x-h-bubble-audio`
- `x-h-bubble-file`
- `x-h-bubble-link`
- `x-h-bubble-reactions`

## API

### Attributes

#### x-h-bubble

| Attribute    | Type                                                                                                                                         | Required | Description                                                                                                        |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| data-align   | `left`<br />`right`                                                                                                                          | false    | Message side. Defaults to `left` (received). Use `right` for sent ones.                                            |
| data-variant | `primary`<br />`secondary`<br />`positive`<br />`negative`<br />`warning`<br />`information`<br />`outline`<br />`transparent`<br />`custom` | false    | Semantic color style. Defaults to `secondary`. See CSS Variables below for the `custom` variant. |

### CSS Variables

| Variable    | Default | Description                                                                                                                       |
| ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| --bg-bubble | none    | Background color used when `data-variant` is set to `custom`. Set it on the bubble itself, an ancestor, or in the theme CSS file. |
| --fg-bubble | none    | Text/icon color used when `data-variant` is set to `custom`. Set it on the bubble itself, an ancestor, or in the theme CSS file.  |

#### x-h-bubble-audio

Apply to an `<audio>` element with a `src` (or a `<source>` child). It renders a custom player (play/pause button, seek slider and time readout) and hides the native controls.

| Attribute            | Type   | Required | Description                                                                                                                                  |
| -------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| data-play-label      | string | false    | Accessible name for the play button. Defaults to `Play`.                                                                                     |
| data-pause-label     | string | false    | Accessible name for the button while playing. Defaults to `Pause`.                                                                           |
| data-seek-label      | string | false    | Accessible name for the seek slider. Defaults to `Seek`.                                                                                     |
| data-valuetext-label | string | false    | Template for the position announced on the seek slider. `{current}` and `{duration}` are substituted. Defaults to `{current} of {duration}`. |

## Accessibility

The bubble itself is a plain container and gets no role. Give the surrounding message list `role="log"` with `aria-live="polite"` so new messages are announced, and use `<time datetime="...">` for timestamps. The reactions element defaults to `role="group"` with an accessible name of "Reactions", which you can override with your own `aria-label` or `aria-labelledby`. Images must carry an `alt` attribute. Audio attachments render a custom player. The play/pause button and the seek slider (`role="slider"`, operable with the arrow, Home and End keys) get accessible names you can localize with `data-play-label`, `data-pause-label` and `data-seek-label`.

## Examples

### Basic conversation

```html
<div class="vbox w-full gap-2">
  <div x-h-bubble>
    <div x-h-bubble-header>John Doe <time datetime="11:46">11:46</time></div>
    <div x-h-bubble-content>That's awesome. I think our users will really appreciate the improvements.</div>
    <div x-h-bubble-footer>
      <svg x-h-icon data-icon="check" role="presentation"></svg>
      Delivered
    </div>
  </div>
  <div x-h-bubble data-align="right" data-variant="primary">
    <div x-h-bubble-header>You <time datetime="11:47">11:47</time></div>
    <div x-h-bubble-content>Thanks! The new version ships next week.</div>
  </div>
</div>
```

### Variants

```html
<div class="vbox w-full gap-2">
  <div x-h-bubble data-variant="primary">
    <div x-h-bubble-content>Primary</div>
  </div>
  <div x-h-bubble>
    <div x-h-bubble-content>Secondary (default)</div>
  </div>
  <div x-h-bubble data-variant="positive">
    <div x-h-bubble-content>Positive</div>
  </div>
  <div x-h-bubble data-variant="negative">
    <div x-h-bubble-content>Negative</div>
  </div>
  <div x-h-bubble data-variant="warning">
    <div x-h-bubble-content>Warning</div>
  </div>
  <div x-h-bubble data-variant="information">
    <div x-h-bubble-content>Information</div>
  </div>
  <div x-h-bubble data-variant="outline">
    <div x-h-bubble-content>Outline</div>
  </div>
  <div x-h-bubble data-variant="transparent">
    <div x-h-bubble-content>Transparent</div>
  </div>
</div>
```

### Custom color

```html
<div x-h-bubble data-variant="custom" style="--bg-bubble: #E9AE76;--fg-bubble: #000;">
  <div x-h-bubble-content>Custom</div>
</div>
```

### With avatar

```html
<div class="hbox w-full items-end gap-2">
  <span x-h-avatar>
    <img x-h-avatar-image src="/harmonia/logo/harmonia-square.svg" alt="@harmonia" />
  </span>
  <div x-h-bubble>
    <div x-h-bubble-header>Harmonia <time datetime="11:46">11:46</time></div>
    <div x-h-bubble-content>Welcome to the chat!</div>
  </div>
</div>
```

### Image attachment

```html
<div x-h-bubble>
  <div x-h-bubble-header>John Doe <time datetime="11:46">11:46</time></div>
  <img x-h-bubble-image class="w-3xs" src="/harmonia/photos/ignartonosbg-mountain.jpg" alt="Sheet component preview" />
  <div x-h-bubble-footer>Delivered</div>
</div>
```

### Image gallery

```html
<div x-h-bubble>
  <div x-h-bubble-header>John Doe <time datetime="11:46">11:46</time></div>
  <div x-h-bubble-gallery class="w-3xs">
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-fences.jpg" alt="Photo preview" />
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-foxtail-grass.jpg" alt="Photo preview" />
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-mountains.jpg" alt="Photo preview" />
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-orange-juice.jpg" alt="Photo preview" />
  </div>
</div>
```

### Gallery with more indicator

When a message carries more photos than the four shown, wrap the fourth tile in `x-h-bubble-gallery-more` and overlay a `<button>` with a label. The label text is yours to author, so it can be localized (for example `+3` or `3 more`). Give the button a descriptive `aria-label`, since the terse label alone does not convey the action.

```html
<div x-h-bubble>
  <div x-h-bubble-header>John Doe <time datetime="11:46">11:46</time></div>
  <div x-h-bubble-gallery class="w-3xs">
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-fences.jpg" alt="Photo preview" />
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-foxtail-grass.jpg" alt="Photo preview" />
    <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-mountains.jpg" alt="Photo preview" />
    <div x-h-bubble-gallery-more>
      <img x-h-bubble-image src="/harmonia/photos/ignartonosbg-orange-juice.jpg" alt="Photo preview" />
      <button aria-label="Show 3 more photos">+3</button>
    </div>
  </div>
</div>
```

### Audio attachment

```html
<div x-h-bubble>
  <div x-h-bubble-header>John Doe <time datetime="11:48">11:48</time></div>
  <audio x-h-bubble-audio src="/harmonia/audio/chime.wav"></audio>
</div>
```

### File attachment

```html
<div x-h-bubble>
  <div x-h-bubble-file>
    <svg x-h-icon data-icon="file" role="presentation"></svg>
    <div class="vbox min-w-0">
      <span class="truncate font-medium">Quarterly-report.pdf</span>
      <span class="text-xs opacity-75">PDF, 2.4 MB</span>
    </div>
    <button x-h-button data-size="icon-sm" data-variant="transparent" aria-label="Download">
      <svg x-h-icon data-icon="export" role="presentation"></svg>
    </button>
  </div>
  <div x-h-bubble-footer>Delivered</div>
</div>
```

### Link preview

```html
<div x-h-bubble>
  <div x-h-bubble-content>Check out the project page</div>
  <a x-h-bubble-link class="w-3xs" href="https://github.com/codbex/codbex-harmonia" target="_blank">
    <img src="/harmonia/preview.png" alt="Harmonia project preview" />
    <div class="vbox gap-1 p-2">
      <span class="font-medium">Harmonia</span>
      <span class="hbox items-center gap-1 text-xs opacity-75">
        <svg x-h-icon class="size-3" data-icon="link" role="presentation"></svg>
        codbex.com/harmonia/
      </span>
    </div>
  </a>
</div>
```

### Reactions

```html
<div class="vbox w-full gap-2">
  <div x-h-bubble>
    <div x-h-bubble-content>We just shipped version 2.4!</div>
    <div x-h-bubble-reactions>
      <button>🎉 3</button>
      <button>👍 1</button>
    </div>
  </div>
  <div x-h-bubble data-align="right" data-variant="primary">
    <div x-h-bubble-content>Congratulations team!</div>
    <div x-h-bubble-reactions aria-label="Reactions to your message">
      <button>❤️ 2</button>
    </div>
  </div>
</div>
```

Full docs: https://www.codbex.com/harmonia/components/bubble.html

## Notes

- Directive values are Alpine expressions, so quote string literals: `x-h-...="'Label'"`.
- Components render only after Alpine has registered Harmonia. See SKILL.md for setup.
