> ## Documentation Index
> Fetch the complete documentation index at: https://velt.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Comment Bubble Wireframe Variables

> Template variables exposed by the Comment Bubble wireframe — read them with velt-data, velt-if, and velt-class to drive dynamic content, conditional rendering, and class toggling.

<Note>
  New to wireframes? Start with [UI Customization Concepts](/docs/ui-customization/overview) and the [Template Variables](/docs/ui-customization/template-variables) overview.
</Note>

## Overview

The **Comment Bubble** wireframe exposes the variables below. Use them inside any `<velt-comment-bubble-...-wireframe>` tag via three forms:

| You want to… | Use | Example |
| - | - | - |
| Display a value as text | `<velt-data field="var" />` | `<velt-data field="annotation.from.name" />` |
| Hide / show conditionally | `velt-if="{var}"` | `velt-if="{annotation.unread}"` |
| Toggle a CSS class | `velt-class="'cls': {var}"` | `velt-class="'is-unread': {annotation.unread}"` |

All variables are mapped — reference them by their short name. You do not need the `componentConfig.` prefix.

<Note>
  **Naming conflicts — use full path.** A few names collide with mappings used elsewhere. Inside a Comment Bubble wireframe, prefer the explicit path on the right when reading these values:

  | Conflicting name | Use this in Comment Bubble |
  | - | - |
  | `customStatusesShown` | `globalConfigSignal.featureState.customStatusesShown` |
  | `resolvedCommentsOnDom` | `globalConfigSignal.featureState.resolvedCommentsOnDom` |
  | `readOnly` | `globalConfigSignal.featureState.readOnly` (workspace) or `{readOnly}` (per-render local) |
</Note>

## App State

App-wide values resolved from the shared global signal.

| Variable | Type | Description | Example |
| - | - | - | - |
| `globalConfigSignal.appState.user` | [`User`](/docs/api-reference/sdk/models/data-models#user) \| `null` | Currently identified end-user. | `<velt-data field="globalConfigSignal.appState.user.name" />` |

## Data State

Per-bubble data: the annotation this bubble previews, the surrounding annotation list, and unread / unresolved counts.

| Variable | Type | Description | Example |
| - | - | - | - |
| `annotation` | [`CommentAnnotation`](/docs/api-reference/sdk/models/data-models#commentannotation) \| `null` | Annotation this bubble represents. | `velt-if="{annotation}"` |
| `annotation.from` | [`User`](/docs/api-reference/sdk/models/data-models#user) | Author of the annotation's first comment. | `<velt-data field="annotation.from.name" />` |
| `annotation.comments` | [`Comment`](/docs/api-reference/sdk/models/data-models#comment)`[]` | Comments in the thread. | `<velt-data field="annotation.comments.length" />` |
| `annotation.status.id` | `string` | Current status id (e.g. `"open"`, `"resolved"`). | `velt-class="'status-{annotation.status.id}': true"` |
| `annotation.unread` | `boolean` | Annotation has unread comments for the current user. | `velt-class="'is-unread': {annotation.unread}"` |
| `annotation.iam.accessMode` | `'public'` \| `'private'` | Legacy visibility mode. A value of `'private'` here indicates the annotation is in legacy private mode (`iam.accessMode = 'private'`); as of v5.0.2-beta.27 this takes unconditional precedence over [`visibilityConfig.type`](/docs/api-reference/sdk/models/data-models#commentannotationvisibilityconfig) for all privacy-gating surfaces. For V2 visibility-model annotations (`visibilityConfig.type === 'restricted' \| 'organizationPrivate'`), check `{isPrivateComment}` instead — it routes through the SDK's shared privacy check. | `velt-if="{annotation.iam.accessMode} === 'private'"` |
| `annotations` | [`CommentAnnotation`](/docs/api-reference/sdk/models/data-models#commentannotation)`[]` | All annotations currently in scope. | `<velt-data field="annotations.length" />` |
| `unresolvedAnnotationsCount` | `number` | Number of unresolved annotations across the document. | `<velt-data field="unresolvedAnnotationsCount" />` |
| `unreadCount` | `number` | Unread-comment count for this bubble's annotation. | `<velt-data field="unreadCount" />` |
| `data.folderId` | `string` | Folder id this bubble's annotation belongs to. | `<velt-data field="data.folderId" />` |
| `data.context` | `Record<string, any>` | Free-form annotation context. | `<velt-data field="data.context.key" />` |

## UI State

Per-bubble UI flags driven by the bubble itself.

| Variable | Type | Description | Example |
| - | - | - | - |
| `uiState.commentPinSelected` | `boolean` | Pin associated with this bubble is currently selected. | `velt-class="'pin-selected': {uiState.commentPinSelected}"` |
| `selectedAnnotationsMap` | `SelectedAnnotationsMap` | Map keyed by `annotationId` → `boolean`; `true` if that annotation is selected. | `velt-class="'selected': {selectedAnnotationsMap[annotation.annotationId]}"` |
| `selectedAnnotationsLocationMap` | `SelectedAnnotationsLocationMap` | Internal selection bookkeeping by location. | *Internal — read individual entries via bracket notation if needed.* |
| `darkMode` | `boolean` | Dark mode is active for this bubble. | `velt-class="'dark': {darkMode}"` |
| `variant` | `string` | Per-instance variant tag set on the host element. | `<velt-data field="variant" />` |
| `parentLocalUIState.shadowDom` | `boolean` | Shadow-DOM rendering is enabled for this instance. | *Host config — set via element attribute.* |
| `commentBubbleTargetPinHover` | `boolean` | The bubble's anchor pin is currently hovered. | `velt-class="'hover': {commentBubbleTargetPinHover}"` |
| `isPrivateComment` | `boolean` | `true` when the annotation is private. Reflects both legacy `iam.accessMode === 'private'` and the V2 `visibilityConfig` model (`visibilityConfig.type === 'restricted' \| 'organizationPrivate'`) — routes through the SDK's shared privacy check (`isAnnotationPrivate`). | `velt-if="{isPrivateComment}"` |
| `openDialog` | `boolean` | A comment dialog is open for this bubble's annotation. | `velt-if="{openDialog}"` |
| `readOnly` | `boolean` | Per-render read-only flag. | `velt-class="'readonly': {readOnly}"` |
| `showAvatar` | `boolean` | Avatar should be rendered. | `velt-if="{showAvatar}"` |
| `commentCountType` | `'total'` \| `'unread'` | Which count drives the badge. | `velt-class="'count-{commentCountType}': true"` |

## Feature State

Capability flags toggled at the workspace level. These are documented under the explicit `globalConfigSignal.featureState.<name>` path because the bare names collide with other mappings. Available in both V1 and V2 Comment Bubble wireframes.

<Note>
  **V2 root alias.** V2 wireframes can use either `globalConfig` or `globalConfigSignal` as the root for these deep paths — both resolve identically (v5.0.2-beta.25).
</Note>

| Variable | Type | Description | Example |
| - | - | - | - |
| `globalConfigSignal.featureState.customStatusesShown` | `boolean` | Custom-status decoration enabled on bubbles. | `velt-class="'show-status': {globalConfigSignal.featureState.customStatusesShown}"` |
| `globalConfigSignal.featureState.groupMatchedComments` | `boolean` | Matched comments are grouped together on the page. | `velt-class="'grouped': {globalConfigSignal.featureState.groupMatchedComments}"` |
| `globalConfigSignal.featureState.resolvedCommentsOnDom` | `boolean` | Resolved annotations still render bubbles. | `velt-if="{globalConfigSignal.featureState.resolvedCommentsOnDom}"` |
| `globalConfigSignal.featureState.readOnly` | `boolean` | Workspace read-only mode is active. | `velt-class="'readonly': {globalConfigSignal.featureState.readOnly}"` |
| `globalConfigSignal.featureState.sidebarVisible` | `boolean` | The comment sidebar is currently open. Also readable via `globalConfig.featureState.sidebarVisible` in V2 wireframes. | `velt-class="'sidebar-open': {globalConfigSignal.featureState.sidebarVisible}"` |

## Common Props

Every Comment Bubble primitive accepts:

| React Prop | HTML Attribute | Type | Default | Description |
| - | - | - | - | - |
| `defaultCondition` | `default-condition` | `boolean \| "true" \| "false"` | `true` | When `false`, the component always renders regardless of internal state. |

Signal inputs (for parent-child component composition):

* `[componentConfigSignal]` — shared config signal (annotation, selected-annotations map, unread count).
* `[parentLocalUIState]` — per-instance UI state signal (darkMode, variant, shadowDom, readOnly, showAvatar, commentCountType).

The root `<velt-comment-bubble>` element additionally accepts attributes that map onto config and local UI state values: `dark-mode`, `variant`, `show-avatar`, `comment-count-type`, etc.

## Type Reference

Types referenced by the variables above are documented in [Data Models](/docs/api-reference/sdk/models/data-models):

| Type | Description |
| - | - |
| [`CommentAnnotation`](/docs/api-reference/sdk/models/data-models#commentannotation) | The annotation thread (id, status, comments, from, unread, iam, etc.). |
| [`Comment`](/docs/api-reference/sdk/models/data-models#comment) | A single message inside an annotation thread. |
| [`User`](/docs/api-reference/sdk/models/data-models#user) | Identified end-user object. |
| [`CommentVisibilityOptionType`](/docs/api-reference/sdk/models/data-models#commentvisibilityoptiontype) | Annotation visibility mode (`'public'`, `'private'`, `'organization'`, `'selected_people'`). |

## Subcomponents

Each subcomponent below has its own wireframe tag. The annotation root supports nested access — see [`CommentAnnotation`](/docs/api-reference/sdk/models/data-models#commentannotation) for the full shape.

### `comment-bubble` (root)

The bubble pin that previews an annotation when the user is not focused on it.

* **Public element:** `<velt-comment-bubble>`
* **Wireframe tag:** `<velt-comment-bubble-wireframe>`
* **Children:** `*-avatar`, `*-comments-count`, `*-unread-icon`

| Property | Value |
| - | - |
| Extra variables | None — root only sees common variables. |
| `shouldShow` | One bubble renders per non-resolved annotation on the current document. Resolved bubbles render only when `globalConfigSignal.featureState.resolvedCommentsOnDom === true`. |

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    <VeltCommentBubbleWireframe
      veltClass="'unread': {annotation.unread}, 'selected': {selectedAnnotationsMap[annotation.annotationId]}">
      <div className="my-bubble">
        <VeltCommentBubbleWireframe.Avatar />
        <VeltCommentBubbleWireframe.CommentsCount />
        <VeltCommentBubbleWireframe.UnreadIcon />
      </div>
    </VeltCommentBubbleWireframe>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```html theme={null}
    <velt-comment-bubble-wireframe
      velt-class="'unread': {annotation.unread}, 'selected': {selectedAnnotationsMap[annotation.annotationId]}">
      <div class="my-bubble">
        <velt-comment-bubble-avatar-wireframe></velt-comment-bubble-avatar-wireframe>
        <velt-comment-bubble-comments-count-wireframe></velt-comment-bubble-comments-count-wireframe>
        <velt-comment-bubble-unread-icon-wireframe></velt-comment-bubble-unread-icon-wireframe>
      </div>
    </velt-comment-bubble-wireframe>
    ```
  </Tab>
</Tabs>

***

### `comment-bubble-avatar`

The avatar of the annotation's author.

* **Public element:** `<velt-comment-bubble-avatar>`
* **Wireframe tag:** `<velt-comment-bubble-avatar-wireframe>`

| Property | Value |
| - | - |
| Extra variables | None beyond common variables. Read `annotation.from.photoUrl` / `annotation.from.name` from the parent annotation. |

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    <VeltCommentBubbleWireframe.Avatar>
      <img className="my-avatar" src="{annotation.from.photoUrl}" />
    </VeltCommentBubbleWireframe.Avatar>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```html theme={null}
    <velt-comment-bubble-avatar-wireframe>
      <img class="my-avatar" [src]="annotation.from.photoUrl" />
    </velt-comment-bubble-avatar-wireframe>
    ```
  </Tab>
</Tabs>

***

### `comment-bubble-comments-count`

The "N" badge showing how many comments are in the thread.

* **Public element:** `<velt-comment-bubble-comments-count>`
* **Wireframe tag:** `<velt-comment-bubble-comments-count-wireframe>`

| Property | Value |
| - | - |
| Extra variables | None beyond common variables. |
| `shouldShow` | Renders only when the thread has more than one comment. |

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    <VeltCommentBubbleWireframe.CommentsCount>
      <VeltIf condition="{annotation.comments.length} > 1">
        <span>
          <VeltData field="annotation.comments.length" />
        </span>
      </VeltIf>
    </VeltCommentBubbleWireframe.CommentsCount>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```html theme={null}
    <velt-comment-bubble-comments-count-wireframe>
      <span velt-if="{annotation.comments.length} > 1">
        <velt-data field="annotation.comments.length"></velt-data>
      </span>
    </velt-comment-bubble-comments-count-wireframe>
    ```
  </Tab>
</Tabs>

***

### `comment-bubble-unread-icon`

The unread-indicator dot on the bubble.

* **Public element:** `<velt-comment-bubble-unread-icon>`
* **Wireframe tag:** `<velt-comment-bubble-unread-icon-wireframe>`

| Property | Value |
| - | - |
| Extra variables | None beyond common variables. |
| `shouldShow` | `unreadCount > 0` (or `annotation.unread === true`, depending on `commentCountType`). |

## Deeply-Nested Wireframe Tags

A separate-but-related set of primitives renders the **comment pin** (the small marker on the page) — distinct from the bubble (which previews the comment). Each tag below has its own `<velt-comment-pin-...-wireframe>` registration and reads from the same `annotation` context.

### Comment Pin tags

| Tag | Notes | Example |
| - | - | - |
| `<velt-comment-pin-wireframe>` | Root pin element. | `velt-class="'pin-status-{annotation.status.id}': true"` |
| `<velt-comment-pin-triangle-wireframe>` | The pointing-arrow triangle below the pin. | *Visual only — no data binding.* |
| `<velt-comment-pin-index-wireframe>` | Pin index number (e.g. "3" — order it was placed). | `<velt-data field="annotation.annotationIndex" />` |
| `<velt-comment-pin-number-wireframe>` | Auto-generated annotation number. | `<velt-data field="annotation.annotationNumber" />` |
| `<velt-comment-pin-unread-comment-indicator-wireframe>` | Unread dot indicator on the pin. | `velt-if="{annotation.unread}"` |
| `<velt-comment-pin-private-comment-indicator-wireframe>` | Private-mode lock icon on the pin. For annotations using `visibilityConfig` (`restricted` or `organizationPrivate`), use `velt-if="{isPrivateComment}"` instead of the legacy `iam.accessMode` check. | `velt-if="{isPrivateComment}"` |
| `<velt-comment-pin-ghost-comment-indicator-wireframe>` | Indicator shown when the pin has lost its DOM target (ghost-comment state). | `velt-if="{annotation.ghostComment}"` |

## Related

* [Comment Bubble Wireframes](./wireframes) — composition reference for the wireframe tags themselves.
* [Comment Bubble Primitives](./primitives) — granular components if you don't need a full wireframe.
* [Template Variables](/docs/ui-customization/template-variables) — overview of the `velt-data` / `velt-if` / `velt-class` system.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.