bsky-richtext-react 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.1.0] — 2026-02-17
11
+
12
+ ### Changed
13
+
14
+ - **Default classNames now use Tailwind CSS utility classes** instead of the previous BEM-style class names (`bsky-editor`, `bsky-richtext`, etc.). All components have sensible out-of-the-box appearance as long as Tailwind is configured in the consumer's project — no extra setup needed.
15
+ - `defaultDisplayClassNames` — mentions, links, and tags are styled `text-blue-500 hover:underline`.
16
+ - `defaultEditorClassNames` — editor root is `block w-full relative`; mention chips and autolinks are `inline text-blue-500`.
17
+ - `defaultSuggestionClassNames` — dropdown has full visual defaults: `bg-white rounded-lg shadow-lg border`, hover states, avatar layout, truncated text columns.
18
+ - **`styles.css` removed from the published package.** The `bsky-richtext-react/styles.css` sub-export and the `./dist/styles.css` artefact no longer exist. Remove any `import 'bsky-richtext-react/styles.css'` from your app.
19
+ - **`sideEffects` set to `false`** — the package no longer has CSS side effects, enabling full tree-shaking.
20
+ - **Tailwind added as a `devDependency`** — used exclusively in Storybook for development/testing. Not bundled in or required by consumers.
21
+ - The `classNames` prop API and `generateClassNames()` are unchanged — all existing override patterns continue to work.
22
+
23
+ ### Migration
24
+
25
+ ```diff
26
+ - import 'bsky-richtext-react/styles.css'
27
+ import { RichTextEditor } from 'bsky-richtext-react'
28
+ ```
29
+
30
+ If you were targeting the old BEM class names in your own CSS, replace them with the `classNames` prop or `generateClassNames()` to apply your own classes directly.
31
+
32
+ ---
33
+
10
34
  ## [1.0.2] — 2026-02-17
11
35
 
12
36
  ### Fixed
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  - **`<RichTextDisplay>`** — Render AT Protocol richtext records (`text` + `facets`) as interactive HTML. Handles @mentions, links, and #hashtags with fully customisable renderers and URL resolvers.
15
15
  - **`<RichTextEditor>`** — TipTap-based editor with real-time @mention autocomplete (powered by the **Bluesky public API** by default — no auth required), stateless URL decoration, undo/redo, and an imperative ref API.
16
16
  - **`generateClassNames()`** — Deep-merge utility for the `classNames` prop system. Pass an array of partial classNames objects and get one merged result, optionally using your own `cn()` / `clsx` / `tailwind-merge` utility.
17
- - **Headless by design** — Ships with layout-only CSS. Bring your own colours, fonts, and borders.
17
+ - **Tailwind defaults, fully overridable** — Default classNames use Tailwind utility classes out of the box. Override any part via the `classNames` prop — no stylesheet import needed.
18
18
  - **Fully typed** — TypeScript-first with complete type definitions for all AT Protocol facet types.
19
19
  - **Tree-shakeable** — ESM + CJS dual build via `tsup`.
20
20
 
@@ -47,7 +47,6 @@ bun add react react-dom
47
47
 
48
48
  ```tsx
49
49
  import { RichTextDisplay } from 'bsky-richtext-react'
50
- import 'bsky-richtext-react/styles.css'
51
50
 
52
51
  // Pass raw fields from an app.bsky.feed.post record
53
52
  export function Post({ post }) {
@@ -59,7 +58,6 @@ export function Post({ post }) {
59
58
 
60
59
  ```tsx
61
60
  import { RichTextEditor } from 'bsky-richtext-react'
62
- import 'bsky-richtext-react/styles.css'
63
61
 
64
62
  export function Composer() {
65
63
  return (
@@ -81,84 +79,13 @@ export function Composer() {
81
79
 
82
80
  ## Styling
83
81
 
84
- ### 1. Import the structural CSS
82
+ The library ships **no CSS file**. All default styles are Tailwind utility classes applied through the `classNames` prop system. As long as Tailwind is configured in your project, components look good with zero extra setup.
85
83
 
86
- ```ts
87
- import 'bsky-richtext-react/styles.css'
88
- ```
89
-
90
- This only sets `display`, `word-break`, `box-sizing`, and flex layout rules. **No colours, fonts, or borders are applied.** You control all visual styling.
91
-
92
- ### 2. Target the default class names
93
-
94
- Every element rendered by the components carries a predictable CSS class that you can target directly:
95
-
96
- #### `<RichTextDisplay>`
97
-
98
- | Class | Element |
99
- |-------|---------|
100
- | `.bsky-richtext` | Root `<span>` |
101
- | `.bsky-mention` | @mention `<a>` |
102
- | `.bsky-link` | Link `<a>` |
103
- | `.bsky-tag` | #hashtag `<a>` |
104
-
105
- #### `<RichTextEditor>`
106
-
107
- | Class | Element |
108
- |-------|---------|
109
- | `.bsky-editor` | Root wrapper `<div>` |
110
- | `.bsky-editor-content` | ProseMirror wrapper |
111
- | `.bsky-editor-mention` | Mention chip inside editor |
112
- | `.autolink` | URL decoration span inside editor |
113
-
114
- #### `<MentionSuggestionList>` (dropdown)
115
-
116
- | Class | Element |
117
- |-------|---------|
118
- | `.bsky-suggestions` | Outer container |
119
- | `.bsky-suggestion-item` | Each suggestion row (`<button>`) |
120
- | `.bsky-suggestion-item-selected` | Currently highlighted row |
121
- | `.bsky-suggestion-avatar` | Avatar wrapper |
122
- | `.bsky-suggestion-avatar-img` | `<img>` element |
123
- | `.bsky-suggestion-avatar-placeholder` | Initial-letter fallback |
124
- | `.bsky-suggestion-text` | Text column (name + handle) |
125
- | `.bsky-suggestion-name` | Display name |
126
- | `.bsky-suggestion-handle` | `@handle` |
127
- | `.bsky-suggestion-empty` | "No results" message |
128
-
129
- ```css
130
- /* Example: style the editor */
131
- .bsky-editor .ProseMirror {
132
- padding: 12px 16px;
133
- border: 1px solid #e1e4e8;
134
- border-radius: 8px;
135
- font-size: 16px;
136
- line-height: 1.5;
137
- min-height: 120px;
138
- }
84
+ ### 1. Defaults — Tailwind utility classes
139
85
 
140
- /* Example: style mentions in a post */
141
- .bsky-mention {
142
- color: #0085ff;
143
- font-weight: 600;
144
- text-decoration: none;
145
- }
146
- .bsky-mention:hover { text-decoration: underline; }
147
-
148
- /* Example: style the suggestion dropdown */
149
- .bsky-suggestions {
150
- background: #fff;
151
- border: 1px solid #e1e4e8;
152
- border-radius: 8px;
153
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
154
- min-width: 240px;
155
- padding: 4px;
156
- }
157
- .bsky-suggestion-item { padding: 8px 10px; border-radius: 6px; }
158
- .bsky-suggestion-item-selected { background: #f0f4ff; }
159
- ```
86
+ Every component has a set of default Tailwind classes applied out of the box (see `defaultEditorClassNames`, `defaultDisplayClassNames`, `defaultSuggestionClassNames`). No stylesheet import is required.
160
87
 
161
- ### 3. Use `generateClassNames()` for targeted overrides
88
+ ### 2. Use `generateClassNames()` for targeted overrides
162
89
 
163
90
  The `classNames` prop on each component accepts a nested object. Use `generateClassNames()` to cleanly layer your classes on top of the defaults without rewriting them from scratch:
164
91
 
@@ -205,7 +132,9 @@ Pass a plain object to skip the defaults entirely:
205
132
  <RichTextDisplay classNames={{ root: 'my-text', mention: 'my-mention' }} />
206
133
  ```
207
134
 
208
- ### 4. Tailwind integration
135
+ ### 3. Using `tailwind-merge` to deduplicate classes
136
+
137
+ When layering your own Tailwind classes on top of the defaults, use `tailwind-merge` as the `cn` argument to avoid conflicting class duplication:
209
138
 
210
139
  ```tsx
211
140
  import { twMerge } from 'tailwind-merge'
package/dist/index.cjs CHANGED
@@ -35,29 +35,28 @@ function isTagFeature(feature) {
35
35
 
36
36
  // src/defaults/classNames.ts
37
37
  var defaultDisplayClassNames = {
38
- root: "bsky-richtext",
39
- mention: "bsky-mention",
40
- link: "bsky-link",
41
- tag: "bsky-tag"
38
+ root: "inline break-words",
39
+ mention: "inline text-blue-500 hover:underline cursor-pointer",
40
+ link: "inline text-blue-500 hover:underline",
41
+ tag: "inline text-blue-500 hover:underline cursor-pointer"
42
42
  };
43
43
  var defaultSuggestionClassNames = {
44
- root: "bsky-suggestions",
45
- item: "bsky-suggestion-item",
46
- itemSelected: "bsky-suggestion-item-selected",
47
- avatar: "bsky-suggestion-avatar",
48
- avatarImg: "bsky-suggestion-avatar-img",
49
- avatarPlaceholder: "bsky-suggestion-avatar-placeholder",
50
- text: "bsky-suggestion-text",
51
- name: "bsky-suggestion-name",
52
- handle: "bsky-suggestion-handle",
53
- empty: "bsky-suggestion-empty"
44
+ root: "flex flex-col max-h-80 overflow-y-auto bg-white rounded-lg shadow-lg border border-gray-200 min-w-60",
45
+ item: "flex items-center gap-3 w-full px-3 py-2 text-left cursor-pointer border-none bg-transparent hover:bg-gray-100 select-none",
46
+ itemSelected: "bg-gray-100",
47
+ avatar: "flex-shrink-0 w-10 h-10 rounded-full overflow-hidden bg-gray-200 flex items-center justify-center",
48
+ avatarImg: "block w-full h-full object-cover",
49
+ avatarPlaceholder: "flex items-center justify-center w-full h-full text-gray-500 font-medium text-sm",
50
+ text: "flex flex-col flex-1 min-w-0 overflow-hidden",
51
+ name: "block truncate font-medium text-gray-900 text-sm",
52
+ handle: "block truncate text-xs text-gray-500",
53
+ empty: "block px-3 py-2 text-sm text-gray-500"
54
54
  };
55
55
  var defaultEditorClassNames = {
56
- root: "bsky-editor",
57
- content: "bsky-editor-content",
58
- placeholder: "bsky-editor-placeholder",
59
- mention: "bsky-editor-mention",
60
- link: "autolink",
56
+ root: "block w-full relative",
57
+ content: "block w-full",
58
+ mention: "inline text-blue-500",
59
+ link: "inline text-blue-500",
61
60
  suggestion: defaultSuggestionClassNames
62
61
  };
63
62
 
@@ -372,13 +371,7 @@ function createDebouncedSearch(delayMs = 300) {
372
371
  });
373
372
  };
374
373
  }
375
- var MentionSuggestionList = react.forwardRef(function MentionSuggestionListImpl({
376
- items,
377
- command,
378
- showAvatars = true,
379
- noResultsText = "No results",
380
- classNames: classNamesProp
381
- }, ref) {
374
+ var MentionSuggestionList = react.forwardRef(function MentionSuggestionListImpl({ items, command, showAvatars = true, noResultsText = "No results", classNames: classNamesProp }, ref) {
382
375
  const [selectedIndex, setSelectedIndex] = react.useState(0);
383
376
  const cn = react.useMemo(
384
377
  () => generateClassNames([defaultSuggestionClassNames, classNamesProp]),