VeltCommentPin, VeltCommentBubble, and VeltCommentTool are the three anchored / Velt-positioned comment surfaces: they drop where the user clicks, attach to a target element, or sit inside Velt’s own comment host, and Velt owns their placement. This file owns the layer above Props for these three: what every prop does at runtime, its default, and how it combines with the others, plus the default click/hover/drag/index behaviors you get with no prop at all.
- Who positions them: see
Behaviors→ Positioning ownership. Pins drop where clicked, the dialog anchors to its pin and re-anchors on resize, bubbles attach to their target element: all for free as long as you stay in wireframes. Drop to primitives/headless and you take over positioning. - Anchored-vs-static classification: see
Component definitions. Pins, the pin dialog, and bubbles are anchored; the sidebar, inline comments section, and notifications panel are statically placed.
context / contextOptions / locationId / documentId / folderId scoping props with identical semantics, documented once here per component.
Per-prop behavior (exhaustive)
The exhaustive per-prop behavior for the three anchored comment surfaces is organized per component below: each## Velt… subsection carries that component’s full prop table (default + runtime behavior + interactions) and its own ### Default behaviors (no prop needed) block. Every prop in Props for these components is covered; see Provable coverage.
VeltCommentPin
The pin marks an existing comment annotation’s anchor point and opens its dialog. In React,VeltCommentPin renders one pin for a known annotation (you pass annotationId); the auto-managed pins that VeltComments drops on click are the same underlying element driven by the host. Pin placement, drag, dialog open/close, and the index/number badge are default behaviors (global feature state), not per-instance props: see Default behaviors below.
| Prop | Default | Behavior | Interactions & gotchas |
|---|---|---|---|
annotationId | (none) | The annotation this pin represents. The pin reads that annotation’s resolved anchor, comments, author, status, and unread state, and binds its dialog to it. | Required for a standalone VeltCommentPin; the host-managed pins set it internally. With no annotationId (and no other scope) the pin has no annotation to render. Changing it re-syncs the pin to the new annotation. |
multiThreadAnnotationId | (none) | Selects one thread within a multi-thread pin (several comment threads sharing one anchor). Scopes the dialog to that thread. | Only meaningful when the annotation is a multi-thread pin; ignored otherwise. Pairs with multi-thread dialog primitives. |
variant | (none → base wireframe) | Picks the named pin wireframe variant to render. Resolves the registered wireframe velt-comment-pin-wireframe---<variant>; if none is registered for that name, the pin falls back to the base wireframe. | The variant string is also the scoping key: a variant only renders content registered under that exact name. Use to render different pin looks in different contexts (see Behaviors → Variant → context scoping). |
context | (none) | Free-form key/value object attached to new comments created via this pin’s flow, and used to scope which annotation the pin resolves. | Stringified to the element attribute internally; pass an object. Combines with contextOptions (resolution rules) and the locationId/documentId/folderId scope. Does not retro-tag existing comments. |
contextOptions | (none) | Resolution options governing how context is matched when locating/creating the annotation. | Only has effect alongside context. |
locationId | (none) | Scopes the pin to a specific Velt location (sub-page / tab / canvas). | Considered only when a folderId or documentId is also in play; a bare locationId does not narrow resolution on its own. |
documentId | (none) | Scopes the pin to a specific document. The client id you pass is converted to the internal server document id. | Falsy value clears it to undefined (not null) for hash parity with the tool/bubble. |
folderId | (none) | Scopes the pin to a folder; the server folder id is derived from it. | Same undefined-on-clear parity as documentId. |
defaultCondition | (unset → pin shows) | Render gate. true forces the pin to render; false hides it. Passed through as the default-condition attribute and consumed by Velt’s conditional-rendering layer. | Tri-state: only literal true/false emit the attribute: any other value leaves it unset (default render). Use to conditionally suppress a pin without unmounting the component. |
Default behaviors (no prop needed)
- Pin placement (anchored): the pin is positioned by the host at the annotation’s resolved anchor: where the user clicked, or on the target element. The pin element itself takes no
x/y/positioninput. Default pin marker size is 40px (34px when the comment was added on a different device type and the device indicator is on). - Click opens the dialog: pointer-up on the pin toggles selection. If the pin is not selected, the click selects it and opens its anchored dialog; if already selected, it toggles closed. This is
togglePinSelection, emitted on pointer-up. (Pointer-up on the dialog itself also selects the pin if it wasn’t already, so interacting with the dialog keeps it open.) - Drag to reposition: pins are draggable by default (
pinDragdefaultstruein global feature state). Dragging sets anannotationDraggingstate and, on drop, re-anchors the annotation. There is no per-pin prop to disable drag onVeltCommentPin; it is governed by the global comment feature config. - Hover / bubble-on-pin:
bubbleOnPinandbubbleOnPinHover(global feature state, both defaultfalse) control whether a preview bubble shows on the pin permanently or on hover. Off by default: the bare pin shows the avatar marker only. - Index / number badge:
showCommentIndex(global, defaultfalse) toggles a numeric index badge on pins (thecomment-pin-number/comment-pin-indexprimitives). Off by default, so pins show the author avatar, not a number. Unread, private, ghost, and agent-comment states each swap in their own marker primitive (agent comments render a distinct blue lego-bot marker instead of an avatar). - Shadow DOM: the pin’s own shadow-DOM isolation comes from global
pinShadowDOM(defaultfalse), not from a per-pin prop:VeltCommentPinexposes noshadowDomprop. (Confirmed: the pin marker defaultspinShadowDOM: false, while the pin dialog defaultsdialogShadowDom: true: the two surfaces isolate independently; seeBehaviors.)
VeltCommentBubble
The bubble is a chip that attaches to a target element (or an annotation) and, on click, opens that annotation’s comment dialog. It is the “comment count on a row/card” surface. Unlike the pin, the bubble fetches its own annotation from the scope you give it (targetElementId / annotationId / context+scope).
| Prop | Default | Behavior | Interactions & gotchas |
|---|---|---|---|
targetElementId | (none) | The element id the bubble attaches to; the bubble resolves the annotation(s) bound to that element and anchors to it. | Triggers an annotation fetch on set/change. Primary way to place a bubble on a list row or card. |
targetCommentElementId | (none) | Deprecated alias for targetElementId. Setting it writes through to targetElementId. | Use targetElementId. Kept only for back-compat; identical behavior. |
annotationId | (none) | Binds the bubble to one specific annotation instead of resolving by element/scope. | Triggers a fetch on change. Takes precedence as a direct bind; combine with scope props only when you have no fixed id. |
avatar | true (shows) | Shows the commenter avatar on the bubble. false hides it. | Writes to the same showAvatar UI state as the deprecated showAvatar prop: last setter wins. |
showAvatar | true (shows) | Deprecated alias for avatar. | Use avatar. Both target one flag; passing conflicting values means the later-applied one wins. |
commentCountType | 'total' | Which count the bubble renders: 'total' = all comments on the annotation; 'unread' = unread count only. | Enum-backed (CommentCountType.TOTAL/UNREAD). The count primitive branches on this value; with 'unread' and nothing unread, the count reflects zero/none. |
commentBubbleTargetPinHover | false | When true, hovering the bubble highlights its target pin/element (adds a hover class on the linked target). | Off by default: hovering does nothing visible. Use to cross-highlight a bubble and its anchor in dense layouts. |
openDialog | true | Whether clicking the bubble opens the comment dialog. true → click toggles the anchored dialog open/closed. false → click does not open a dialog; instead it only emits the commentBubbleClick event. | The escape hatch for “bubble as a pure click target”: set openDialog={false} and listen for the click event to drive your own UI. With true, click runs the full select/toggle flow (resets other selections, scrolls to drafts, toggles this annotation). |
readOnly | false | Renders the bubble’s dialog view-only (no composing/editing). | OR-combined with the global readOnly feature state: if either is on, the bubble is read-only. |
variant | (none → base wireframe) | Picks the named bubble wireframe variant (velt-comment-bubble-wireframe---<variant>), else base. | Same variant-as-scoping-key rule as the pin. |
darkMode | false | Forces the dark theme on the bubble; sets a dark attribute on the host. | Passing null/undefined leaves the theme unmanaged (inherits ambient). String "false" is treated as off. |
shadowDom | true | Style-isolates the bubble in shadow DOM. false exposes it to selector CSS. | The component tracks whether true was explicitly passed (vs defaulting): relevant when a complete consumer wireframe is provided, which otherwise auto-wraps. Set false to style with plain CSS classes. |
context | (none) | Key/value object attached to new comments created through the bubble and used to scope annotation resolution. | Stringified internally; pass an object. Re-triggers the annotation fetch on change. Does not retro-tag existing comments. |
contextOptions | (none) | Resolution options for how context is matched. | Effective only with context. |
locationId | (none) | Scopes the bubble to a Velt location; re-triggers fetch. | Behaves with folderId/documentId as the resolution narrows. |
documentId | (none) | Scopes to a document (client id → server document id); re-triggers fetch. | Falsy clears to undefined. |
folderId | (none) | Scopes to a folder (server folder id derived); re-triggers fetch. | Falsy clears to undefined. |
Default behaviors (no prop needed)
- Anchored placement: the bubble attaches to its
targetElementIdand is positioned by Velt relative to that element; you don’t pass coordinates. - Click → dialog (default on): with default
openDialog={true}, clicking the bubble selects the annotation and opens its dialog, resetting any other selected annotation first and scrolling drafts into view. Clicking again toggles it closed. - Click event always fires: the
commentBubbleClickevent is emitted on every click regardless ofopenDialog, so evenopenDialog={false}bubbles are usable as click targets. - Count + avatar shown: by default the bubble shows the author avatar and the total comment count, plus an unread indicator when there are unread comments.
- Shadow-DOM wrapped: default
shadowDom={true}isolates styles; if you register a full bubble wireframe and don’t pass shadow DOM, the root still resolves its own wireframe so it doesn’t auto-wrap unexpectedly.
VeltCommentTool
The tool is the button that starts a new comment. Clicking it either enters “click anywhere to place a pin” add-comment mode, attaches a comment directly to a target element (popover mode), or opens the sidebar’s page-mode composer: depending on configuration. It carries no avatar/count; its job is to begin the flow.| Prop | Default | Behavior | Interactions & gotchas |
|---|---|---|---|
targetElementId | (none) | When set, clicking the tool attaches the new comment directly to that element (in popover mode) instead of entering free placement mode. | Triggers an annotation-availability check. If the target already has a (visible) comment annotation, the tool hides itself (see default behaviors). |
targetCommentElementId | (none) | Deprecated alias for targetElementId. Writes through to it. | Use targetElementId. |
onCommentModeChange | (no handler) | Callback fired whenever add-comment mode toggles on/off; receives the boolean mode. | Fires on every transition (also when mode is toggled elsewhere, since it’s driven by the shared add-comment-mode stream). Use to sync your own UI (e.g. button pressed state). |
sourceId | (none) | Identifier stamped on the comment when the tool attaches it to a target element. | Only passed through in the direct-attach (popover + target) path. |
darkMode | false | Forces the dark theme on the tool. | String "false" treated as off. |
disabled | false | Disables the tool. When true, clicking the tool does nothing (add-comment is blocked). | Combined with the global comment-tool-enabled state via AND: the tool is active only when not disabled and the global tool is enabled. Independent of IAM permission checks (below), which can also block the action. |
variant | (none → base wireframe) | Picks the named tool wireframe variant (velt-comment-tool-wireframe---<variant>), else base. | Same variant-as-scoping-key rule as pin/bubble. |
shadowDom | true | Style-isolates the tool in shadow DOM. false exposes it to selector CSS. | Tracks explicit-true like the bubble, so providing a full custom button slot doesn’t get force-wrapped. |
context | (none) | Key/value object attached to the new comment the tool creates; in free placement mode it is set as the free-style comment context, in direct-attach mode it is passed to the attach call. | Stringified internally; pass an object. Re-checks annotation availability on change. |
contextOptions | (none) | Resolution options for context. | When contextInPageModeComposer is on, both context and contextOptions are forced to undefined for the availability subscription (the page-mode composer owns its own context). |
locationId | (none) | Scopes the new comment to a Velt location; used to resolve/attach. | Considered for availability only when a folderId or documentId is also set; supplied as a manual location when attaching directly. |
documentId | (none) | Scopes to a document (client id → server document id). | Falsy clears to undefined (hash parity with bubble). Becomes attach metadata in direct-attach mode. |
folderId | (none) | Scopes to a folder (server folder id derived). | Falsy clears to undefined. Becomes attach metadata in direct-attach mode. |
contextInPageModeComposer | false | When true, clicking the tool routes the context + targetElementId into the sidebar page-mode composer and opens the sidebar, instead of placing a pin or attaching inline. | Can also be turned on globally via API: the click honors either the prop or the global flag. While on, it suppresses context-scoping of the tool’s own availability subscription (see contextOptions). Use to make a tool feed a sidebar composer rather than drop a pin. |
Default behaviors (no prop needed)
- Default button when no custom slot: if you don’t fill the tool’s button slot, Velt renders its default comment-tool button. Provide your own element in the slot to replace it (the tool detects an empty slot and shows the default only then).
- Click → add-comment mode (default): with no
targetElementId/popover config, clicking the tool enables add-comment mode: the next click on the page drops a pin. Clicking the tool again while in add-comment mode disables it (toggle). The host adds acomment-tool-activeclass while mode is on. - Direct attach in popover mode: when popover mode is on and a
targetElementId/context/scope is provided, the click attaches the comment straight to that element (carryingsourceIdand folder/document/location metadata) rather than entering placement mode. - Auto-hide when the target already has a comment: in popover mode, the tool subscribes to annotation availability for its target; if a (visible, unresolved unless resolved-on-DOM is on) comment already exists there,
commentAnnotationAvailablebecomes true and the tool hides, so you don’t stack a second tool over an existing comment. - Permission & auth gates: the click is a no-op (with a console note) for anonymous/read-only users, when the SDK isn’t authenticated, when the plan is expired, or when the user lacks an ADMIN/COMMENTER role. These gates are in addition to the
disabledprop and the global tool-enabled flag. - Analytics: every tool click emits a
COMMENT_TOOL_CLICKEDanalytics event taggedcustomordefaultdepending on whether a custom button slot was supplied.
Default behaviors (no prop needed)
Each component’s zero-prop baseline is documented in its### Default behaviors (no prop needed) block above. In brief: a pin is placed by the host at the annotation’s resolved anchor (no x/y input), opens its dialog on click, is draggable by default (pinDrag global, true), shows the author avatar (no index/bubble unless the global flags are on), and isolates via global pinShadowDOM (default false); a bubble anchors to its targetElementId, opens the dialog on click (default openDialog={true}) while always emitting commentBubbleClick, and shows the avatar + total count, shadow-DOM wrapped; a tool renders Velt’s default button (unless you fill its slot), enters add-comment mode on click (or direct-attaches in popover mode), auto-hides when its target already has a comment, and is gated by auth/permission checks plus the global tool-enabled flag.
Prop-interaction matrix
No separate multi-prop matrix for this group: pairwise effects live in each prop’s Interactions & gotchas column above. The recurring cross-prop notes: the scoping set (context + contextOptions + locationId + documentId + folderId) resolves together, and locationId only narrows when a folderId/documentId is also present; the deprecated aliases (showAvatar→avatar, targetCommentElementId→targetElementId) write the same flag as their replacement (last setter wins); disabled on the tool AND-combines with the global tool-enabled state; and contextInPageModeComposer forces the tool’s own context/contextOptions to undefined for its availability subscription. Pin/bubble/dialog shadow-DOM isolation is covered in the behaviors index.
Positioning & composition
All three surfaces are anchored / Velt-positioned: the pin drops where the user clicked (or on its target element), the bubble attaches to itstargetElementId, and the tool/its resulting pin are placed by Velt’s comment host: none take a coordinate input. You keep this anchoring for free while you stay in wireframes; dropping to primitives or headless means you take over positioning. See Behaviors → Positioning ownership and the anchored-vs-static classification in Component definitions.
Provable coverage
Counted againstProps ### VeltCommentBubble / ### VeltCommentPin / ### VeltCommentTool:
- VeltCommentBubble: 18/18: targetCommentElementId, targetElementId, showAvatar, avatar, commentBubbleTargetPinHover, shadowDom, variant, darkMode, readOnly, commentCountType, context, contextOptions, locationId, documentId, folderId, annotationId, openDialog: plus the two deprecated aliases counted inline (showAvatar→avatar, targetCommentElementId→targetElementId). Every row in the props.md table is covered.
- VeltCommentPin: 9/9: annotationId, multiThreadAnnotationId, variant, context, contextOptions, locationId, documentId, folderId, defaultCondition. (props.md lists 9 rows; “~10” estimate included.)
- VeltCommentTool: 14/14: targetCommentElementId, targetElementId, onCommentModeChange, sourceId, darkMode, disabled, variant, shadowDom, context, contextOptions, locationId, documentId, folderId, contextInPageModeComposer. Every row covered.
shadowDom flag for VeltCommentPin (the pin exposes no shadowDom prop; isolation is global pinShadowDOM, default false).
