Compact anchored comment marker that opens an inline threaded discussion surface.
Install @wyrd-company/react or the Flutter package and use it directly —
your DESIGN.md does the styling.
catalog/contracts/comment-anchor.ymlsource of truth · verbatim
$schema: https://thewyrdingway.dev/schemas/component-contract.schema.ymlcontractVersion: 0.1.0identity:name: CommentAnchortag: wyrd-comment-anchorpackage: wyrd.component.comment-anchortitle: CommentAnchorsummary: Compact anchored comment marker that opens an inline threaded discussion surface.relationships:related-to: - quality-parity-and-gatesclassification:category: collaborationcomposition: overlayinteractive: truetarget-strategy:react: requiredflutter: requiredsurface:properties: - name: thread-idtype:kind: stringrequired: trueserializable: truesummary: Opaque host-supplied thread identifier represented by the marker. - name: labeltype:kind: stringrequired: trueserializable: truesummary: Accessible marker label supplied by the host for the anchor target. - name: comment-counttype:kind: integerminimum: 0required: falsedefault: 0serializable: truesummary: Number of comments represented by the marker. - name: statustype:kind: enumvalues: - open - resolvedrequired: falsedefault: openreflects: trueserializable: truesummary: Host-supplied thread status. - name: is-opentype:kind: booleanrequired: falsecontrolled: truereflects: trueserializable: truesummary: Controlled expansion state. - name: default-opentype:kind: booleanrequired: falsedefault: falseserializable: truesummary: Initial uncontrolled expansion state. - name: placementtype:kind: enumvalues: - inline - block - marginrequired: falsedefault: marginreflects: trueserializable: truesummary: Preferred visual placement for the expanded thread surface. - name: commentstype:kind: arrayitem:kind: objectfields:id:kind: stringauthor-id:kind: stringauthor-name:kind: stringtimestamp:kind: stringtimestamp-label:kind: stringbody-format:kind: enumvalues: - plain-textbody:kind: stringdecision:kind: enumvalues: - none - approve - request-changes - comment - suggestionedited:kind: booleancan-edit:kind: booleancan-delete:kind: booleanrequired: falsedefault: []serializable: truesummary: Flat ordered comments for the expanded thread. - name: reply-valuetype:kind: stringrequired: falsecontrolled: trueserializable: truesummary: Controlled reply composer value for the expanded thread. - name: default-reply-valuetype:kind: stringrequired: falsedefault: ""serializable: truesummary: Initial uncontrolled reply composer value. - name: can-replytype:kind: booleanrequired: falsedefault: trueserializable: truesummary: Shows the reply composer in the expanded thread when true. - name: can-resolvetype:kind: booleanrequired: falsedefault: trueserializable: truesummary: Shows resolve/reopen affordance in the expanded thread when true.slots: []events: - name: open-changepayload:thread-id:kind: stringis-open:kind: booleanreason:kind: enumvalues: - marker - close-button - escape - programmaticbubbles: truecomposed: truecancelable: falsesummary: Emitted when marker expansion changes. - name: reply-changepayload:thread-id:kind: stringvalue:kind: stringbubbles: truecomposed: truecancelable: falsesummary: Re-emitted from the expanded thread reply composer. - name: reply-submitpayload:thread-id:kind: stringbody:kind: stringbubbles: truecomposed: truecancelable: truesummary: Re-emitted when the expanded thread submits a reply. - name: resolvepayload:thread-id:kind: stringbubbles: truecomposed: truecancelable: truesummary: Re-emitted when the expanded thread requests resolving. - name: reopenpayload:thread-id:kind: stringbubbles: truecomposed: truecancelable: truesummary: Re-emitted when the expanded thread requests reopening. - name: comment-editpayload:thread-id:kind: stringcomment-id:kind: stringbubbles: truecomposed: truecancelable: truesummary: Re-emitted when a child comment edit affordance is activated. - name: comment-deletepayload:thread-id:kind: stringcomment-id:kind: stringbubbles: truecomposed: truecancelable: truesummary: Re-emitted when a child comment delete affordance is activated.states: - name: closedsource: propertysummary: Marker is collapsed. - name: opensource: propertysummary: Expanded thread is visible. - name: resolvedsource: propertysummary: Marker represents a resolved thread.parts: - name: rootsummary: Anchor root. - name: markersummary: Compact marker button. - name: countsummary: Comment count text. - name: surfacesummary: Expanded thread surface. - name: closesummary: Expanded surface close button.tokens:marker-background: color.primarymarker-foreground: color.on-primaryresolved-background: color.surface-containerresolved-foreground: color.on-surface-variantsurface-background: color.surfacesurface-foreground: color.on-surfaceborder-color: color.outlineradius: rounded.mdshadow: shadow.mdpadding-x: spacing.smpadding-y: spacing.xsgap: spacing.smmessages: - key: comment-anchor.closedescription: Accessible label for closing the expanded anchor thread. - key: comment-anchor.count-labeldescription: Text label shown next to the marker comment count. - key: comment-anchor.marker-labeldescription: Accessible label for the marker button including host label and comment count.params:label: stringcount: numberobligations:accessibility:role: buttonname: Label property and comment count are combined through the localized marker label.keyboard: - Tab moves focus to the marker, close button, and expanded thread controls. - Enter or Space toggles the marker when focused. - Escape closes the expanded thread and restores focus to the marker.semantics: - The marker is a button with aria-expanded reflecting open state. - The expanded thread is a non-modal inline/anchored surface, not a focus trap. - The close control is a button named by component-owned localized text. - The expanded thread preserves CommentThread native list semantics.behavior:model: overlayinteractions: - is-open ?? default-open determines effective expansion state. - Activating the marker toggles expansion and emits open-change. - Activating close or pressing Escape closes the thread and emits open-change. - Expanded thread interactions are re-emitted with thread-id.hostEffects: - Dispatch open-change event. - Dispatch reply, resolve/reopen, and child comment edit/delete events. - Hosts own anchor positioning context, persistence, identity, permissions, and storage.conditions: - rtl - reduced-motion - cvd-deuteranopia - density-compact - text-expansionsdui:status: serializableserializableProperties: - thread-id - label - comment-count - status - is-open - default-open - placement - comments - reply-value - default-reply-value - can-reply - can-resolveserializableSlots: []eventBindings: - open-change - reply-change - reply-submit - resolve - reopen - comment-edit - comment-deletenotes: CommentAnchor serializes marker state, flat comments, and interaction bindings for embeddable host surfaces. Host-owned persistence and anchor storage are outside the component.examples: - name: closedstate: closedproperties:thread-id: thread-route-delaylabel: Comments on delay copycomment-count: 1status: opendefault-open: falsecomments: - id: comment-route-delay-copyauthor-id: author-rinauthor-name: Rintimestamp: "2026-07-04T16:15:00.000Z"body-format: plain-textbody: Delay copy should identify the affected service window before dispatch signs off.decision: request-changesedited: falsecan-edit: falsecan-delete: falseslots: {} - name: openstate: openproperties:thread-id: thread-coordinator-locklabel: Comments on coordinator fieldcomment-count: 2status: resolveddefault-open: trueplacement: margincomments: - id: comment-coordinator-lockauthor-id: author-maraauthor-name: Maratimestamp: "2026-07-04T16:20:00.000Z"body-format: plain-textbody: Locked coordinator field matches this scenario.decision: approveedited: truecan-edit: truecan-delete: falseslots: {}overrides:react:properties:thread-id: threadIdcomment-count: commentCountis-open: isOpendefault-open: defaultOpenreply-value: replyValuedefault-reply-value: defaultReplyValuecan-reply: canReplycan-resolve: canResolveevents:open-change: onOpenChangereply-change: onReplyChangereply-submit: onReplySubmitcomment-edit: onCommentEditcomment-delete: onCommentDeleteflutter:properties:thread-id: threadIdcomment-count: commentCountis-open: isOpendefault-open: defaultOpenreply-value: replyValuedefault-reply-value: defaultReplyValuecan-reply: canReplycan-resolve: canResolveevents:open-change: onOpenChangereply-change: onReplyChangereply-submit: onReplySubmitcomment-edit: onCommentEditcomment-delete: onCommentDelete
01
Examples
Generated from the contract's examples block.
Previews are the published @wyrd-company/react component, themed by the
brand's role tokens — flip the site's light/dark toggle and they follow.
closedstate: closed
Usage
import { WyrdCommentAnchor } from"@wyrd-company/react";
<WyrdCommentAnchor threadId="thread-route-delay" label="Comments on delay copy" commentCount={1} status="open" defaultOpen={false} comments={[{"id":"comment-route-delay-copy","author-id":"author-rin","author-name":"Rin","timestamp":"2026-07-04T16:15:00.000Z","body-format":"plain-text","body":"Delay copy should identify the affected service window before dispatch signs off.","decision":"request-changes","edited":false,"can-edit":false,"can-delete":false}]} />
// events: onOpenChange, onReplyChange, onReplySubmit, onResolve, onReopen, onCommentEdit, onCommentDelete
import'package:wyrd_flutter/wyrd_flutter.dart';
WyrdCommentAnchor(
threadId: 'thread-route-delay',
label: 'Comments on delay copy',
commentCount: 1,
status: 'open',
defaultOpen: false,
comments: [{"id":"comment-route-delay-copy","author-id":"author-rin","author-name":"Rin","timestamp":"2026-07-04T16:15:00.000Z","body-format":"plain-text","body":"Delay copy should identify the affected service window before dispatch signs off.","decision":"request-changes","edited":false,"can-edit":false,"can-delete":false}],
)
// events: onOpenChange, onReplyChange, onReplySubmit, onResolve, onReopen, onCommentEdit, onCommentDelete
Opaque host-supplied thread identifier represented by the marker.
label
string
undefined
Accessible marker label supplied by the host for the anchor target.
comment-count
integer
0
Number of comments represented by the marker.
status
open | resolved
open
Host-supplied thread status.
is-open
boolean
undefined
Controlled expansion state.
default-open
boolean
false
Initial uncontrolled expansion state.
placement
inline | block | margin
margin
Preferred visual placement for the expanded thread surface.
comments
array
Flat ordered comments for the expanded thread.
reply-value
string
undefined
Controlled reply composer value for the expanded thread.
default-reply-value
string
Initial uncontrolled reply composer value.
can-reply
boolean
true
Shows the reply composer in the expanded thread when true.
can-resolve
boolean
true
Shows resolve/reopen affordance in the expanded thread when true.
03
Slots & events
Event
Payload
Summary
open-change
thread-id, is-open, reason
Emitted when marker expansion changes.
reply-change
thread-id, value
Re-emitted from the expanded thread reply composer.
reply-submit
thread-id, body
Re-emitted when the expanded thread submits a reply.
resolve
thread-id
Re-emitted when the expanded thread requests resolving.
reopen
thread-id
Re-emitted when the expanded thread requests reopening.
comment-edit
thread-id, comment-id
Re-emitted when a child comment edit affordance is activated.
comment-delete
thread-id, comment-id
Re-emitted when a child comment delete affordance is activated.
04
Tokens
The contract's token bindings. Open the shared contextual sidebar from any example;
primary swatches reflect the color you pick there. Error swatches show the active
preset's values; editing any knob switches to custom and resets error to TWW · Ink.
Binding
Token
marker-background
color.primary
marker-foreground
color.on-primary
resolved-background
color.surface-container
resolved-foreground
color.on-surface-variant
surface-background
color.surface
surface-foreground
color.on-surface
border-color
color.outline
radius
rounded.md
shadow
shadow.md
padding-x
spacing.sm
padding-y
spacing.xs
gap
spacing.sm
05
States
State
Source
Summary
closed
property
Marker is collapsed.
open
property
Expanded thread is visible.
resolved
property
Marker represents a resolved thread.
06
Accessibility
From the contract's accessibility obligations. Role button; Label property and comment count are combined through the localized marker label.
Tab moves focus to the marker, close button, and expanded thread controls.
Enter or Space toggles the marker when focused.
Escape closes the expanded thread and restores focus to the marker.
The marker is a button with aria-expanded reflecting open state.
The expanded thread is a non-modal inline/anchored surface, not a focus trap.
The close control is a button named by component-owned localized text.
The expanded thread preserves CommentThread native list semantics.
Status: serializable. CommentAnchor serializes marker state, flat comments, and interaction bindings for embeddable host surfaces. Host-owned persistence and anchor storage are outside the component.