Add Bluesky replies, quotes, and reposts to any web page. Handy for adding a comments section anywhere.
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add README with usage docs, attributes, and CSS custom properties

Jim Ray fd148bc5 abaf334c

+113
+113
README.md
··· 1 + # &lt;bsky-conversation&gt; 2 + 3 + A zero-dependency web component that displays a Bluesky conversation thread — replies, quote posts, and reposts — for any public Bluesky post. Drop it into any page with a single `<script>` tag. 4 + 5 + ## Quick start 6 + 7 + ```html 8 + <script type="module" src="https://unpkg.com/bsky-conversation"></script> 9 + 10 + <bsky-conversation uri="https://bsky.app/profile/did:plc:.../post/..."></bsky-conversation> 11 + ``` 12 + 13 + That's it. No build step, no dependencies. 14 + 15 + ## Install via npm 16 + 17 + ```bash 18 + npm install bsky-conversation 19 + ``` 20 + 21 + ```js 22 + // Auto-registers the <bsky-conversation> element 23 + import 'bsky-conversation' 24 + 25 + // Or import the class for manual registration 26 + import { BskyConversation } from 'bsky-conversation' 27 + customElements.define('my-conversation', BskyConversation) 28 + ``` 29 + 30 + ## Attributes 31 + 32 + | Attribute | Default | Description | 33 + |-----------|---------|-------------| 34 + | `uri` | (required) | The bsky.app post URL. Use DID-based URLs for reliability. | 35 + | `max-depth` | `3` | How many levels of nested replies to show. At the cutoff, a "More of the conversation on Bluesky" link appears. | 36 + | `show-original-post` | `false` | Set to `"true"` to include the root post in the timeline. | 37 + | `engage-text` | `"Add your thoughts on Bluesky"` | CTA link text shown in the header and at the bottom. Set to `""` to hide. | 38 + | `header-template` | (none) | Custom header template string. Overrides the default header format. | 39 + 40 + ## Template syntax 41 + 42 + The `header-template` attribute supports a mini template language. 43 + 44 + **Simple tokens** — replaced with their value: 45 + 46 + | Token | Value | 47 + |-------|-------| 48 + | `{replies}` | Raw reply count | 49 + | `{quotes}` | Raw quote count | 50 + | `{reposts}` | Raw repost count | 51 + | `{repostedBy}` | Linked names, e.g. `@alice, @bob, and 3 others` | 52 + | `{postUrl}` | The bsky.app post URL | 53 + 54 + **Pluralization** — `{name|singular|plural}` outputs nothing when 0, `"1 singular"` when 1, `"N plural"` when 2+: 55 + 56 + ``` 57 + {replies|reply|replies} → "" or "1 reply" or "17 replies" 58 + {quotes|quote|quotes} → "" or "1 quote" or "5 quotes" 59 + ``` 60 + 61 + **Conditional blocks** — `{name?content}` renders content only if the value is truthy: 62 + 63 + ``` 64 + {repostedBy?Reposted by {repostedBy}.} → "" or "Reposted by @alice, @bob." 65 + {replies?{replies|reply|replies} so far} → "" or "17 replies so far" 66 + ``` 67 + 68 + **Full example:** 69 + 70 + ```html 71 + <bsky-conversation 72 + uri="https://bsky.app/profile/did:plc:.../post/..." 73 + header-template="This post has {replies?{replies|reply|replies}}{quotes?, {quotes|quote|quotes}}{repostedBy?, and has been reposted by {repostedBy}}." 74 + /> 75 + ``` 76 + 77 + ## CSS custom properties 78 + 79 + The component defines design tokens with sensible defaults, overridable from the host page. All internal sizing uses `em` units, so it scales with inherited font size. 80 + 81 + | Property | Light default | Dark default | Controls | 82 + |----------|--------------|-------------|----------| 83 + | `--bsky-border-color` | `#e5e7eb` | `#374151` | Separators, thread lines | 84 + | `--bsky-muted-color` | `#6b7280` | `#9ca3af` | Handles, timestamps, secondary text | 85 + | `--bsky-link-color` | `black` | `#60a5fa` | Link text color | 86 + | `--bsky-link-hover` | `#2563eb` | `#3b82f6` | Link hover color | 87 + | `--bsky-link-underline` | `rgba(82,82,91,0.5)` | `rgba(59,130,246,0.3)` | Link underline color | 88 + | `--bsky-link-underline-hover` | `rgba(59,130,246,0.3)` | `rgba(59,130,246,0.3)` | Link underline hover color | 89 + 90 + ```css 91 + bsky-conversation { 92 + --bsky-link-color: #333; 93 + --bsky-muted-color: #888; 94 + } 95 + ``` 96 + 97 + Dark mode activates when a parent element has the `dark` class. 98 + 99 + ## Behavior 100 + 101 + - Fetches from the public Bluesky API (no authentication needed) 102 + - Rich text rendering with proper UTF-8 byte-offset facet handling (links, @mentions, #hashtags) 103 + - Root post author's direct replies are filtered out (they're extensions of the original post, not conversation) 104 + - Hidden replies (via threadgate) and detached quotes (via postgate) are filtered out 105 + - Reply threads stay grouped — nested replies are not flattened into the timeline 106 + - Quote posts are interleaved chronologically with top-level reply threads 107 + - Reposts appear only in the header summary, not as timeline items 108 + - API failures degrade gracefully — the rest of the conversation still renders 109 + - All user content is XSS-hardened through `escapeHtml()` 110 + 111 + ## License 112 + 113 + MIT