slack-blocks-to-jsx 1.0.3 → 1.0.4
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/KNOWLEDGE_BASE.md +772 -0
- package/dist/index.d.mts +113 -2
- package/dist/index.d.ts +113 -2
- package/dist/index.js +11 -11
- package/dist/index.mjs +11 -11
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/readme.md +3 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
# slack-blocks-to-jsx — Knowledge Base
|
|
2
|
+
|
|
3
|
+
> Book-style knowledge base for the `slack-blocks-to-jsx` React library.
|
|
4
|
+
> Use the index below to jump to the section you need — do **not** read linearly.
|
|
5
|
+
> When code or architecture changes, update the relevant chapter here in the same PR.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
| Ch. | Section | Topic |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| 0 | [§0](#chapter-0--how-to-use-this-knowledge-base) | **How to use this knowledge base** |
|
|
14
|
+
| 0 | [§0.1](#01-what-this-document-is) | What this document is |
|
|
15
|
+
| 0 | [§0.2](#02-how-to-read-it-effectively) | How to read it effectively |
|
|
16
|
+
| 0 | [§0.3](#03-when-to-update-it) | When to update it |
|
|
17
|
+
| 0 | [§0.4](#04-how-to-update-it) | How to update it |
|
|
18
|
+
| 0 | [§0.5](#05-style-rules) | Style rules |
|
|
19
|
+
| 0 | [§0.6](#06-who-maintains-this) | Who maintains this |
|
|
20
|
+
| 1 | [§1](#chapter-1--overview) | Overview, identity, links |
|
|
21
|
+
| 1 | [§1.1](#11-package-identity) | Package identity |
|
|
22
|
+
| 1 | [§1.2](#12-what-it-does) | What it does |
|
|
23
|
+
| 1 | [§1.3](#13-repo--publish-links) | Repo and publish links |
|
|
24
|
+
| 2 | [§2](#chapter-2--installation--runtime-requirements) | Installation & runtime requirements |
|
|
25
|
+
| 2 | [§2.1](#21-install-command) | Install command |
|
|
26
|
+
| 2 | [§2.2](#22-peer-dependencies) | Peer dependencies |
|
|
27
|
+
| 2 | [§2.3](#23-runtime-dependencies) | Runtime dependencies |
|
|
28
|
+
| 2 | [§2.4](#24-css-import-requirement) | CSS import requirement |
|
|
29
|
+
| 3 | [§3](#chapter-3--public-api) | Public API |
|
|
30
|
+
| 3 | [§3.1](#31-exports-from-srcindexts) | Exports from `src/index.ts` |
|
|
31
|
+
| 3 | [§3.2](#32-message-component-props) | `Message` component props |
|
|
32
|
+
| 3 | [§3.3](#33-header-component) | `Header` component |
|
|
33
|
+
| 3 | [§3.4](#34-blockwrapper-component) | `BlockWrapper` component |
|
|
34
|
+
| 3 | [§3.5](#35-component-dispatcher) | Component dispatcher |
|
|
35
|
+
| 4 | [§4](#chapter-4--blocks-reference) | Blocks reference |
|
|
36
|
+
| 4 | [§4.1](#41-layout--structure-blocks) | Layout & structure blocks (section, divider, header) |
|
|
37
|
+
| 4 | [§4.2](#42-media-blocks) | Media blocks (image, video, file) |
|
|
38
|
+
| 4 | [§4.3](#43-interactive-blocks) | Interactive blocks (actions, input, context_actions) |
|
|
39
|
+
| 4 | [§4.4](#44-content-blocks) | Content blocks (context, table, markdown) |
|
|
40
|
+
| 4 | [§4.5](#45-ai-workflow-blocks) | AI/workflow blocks (plan, task_card) |
|
|
41
|
+
| 4 | [§4.6](#46-status--rich-container-blocks-added-v110--slack-2026-04-16) | Status & rich-container blocks (alert, card, carousel) |
|
|
42
|
+
| 4 | [§4.7](#47-rich-text-block) | Rich text block (deep dive) |
|
|
43
|
+
| 5 | [§5](#chapter-5--elements-reference) | Elements reference |
|
|
44
|
+
| 5 | [§5.1](#51-button-family) | Button family (button, workflow_button) |
|
|
45
|
+
| 5 | [§5.2](#52-single-select-menus) | Single-select menus |
|
|
46
|
+
| 5 | [§5.3](#53-multi-select-menus) | Multi-select menus |
|
|
47
|
+
| 5 | [§5.4](#54-choice-elements) | Choice elements (radio_buttons, checkboxes) |
|
|
48
|
+
| 5 | [§5.5](#55-text--input-elements) | Text & input elements |
|
|
49
|
+
| 5 | [§5.6](#56-date--time-pickers) | Date & time pickers |
|
|
50
|
+
| 5 | [§5.7](#57-file-upload--rich-text-input) | File upload & rich text input |
|
|
51
|
+
| 5 | [§5.8](#58-display-elements) | Display elements (image, overflow, feedback, icon, url) |
|
|
52
|
+
| 6 | [§6](#chapter-6--type-system) | Type system |
|
|
53
|
+
| 6 | [§6.1](#61-typeslayoutts) | `types/layout.ts` — block union |
|
|
54
|
+
| 6 | [§6.2](#62-typeselementsts) | `types/elements.ts` — element union |
|
|
55
|
+
| 6 | [§6.3](#63-typesobjectsts) | `types/objects.ts` — composition objects |
|
|
56
|
+
| 6 | [§6.4](#64-typesrich_text_elementts) | `types/rich_text_element.ts` |
|
|
57
|
+
| 7 | [§7](#chapter-7--state--render-hooks) | State & render hooks |
|
|
58
|
+
| 7 | [§7.1](#71-data-prop-users-channels-usergroups) | `data` prop |
|
|
59
|
+
| 7 | [§7.2](#72-hooks-prop-custom-renderers) | `hooks` prop |
|
|
60
|
+
| 7 | [§7.3](#73-useglobaldata-hook) | `useGlobalData` internal hook |
|
|
61
|
+
| 8 | [§8](#chapter-8--rendering-pipeline) | Rendering pipeline |
|
|
62
|
+
| 8 | [§8.1](#81-dispatcher-flow) | Dispatcher flow |
|
|
63
|
+
| 8 | [§8.2](#82-markdown-parser) | Markdown parser (Yozora + custom Slack tokenizers) |
|
|
64
|
+
| 8 | [§8.3](#83-emoji-parser) | Emoji parser |
|
|
65
|
+
| 8 | [§8.4](#84-composition-objects) | Composition objects (text, confirm_dialog) |
|
|
66
|
+
| 9 | [§9](#chapter-9--styling) | Styling |
|
|
67
|
+
| 9 | [§9.1](#91-tailwind-scope) | Tailwind scope |
|
|
68
|
+
| 9 | [§9.2](#92-dark-mode) | Dark mode |
|
|
69
|
+
| 9 | [§9.3](#93-css-build-pipeline) | CSS build pipeline |
|
|
70
|
+
| 10 | [§10](#chapter-10--build--distribution) | Build & distribution |
|
|
71
|
+
| 10 | [§10.1](#101-tsup-config) | tsup config |
|
|
72
|
+
| 10 | [§10.2](#102-tsconfig) | tsconfig |
|
|
73
|
+
| 10 | [§10.3](#103-scripts) | npm scripts |
|
|
74
|
+
| 10 | [§10.4](#104-dist-output) | dist/ output |
|
|
75
|
+
| 10 | [§10.5](#105-npm-publish-hygiene) | npm publish hygiene |
|
|
76
|
+
| 11 | [§11](#chapter-11--release-process) | Release process |
|
|
77
|
+
| 12 | [§12](#chapter-12--test-fixtures) | Test fixtures |
|
|
78
|
+
| 13 | [§13](#chapter-13--complete-file-tree) | Complete file tree |
|
|
79
|
+
| 14 | [§14](#chapter-14--architectural-conventions) | Architectural conventions |
|
|
80
|
+
| 15 | [§15](#chapter-15--change-log-of-this-doc) | Change log of this doc |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Chapter 0 — How to use this knowledge base
|
|
85
|
+
|
|
86
|
+
### 0.1 What this document is
|
|
87
|
+
|
|
88
|
+
A chapter-indexed field manual covering the entire `slack-blocks-to-jsx` codebase — architecture, public API, every block and element, the type system, the rendering pipeline, build tooling, and release process. It is **not** a tutorial and **not** the source of truth (the code is). It is the fastest way to answer "where does X live?" and "how does X work?" without re-reading source.
|
|
89
|
+
|
|
90
|
+
Think of it as a reference book, not a textbook. Look up, don't read through.
|
|
91
|
+
|
|
92
|
+
### 0.2 How to read it effectively
|
|
93
|
+
|
|
94
|
+
1. **Always start at the TOC.** Never scroll the body looking for a topic — use the index.
|
|
95
|
+
2. **Jump by anchor link.** Every TOC entry is clickable and drops you at the exact section.
|
|
96
|
+
3. **Read the smallest unit needed.** A section is written to stand on its own; you rarely need the surrounding chapter.
|
|
97
|
+
4. **Follow the cross-references.** `see §4.7` means "jump to Chapter 4, Section 7" — do that instead of searching again.
|
|
98
|
+
5. **Gap = signal.** If the TOC doesn't answer your question, the doc is stale. Fix it after you learn the answer from the code (see §0.3–§0.4).
|
|
99
|
+
|
|
100
|
+
Quick routing by task:
|
|
101
|
+
|
|
102
|
+
| If you're asking… | Go to |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| What file is X in? | Ch. 13 (file tree) |
|
|
105
|
+
| What does the public API export? | Ch. 3 |
|
|
106
|
+
| How is block/element X implemented? | Ch. 4 / Ch. 5 |
|
|
107
|
+
| What's the type of X? | Ch. 6 |
|
|
108
|
+
| How do mentions / mrkdwn get parsed? | Ch. 8 §8.2 |
|
|
109
|
+
| How does styling / dark mode work? | Ch. 9 |
|
|
110
|
+
| How do I ship a change? | Ch. 10 / Ch. 11 |
|
|
111
|
+
| What rule governs X? | Ch. 14 (conventions) |
|
|
112
|
+
|
|
113
|
+
### 0.3 When to update it
|
|
114
|
+
|
|
115
|
+
Update the knowledge base in the **same PR** as the code change, never in a follow-up. Treat these as mandatory triggers:
|
|
116
|
+
|
|
117
|
+
- A block or element is added, renamed, removed, or has its props changed → update Ch. 4 / Ch. 5 + Ch. 6.
|
|
118
|
+
- A public export is added or removed from `src/index.ts` → update Ch. 3.
|
|
119
|
+
- Build tooling changes (`tsup.config.ts`, `tsconfig.json`, `tailwind.config.js`, `postcss.config.js`) → update Ch. 9 / Ch. 10.
|
|
120
|
+
- Peer deps, runtime deps, Node/React target shifts → update Ch. 2.
|
|
121
|
+
- A file moves, is deleted, or a folder is renamed → update Ch. 13.
|
|
122
|
+
- The release or publish process changes → update Ch. 11.
|
|
123
|
+
- A new architectural rule emerges (or an existing one changes) → update Ch. 14.
|
|
124
|
+
- A new test fixture is added under `test-blocks/` → update Ch. 12.
|
|
125
|
+
|
|
126
|
+
If the KB and code diverge by even one commit, the KB stops being trustable. Don't let that happen.
|
|
127
|
+
|
|
128
|
+
### 0.4 How to update it
|
|
129
|
+
|
|
130
|
+
1. Find the affected chapter via the TOC (do **not** grep the whole file — use the index).
|
|
131
|
+
2. Edit only the relevant section. Keep style consistent with §0.5.
|
|
132
|
+
3. **Add** a new section if needed: give it a number (`§4.8`), add a TOC row, and match the existing anchor format (lowercase, hyphen-separated, strip punctuation).
|
|
133
|
+
4. If you break an existing anchor (renamed a heading), search for `§X.Y` cross-references and update them.
|
|
134
|
+
5. Log the change in **Ch. 15 — Change log of this doc** with: date, author, one-line summary.
|
|
135
|
+
6. Review the diff: TOC row, body section, change log row — all three should appear.
|
|
136
|
+
|
|
137
|
+
### 0.5 Style rules
|
|
138
|
+
|
|
139
|
+
- **Tables** for enumerations ("list of X with properties Y and Z").
|
|
140
|
+
- **Prose** for concepts and rationale.
|
|
141
|
+
- **Code blocks** for type signatures, short examples, prop shapes — never for paragraphs.
|
|
142
|
+
- **Backticks** for file paths, identifiers, tool names, package names.
|
|
143
|
+
- **Don't duplicate source.** Summarize intent and link to the file path; the code is the truth.
|
|
144
|
+
- **Self-contained sections.** Assume the reader lands mid-book; don't say "as we saw above" without a `§` reference.
|
|
145
|
+
- **Every new heading gets a TOC row.**
|
|
146
|
+
|
|
147
|
+
### 0.6 Who maintains this
|
|
148
|
+
|
|
149
|
+
Whoever makes the code change owns the KB update for that change. When Claude/Cowork is driving, I update the KB in the same turn as the code edit and log it in Ch. 15. If you ever spot KB drift, flag it and I'll reconcile.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Chapter 1 — Overview
|
|
154
|
+
|
|
155
|
+
### 1.1 Package identity
|
|
156
|
+
|
|
157
|
+
| Field | Value |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| Name | `slack-blocks-to-jsx` |
|
|
160
|
+
| Current version | `1.0.3` (see `package.json`) |
|
|
161
|
+
| License | MIT |
|
|
162
|
+
| Author | Manish Panwar (`codeemash@gmail.com`) |
|
|
163
|
+
| Package manager | pnpm |
|
|
164
|
+
| Node target | ES2016 via tsup |
|
|
165
|
+
| Keywords | `slack`, `slack-blocks`, `slack-blocks-to-jsx`, `slack-blocks-to-react`, `slack-blocks-to-react-comp` |
|
|
166
|
+
|
|
167
|
+
### 1.2 What it does
|
|
168
|
+
|
|
169
|
+
Renders a Slack Block Kit `blocks[]` payload as React/JSX with pixel-close styling to the real Slack UI. Used to preview Slack messages, modals, and interactive surfaces inside any React app. Ships CJS, ESM, type declarations, and a single compiled CSS file.
|
|
170
|
+
|
|
171
|
+
### 1.3 Repo and publish links
|
|
172
|
+
|
|
173
|
+
- Repository: `https://github.com/themashcodee/slack-blocks-to-jsx.git`
|
|
174
|
+
- npm: `https://www.npmjs.com/package/slack-blocks-to-jsx`
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Chapter 2 — Installation & runtime requirements
|
|
179
|
+
|
|
180
|
+
### 2.1 Install command
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npm install slack-blocks-to-jsx
|
|
184
|
+
# or
|
|
185
|
+
pnpm add slack-blocks-to-jsx
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 2.2 Peer dependencies
|
|
189
|
+
|
|
190
|
+
```jsonc
|
|
191
|
+
{
|
|
192
|
+
"react": "^17 || ^18 || ^19",
|
|
193
|
+
"react-dom": "^17 || ^18 || ^19"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`.npmrc` enables `auto-install-peers=true`. Since v1.0.1, `react-markdown` and `remark-gfm` are **externalized**, so downstream bundlers (Next.js etc.) must be able to resolve them.
|
|
198
|
+
|
|
199
|
+
### 2.3 Runtime dependencies
|
|
200
|
+
|
|
201
|
+
| Package | Version | Purpose |
|
|
202
|
+
| --- | --- | --- |
|
|
203
|
+
| `node-emoji` | `^2.1.3` | Convert `:emoji_name:` → unicode |
|
|
204
|
+
| `react-markdown` | `^9.1.0` | Renders the `markdown` block (standard GFM) |
|
|
205
|
+
| `remark-gfm` | `^4.0.1` | GFM plugin for the above |
|
|
206
|
+
|
|
207
|
+
### 2.4 CSS import requirement
|
|
208
|
+
|
|
209
|
+
Users must import the compiled stylesheet once in their app entry:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import "slack-blocks-to-jsx/dist/style.css"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
All classes are prefixed under `#slack_blocks_to_jsx` (see §9.1) so they do not leak.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Chapter 3 — Public API
|
|
220
|
+
|
|
221
|
+
### 3.1 Exports from `src/index.ts`
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
export * from "./message" // Message, MessageProps
|
|
225
|
+
export * from "./types" // Block, Element, TextObject, etc.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Nothing else is intentionally public.
|
|
229
|
+
|
|
230
|
+
### 3.2 `Message` component props
|
|
231
|
+
|
|
232
|
+
File: `src/message.tsx`.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
type MessageProps = {
|
|
236
|
+
blocks: Block[]
|
|
237
|
+
logo: string
|
|
238
|
+
name: string
|
|
239
|
+
time?: Date // defaults to new Date()
|
|
240
|
+
className?: string
|
|
241
|
+
style?: React.CSSProperties
|
|
242
|
+
theme?: "light" | "dark"
|
|
243
|
+
showBlockKitDebug?: boolean // shows a "Open in Block Kit Builder" link
|
|
244
|
+
unstyled?: boolean // default false; skip shipping classes
|
|
245
|
+
withoutWrapper?: boolean // skip the outer container + header
|
|
246
|
+
data?: {
|
|
247
|
+
users?: { id: string; name: string }[]
|
|
248
|
+
channels?: { id: string; name: string }[]
|
|
249
|
+
user_groups?: { id: string; name: string }[]
|
|
250
|
+
}
|
|
251
|
+
hooks?: GlobalStore["hooks"] // see §7.2
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Rendered DOM shape:
|
|
256
|
+
|
|
257
|
+
```html
|
|
258
|
+
<div id="slack_blocks_to_jsx" data-theme="light|dark">
|
|
259
|
+
<Header name={name} time={time} />
|
|
260
|
+
<div class="slack_blocks_to_jsx--blocks">
|
|
261
|
+
{blocks.map(b => <BlockWrapper>{getBlockComponent(b)}</BlockWrapper>)}
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 3.3 `Header` component
|
|
267
|
+
|
|
268
|
+
File: `src/header.tsx`. Renders app name (bold) + `APP` badge + `HH:mm` timestamp.
|
|
269
|
+
|
|
270
|
+
### 3.4 `BlockWrapper` component
|
|
271
|
+
|
|
272
|
+
File: `src/block_wrapper.tsx`. Thin wrapper that sets dark-mode text color and `break-words`.
|
|
273
|
+
|
|
274
|
+
### 3.5 Component dispatcher
|
|
275
|
+
|
|
276
|
+
File: `src/components/index.tsx`.
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
getBlockComponent(block: Block): ReactElement | null
|
|
280
|
+
getElementComponent(element: Element): ReactElement | null
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Switch on `block.type` / `element.type`. Returns `null` for unsupported types, so unknown Slack block additions won't crash — they just render nothing.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Chapter 4 — Blocks reference
|
|
288
|
+
|
|
289
|
+
All block components live in `src/components/blocks/`. 17 block types are supported.
|
|
290
|
+
|
|
291
|
+
### 4.1 Layout & structure blocks
|
|
292
|
+
|
|
293
|
+
| Block | File | Key props | Notes |
|
|
294
|
+
| --- | --- | --- | --- |
|
|
295
|
+
| `section` | `section.tsx` | `text`, `fields`, `accessory`, `expand`, `block_id` | Workhorse block; accessory can stack vertically (see `utils/is_accessory_stacked.ts`) |
|
|
296
|
+
| `divider` | `divider.tsx` | `block_id` | Horizontal rule |
|
|
297
|
+
| `header` | `header.tsx` | `text` (plain_text, ≤150), `block_id` | Big bold title |
|
|
298
|
+
|
|
299
|
+
### 4.2 Media blocks
|
|
300
|
+
|
|
301
|
+
| Block | File | Key props |
|
|
302
|
+
| --- | --- | --- |
|
|
303
|
+
| `image` | `image.tsx` | `image_url` or `slack_file`, `alt_text`, `title`, `block_id`; collapsible title |
|
|
304
|
+
| `video` | `video.tsx` | `video_url`, `thumbnail_url`, `title`, `alt_text`, `title_url`, `block_id`; collapsible; YouTube/Vimeo friendly |
|
|
305
|
+
| `file` | `file.tsx` | `external_id`, `source="remote"`, `block_id` |
|
|
306
|
+
|
|
307
|
+
### 4.3 Interactive blocks
|
|
308
|
+
|
|
309
|
+
| Block | File | Key props |
|
|
310
|
+
| --- | --- | --- |
|
|
311
|
+
| `actions` | `actions.tsx` | `elements[]` (≤25) — routes to `getElementComponent` for every supported element type |
|
|
312
|
+
| `input` | `input.tsx` | `label`, `element` (any input element), `optional`, `hint`, `dispatch_action_config` |
|
|
313
|
+
| `context_actions` | `context_actions.tsx` | `elements[]` (feedback/icon buttons for AI-response affordances) |
|
|
314
|
+
|
|
315
|
+
### 4.4 Content blocks
|
|
316
|
+
|
|
317
|
+
| Block | File | Notes |
|
|
318
|
+
| --- | --- | --- |
|
|
319
|
+
| `context` | `context.tsx` | Up to 10 mixed text + image elements |
|
|
320
|
+
| `table` | `table.tsx` | `cells: Cell[][]`, optional `size`; cell alignment; bordered |
|
|
321
|
+
| `markdown` | `markdown_block.tsx` | Renders **standard** GFM via `react-markdown` + `remark-gfm`; tables, task lists, code blocks, syntax highlight |
|
|
322
|
+
|
|
323
|
+
### 4.5 AI/workflow blocks
|
|
324
|
+
|
|
325
|
+
| Block | File | Key props |
|
|
326
|
+
| --- | --- | --- |
|
|
327
|
+
| `plan` | `plan.tsx` | `title: string`, `tasks: TaskCardBlock[]` — sequential progress UI |
|
|
328
|
+
| `task_card` | `task_card.tsx` | `task_id`, `title`, `status: pending \| in_progress \| complete \| error`, `details?`, `output?`, `sources?` |
|
|
329
|
+
|
|
330
|
+
### 4.6 Status & rich-container blocks (added v1.1.0 — Slack 2026-04-16)
|
|
331
|
+
|
|
332
|
+
| Block | File | Key props | Notes |
|
|
333
|
+
| --- | --- | --- | --- |
|
|
334
|
+
| `alert` | `alert.tsx` | `text: TextObject`, `level?: "default" \| "info" \| "warning" \| "error" \| "success"`, `block_id?` | Single-line status banner. Each level gets its own icon and tinted border/background that adapts to light/dark theme. `role="status"` for screen readers. Unknown level falls back to `default`. |
|
|
335
|
+
| `card` | `card.tsx` | `title?`, `subtitle?`, `body?`, `hero_image?: CardImage`, `icon?: CardImage`, `actions?: ButtonElement[]` (≤5), `block_id?`; supports `inCarousel` prop when nested | Rich container. Actions reuse the existing `ButtonElement` renderer (so `primary`/`danger` styles and `confirm` dialogs work for free). Hero image renders in a 16:9 frame above the title; icon renders inline next to the title. |
|
|
336
|
+
| `carousel` | `carousel.tsx` | `elements: CardBlock[]` (1–10), `block_id?` | Horizontally-scrollable gallery using CSS scroll-snap. Slides cap at 10; extra elements are dropped silently. Each slide passes `inCarousel` to the `Card` component for a fixed-width variant. |
|
|
337
|
+
|
|
338
|
+
### 4.7 Rich text block
|
|
339
|
+
|
|
340
|
+
Folder: `src/components/blocks/rich_text/`. The most involved block by far.
|
|
341
|
+
|
|
342
|
+
Dispatcher: `rich_text.tsx` switches on each entry of `elements: RichTextBlockElement[]` and delegates to:
|
|
343
|
+
|
|
344
|
+
- `rich_text_section.tsx` — text elements (text, link, emoji, user, usergroup, channel, broadcast, color, preformatted)
|
|
345
|
+
- `rich_text_list_wrapper.tsx` — ordered/unordered lists, nesting
|
|
346
|
+
- `rich_text_section_color.tsx` — colored background text
|
|
347
|
+
- `rich_text_section_emoji.tsx` — emoji + skin tone
|
|
348
|
+
- `rich_text_section_link.tsx`
|
|
349
|
+
- `rich_text_section_user.tsx`, `..._usergroup.tsx`, `..._channel.tsx`, `..._broadcast.tsx`
|
|
350
|
+
|
|
351
|
+
Each text sub-element supports a `style: { code?, bold?, italic?, strike? }` map. Broadcasts are `@channel`, `@here`, `@everyone`.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Chapter 5 — Elements reference
|
|
356
|
+
|
|
357
|
+
All element components live in `src/components/elements/`. The dispatcher is `getElementComponent`.
|
|
358
|
+
|
|
359
|
+
### 5.1 Button family
|
|
360
|
+
|
|
361
|
+
| Element | File |
|
|
362
|
+
| --- | --- |
|
|
363
|
+
| `button` | `button_element.tsx` — `text`, `action_id`, `url`, `value`, `style: primary\|danger`, `confirm`, `accessibility_label` |
|
|
364
|
+
| `workflow_button` | `workflow_button_element.tsx` — triggers a Slack workflow; supports `primary`/`danger` styles |
|
|
365
|
+
|
|
366
|
+
### 5.2 Single-select menus
|
|
367
|
+
|
|
368
|
+
| Element | File | Notes |
|
|
369
|
+
| --- | --- | --- |
|
|
370
|
+
| `static_select` | `static_select_element.tsx` | `options[]` or `option_groups[]`, `initial_option`, `placeholder`, `confirm` |
|
|
371
|
+
| `external_select` | `external_select_element.tsx` | `min_query_length` |
|
|
372
|
+
| `users_select` | `users_select_element.tsx` | `initial_user` |
|
|
373
|
+
| `conversations_select` | `conversations_select_element.tsx` | `filter`, `default_to_current_conversation` |
|
|
374
|
+
| `channels_select` | `channels_select_element.tsx` | `response_url_enabled` |
|
|
375
|
+
|
|
376
|
+
### 5.3 Multi-select menus
|
|
377
|
+
|
|
378
|
+
| Element | File |
|
|
379
|
+
| --- | --- |
|
|
380
|
+
| `multi_static_select` | `multi_static_select_element.tsx` |
|
|
381
|
+
| `multi_external_select` | `multi_external_select_element.tsx` |
|
|
382
|
+
| `multi_users_select` | `multi_users_select_element.tsx` |
|
|
383
|
+
| `multi_conversations_select` | `multi_conversations_select_element.tsx` |
|
|
384
|
+
| `multi_channels_select` | `multi_channels_select_element.tsx` |
|
|
385
|
+
|
|
386
|
+
All support `max_selected_items` and `confirm`.
|
|
387
|
+
|
|
388
|
+
### 5.4 Choice elements
|
|
389
|
+
|
|
390
|
+
| Element | File |
|
|
391
|
+
| --- | --- |
|
|
392
|
+
| `radio_buttons` | `radio_buttons_element.tsx` — `options[]`, `initial_option`, `confirm` |
|
|
393
|
+
| `checkboxes` | `checkboxes_element.tsx` — `options[]`, `initial_options[]`, `confirm` |
|
|
394
|
+
|
|
395
|
+
### 5.5 Text & input elements
|
|
396
|
+
|
|
397
|
+
| Element | File |
|
|
398
|
+
| --- | --- |
|
|
399
|
+
| `plain_text_input` | `plain_text_input.tsx` — `multiline`, `min_length`, `max_length`, `dispatch_action_config` |
|
|
400
|
+
| `email_text_input` | `email_input_element.tsx` |
|
|
401
|
+
| `url_text_input` | `url_input_element.tsx` |
|
|
402
|
+
| `number_input` | `number_input_element.tsx` — `is_decimal_allowed`, `min_value`, `max_value` |
|
|
403
|
+
|
|
404
|
+
### 5.6 Date & time pickers
|
|
405
|
+
|
|
406
|
+
| Element | File | Value format |
|
|
407
|
+
| --- | --- | --- |
|
|
408
|
+
| `datepicker` | `date_picker_element.tsx` | `YYYY-MM-DD` |
|
|
409
|
+
| `timepicker` | `time_picker_element.tsx` | `HH:mm`, optional `timezone` |
|
|
410
|
+
| `datetimepicker` | `datetime_picker_element.tsx` | Unix timestamp |
|
|
411
|
+
|
|
412
|
+
These are the elements that trigger vertical stacking when used as section accessories (see `utils/is_accessory_stacked.ts`).
|
|
413
|
+
|
|
414
|
+
### 5.7 File upload & rich text input
|
|
415
|
+
|
|
416
|
+
| Element | File |
|
|
417
|
+
| --- | --- |
|
|
418
|
+
| `file_input` | `file_input_element.tsx` — `filetypes[]`, `max_files` |
|
|
419
|
+
| `rich_text_input` | `rich_text_input_element.tsx` — `initial_value`, `placeholder` |
|
|
420
|
+
|
|
421
|
+
### 5.8 Display elements
|
|
422
|
+
|
|
423
|
+
| Element | File | Purpose |
|
|
424
|
+
| --- | --- | --- |
|
|
425
|
+
| `image` | `image_element.tsx` | Display-only image; `image_url` or `slack_file` |
|
|
426
|
+
| `overflow` | `overflow_menu_element.tsx` | 3-dot menu, up to 5 options |
|
|
427
|
+
| `feedback_buttons` | `feedback_buttons_element.tsx` | thumbs up / down |
|
|
428
|
+
| `icon_button` | `icon_button_element.tsx` | small icon action (e.g. trash) |
|
|
429
|
+
| `url` | `url_source_element.tsx` | Clickable URL reference for task cards |
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Chapter 6 — Type system
|
|
434
|
+
|
|
435
|
+
Types live in `src/types/` and are re-exported from `index.ts`.
|
|
436
|
+
|
|
437
|
+
### 6.1 `types/layout.ts`
|
|
438
|
+
|
|
439
|
+
Defines the `Block` union: `ActionsBlock | AlertBlock | CardBlock | CarouselBlock | ContextBlock | ContextActionsBlock | DividerBlock | FileBlock | HeaderBlock | ImageBlock | InputBlock | MarkdownBlock | PlanBlock | SectionBlock | TableBlock | TaskCardBlock | VideoBlock | RichTextBlock`. Each interface documents props with JSDoc pointing to Slack's official API docs.
|
|
440
|
+
|
|
441
|
+
Added in v1.1.0 to cover Slack's 2026-04-16 block launch:
|
|
442
|
+
|
|
443
|
+
- `AlertLevel = "default" | "info" | "warning" | "error" | "success"`
|
|
444
|
+
- `AlertBlock = { type: "alert"; text: TextObject; level?: AlertLevel; block_id? }`
|
|
445
|
+
- `CardImage = { image_url?: string; slack_file?: SlackFileObject; alt_text: string }` (shared helper for hero + icon)
|
|
446
|
+
- `CardBlock = { type: "card"; title?; subtitle?; body?; hero_image?: CardImage; icon?: CardImage; actions?: ButtonElement[]; block_id? }`
|
|
447
|
+
- `CarouselBlock = { type: "carousel"; elements: CardBlock[]; block_id? }`
|
|
448
|
+
|
|
449
|
+
### 6.2 `types/elements.ts`
|
|
450
|
+
|
|
451
|
+
Defines the `Element` union for all 28 interactive/display elements. Every element carries optional `action_id` and `confirm` (except display-only ones).
|
|
452
|
+
|
|
453
|
+
### 6.3 `types/objects.ts`
|
|
454
|
+
|
|
455
|
+
Composition objects:
|
|
456
|
+
|
|
457
|
+
- `TextObject<T = "plain_text" | "mrkdwn">` — `{ type, text, emoji?, verbatim? }`
|
|
458
|
+
- `OptionObject` — `{ text, value, description?, url? }`
|
|
459
|
+
- `OptionGroupObject` — `{ label, options[] }`
|
|
460
|
+
- `ConfirmDialogObject` — `{ title, text, confirm, deny, style? }`
|
|
461
|
+
- `DispatchActionConfigObject` — `{ trigger_actions_on?: ("on_enter_pressed" | "on_character_entered")[] }`
|
|
462
|
+
- `FilterObject` — for conversation/channel selects
|
|
463
|
+
- `SlackFileObject` — `{ url?: string; id?: string }`
|
|
464
|
+
- `TriggerObject`, `WorkflowObject` — workflow_button payloads
|
|
465
|
+
- `Style` — `"primary" | "danger" | "confirm"`
|
|
466
|
+
|
|
467
|
+
### 6.4 `types/rich_text_element.ts`
|
|
468
|
+
|
|
469
|
+
`RichTextBlockElement` union: `text`, `link`, `emoji`, `user`, `usergroup`, `channel`, `broadcast`, `color`, `preformatted`, `rich_text_list`, `rich_text_quote`, `rich_text_section`. Shared `Style = { code?, bold?, italic?, strike? }`.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Chapter 7 — State & render hooks
|
|
474
|
+
|
|
475
|
+
State is kept in a React context defined in `src/store/useGlobalContext.tsx`.
|
|
476
|
+
|
|
477
|
+
### 7.1 `data` prop (users, channels, usergroups)
|
|
478
|
+
|
|
479
|
+
Passed through to `GlobalProvider`. Used by the mention tokenizers so `<@U123>` can render as `@manish` instead of the raw id.
|
|
480
|
+
|
|
481
|
+
```ts
|
|
482
|
+
data?: {
|
|
483
|
+
users?: { id: string; name: string }[]
|
|
484
|
+
channels?: { id: string; name: string }[]
|
|
485
|
+
user_groups?: { id: string; name: string }[]
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### 7.2 `hooks` prop (custom renderers)
|
|
490
|
+
|
|
491
|
+
Full shape:
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
type Hooks = {
|
|
495
|
+
user?: (d: { id: string; name: string }) => ReactNode
|
|
496
|
+
channel?: (d: { id: string; name: string }) => ReactNode
|
|
497
|
+
usergroup?: (d: { id: string; name: string }) => ReactNode
|
|
498
|
+
atChannel?: (style?) => ReactNode
|
|
499
|
+
atEveryone?: (style?) => ReactNode
|
|
500
|
+
atHere?: (style?) => ReactNode
|
|
501
|
+
emoji?: (d, parse: () => ReactNode) => ReactNode
|
|
502
|
+
date?: (d) => ReactNode
|
|
503
|
+
link?: (input) => ReactNode // the whole <a> replacement
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Useful for wiring mentions to internal routing or using Next.js `<Link>` for URLs.
|
|
508
|
+
|
|
509
|
+
### 7.3 `useGlobalData` hook
|
|
510
|
+
|
|
511
|
+
Internal hook used by every component that needs access to `data` or `hooks`. Don't re-export; keep private.
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## Chapter 8 — Rendering pipeline
|
|
516
|
+
|
|
517
|
+
### 8.1 Dispatcher flow
|
|
518
|
+
|
|
519
|
+
```
|
|
520
|
+
Message → GlobalProvider → for each block:
|
|
521
|
+
BlockWrapper → getBlockComponent(block) → one of the blocks/*.tsx files
|
|
522
|
+
Blocks that contain elements call getElementComponent(element)
|
|
523
|
+
Text objects go through text_object.tsx (§8.4)
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### 8.2 Markdown parser
|
|
527
|
+
|
|
528
|
+
Folder: `src/utils/markdown_parser/`. Built on `@yozora/parser` with six custom Slack-specific tokenizers so Slack mrkdwn renders correctly.
|
|
529
|
+
|
|
530
|
+
| Syntax | Conversion |
|
|
531
|
+
| --- | --- |
|
|
532
|
+
| `*text*` | → `**text**` (bold) |
|
|
533
|
+
| `_text_` | italic |
|
|
534
|
+
| `~text~` | → `~~text~~` (strikethrough) |
|
|
535
|
+
| `` `code` `` | inline code |
|
|
536
|
+
| `<url\|label>` | → `[label](url)` |
|
|
537
|
+
|
|
538
|
+
Custom tokenizers (each has its own folder with `match.ts`, `parse.ts`, `tokenizer.ts`, `types.ts`):
|
|
539
|
+
|
|
540
|
+
- `slack_user_mention` — `<@U123>` / `<@U123|name>`
|
|
541
|
+
- `slack_channel_mention` — `<#C123>` / `<#C123|name>`
|
|
542
|
+
- `slack_user_group_mention` — `<!subteam^G123>` / `<!subteam^G123|@name>`
|
|
543
|
+
- `slack_broadcast` — `<!channel>`, `<!here>`, `<!everyone>`
|
|
544
|
+
- `slack_date` — `<!date^ts^format|fallback>`
|
|
545
|
+
- `slack_emoji` — `:emoji_name:` including skin-tone variants
|
|
546
|
+
|
|
547
|
+
Output is `ReactNode` via the `elements/` and `sub_elements/` folders (Blockquote, Code, Paragraph + Delete, Emphasis, InlineCode, Link, Strong, Text, HTML, SlackUserMention, etc.).
|
|
548
|
+
|
|
549
|
+
### 8.3 Emoji parser
|
|
550
|
+
|
|
551
|
+
Folder: `src/utils/emojis/`. `parser.ts` + `list.ts`. Handles `:emoji:` resolution and skin tone modifiers (1–6). Uses `node-emoji` at runtime.
|
|
552
|
+
|
|
553
|
+
### 8.4 Composition objects
|
|
554
|
+
|
|
555
|
+
Folder: `src/components/composition_objects/`.
|
|
556
|
+
|
|
557
|
+
- `text_object.tsx` — renders `TextObject`. For `mrkdwn` runs through the markdown parser above and applies `hooks.link` to any `<a>`.
|
|
558
|
+
- `confirm_dialog.tsx` — modal overlay for `ConfirmDialogObject`; click-outside cancels; supports `primary` (green) / `danger` (red) styles; full dark mode.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Chapter 9 — Styling
|
|
563
|
+
|
|
564
|
+
### 9.1 Tailwind scope
|
|
565
|
+
|
|
566
|
+
`tailwind.config.js` sets `important: "#slack_blocks_to_jsx"` so every utility is compiled as `#slack_blocks_to_jsx .class { … !important }`. This guarantees styles win against arbitrary host-app CSS without polluting it.
|
|
567
|
+
|
|
568
|
+
Theme extensions include Slack-accurate grays, blacks, blues, greens, reds, broadcast colors — all with dark-mode variants. Font sizes: `small: 13px`, `base: 15px`, `header: 18px`. Tailwind scans `./src/**/*.{js,ts,jsx,tsx,mdx}`.
|
|
569
|
+
|
|
570
|
+
### 9.2 Dark mode
|
|
571
|
+
|
|
572
|
+
`darkMode: ["selector", '[data-theme="dark"]']`. Toggled at runtime via the `theme` prop on `Message` which writes `data-theme="dark"` on the outer wrapper.
|
|
573
|
+
|
|
574
|
+
### 9.3 CSS build pipeline
|
|
575
|
+
|
|
576
|
+
`postcss.config.js` pipeline (in order):
|
|
577
|
+
|
|
578
|
+
1. `postcss-nesting`
|
|
579
|
+
2. `tailwindcss`
|
|
580
|
+
3. `autoprefixer`
|
|
581
|
+
4. `cssnano`
|
|
582
|
+
|
|
583
|
+
Input: `src/style.css`. Output: `dist/style.css` (~40 KB).
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Chapter 10 — Build & distribution
|
|
588
|
+
|
|
589
|
+
### 10.1 tsup config
|
|
590
|
+
|
|
591
|
+
`tsup.config.ts` compiles `src/index.ts` to CJS + ESM, generates `.d.ts`, minifies, and marks `react-markdown`, `remark-gfm`, `node-emoji` as external.
|
|
592
|
+
|
|
593
|
+
### 10.2 tsconfig
|
|
594
|
+
|
|
595
|
+
- `target: ES2016`, `module: CommonJS`, `jsx: react-jsx`
|
|
596
|
+
- `strict: true`, `noUncheckedIndexedAccess: true`, `skipLibCheck: true`
|
|
597
|
+
- `noEmit: true` (tsup emits)
|
|
598
|
+
|
|
599
|
+
### 10.3 Scripts
|
|
600
|
+
|
|
601
|
+
| Script | What it does |
|
|
602
|
+
| --- | --- |
|
|
603
|
+
| `build` | tsup build + `build:css` |
|
|
604
|
+
| `build:css` | PostCSS `src/style.css` → `dist/style.css` |
|
|
605
|
+
| `dev` | tsup watch |
|
|
606
|
+
| `dev:css` | PostCSS watch |
|
|
607
|
+
| `lint` | `tsc --noEmit` |
|
|
608
|
+
| `release` | `pnpm run build && changeset publish` |
|
|
609
|
+
| `release-alpha-without-git` | Publish `--tag alpha --no-git-checks` |
|
|
610
|
+
|
|
611
|
+
### 10.4 dist/ output
|
|
612
|
+
|
|
613
|
+
| File | Size | Purpose |
|
|
614
|
+
| --- | --- | --- |
|
|
615
|
+
| `index.js` | ~556 KB | CJS bundle (minified) |
|
|
616
|
+
| `index.mjs` | ~552 KB | ESM bundle (minified) |
|
|
617
|
+
| `index.d.ts` / `index.d.mts` | ~82 KB | TS declarations |
|
|
618
|
+
| `style.css` | ~40 KB | Compiled Tailwind bundle |
|
|
619
|
+
|
|
620
|
+
### 10.5 npm publish hygiene
|
|
621
|
+
|
|
622
|
+
`.npmignore` excludes: `.changeset`, `.github`, `node_modules`, `src`, `.env`, `.prettierrc`, `CHANGELOG.md`, `pnpm-lock.yaml`, `tailwind.config.js`, `tsconfig.json`, `.DS_Store`, `postcss.config.js`.
|
|
623
|
+
|
|
624
|
+
`.npmrc`: `auto-install-peers=true`.
|
|
625
|
+
|
|
626
|
+
`package.json` `exports`: `main` (CJS), `module` (ESM), `types`.
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Chapter 11 — Release process
|
|
631
|
+
|
|
632
|
+
From `PUBLISH_GUIDE.md`, in short:
|
|
633
|
+
|
|
634
|
+
1. Commit source changes.
|
|
635
|
+
2. `pnpm changeset` — pick patch / minor / major; write summary.
|
|
636
|
+
3. `git push origin main` — triggers GitHub Actions (`.github/workflows/`).
|
|
637
|
+
4. CI runs lint + build.
|
|
638
|
+
5. Changesets bot opens a "Version Packages" PR (bumps version, updates `CHANGELOG.md`).
|
|
639
|
+
6. Merge that PR → publish workflow runs `pnpm run release` → `pnpm run build && changeset publish`.
|
|
640
|
+
|
|
641
|
+
`RELEASE_NOTES.md` tells the story of v1.0.1 — the "full Block Kit parity" release — after 80+ iterative releases. Major breaking changes from that version:
|
|
642
|
+
|
|
643
|
+
- `react-markdown` + `remark-gfm` externalized (host apps must transpile them in Next.js).
|
|
644
|
+
- Actions block now routes **all** element types; previously silently dropped unsupported ones.
|
|
645
|
+
- Input block accepts all input element types; previously only `plain_text_input` and `checkboxes`.
|
|
646
|
+
|
|
647
|
+
New in 1.0.1: Context Actions, Markdown, Plan, Task Card blocks; 22 new interactive elements; confirm dialogs everywhere; dark mode via `data-theme`; GFM markdown; Slack-hosted files for images; `expand` on section.
|
|
648
|
+
|
|
649
|
+
Current version at time of writing: **1.0.3**.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## Chapter 12 — Test fixtures
|
|
654
|
+
|
|
655
|
+
Folder: `test-blocks/`. Used as manual/visual test inputs. Each file is a complete `blocks[]` payload.
|
|
656
|
+
|
|
657
|
+
| File | Covers |
|
|
658
|
+
| --- | --- |
|
|
659
|
+
| `00-all-blocks-part1.json` / `part2.json` | Kitchen-sink of every block type |
|
|
660
|
+
| `01-rich-text-nested.json` | Nested rich text formatting |
|
|
661
|
+
| `02-actions-confirm-dialogs.json` | Actions + confirm modals |
|
|
662
|
+
| `03-all-input-types.json` | All input elements |
|
|
663
|
+
| `04-option-groups-multi-selects.json` | Option groups + multi-selects |
|
|
664
|
+
| `05-markdown-block.json` | GFM markdown (tables, code, task lists) |
|
|
665
|
+
| `06-plan-task-cards.json` | Plan + task card statuses |
|
|
666
|
+
| `07-context-actions-feedback.json` | Feedback buttons |
|
|
667
|
+
| `08-table-image-video.json` | Table + image + video |
|
|
668
|
+
| `09-pr-review-workflow.json` | Workflow buttons |
|
|
669
|
+
| `10-kitchen-sink.json` | Large combined payload |
|
|
670
|
+
| `11-alert-card-carousel.json` | All 5 alert levels, card (hero + icon + actions), standalone card, 3-card pricing carousel |
|
|
671
|
+
| `slack-safe-part1.json` | Compatibility fixture |
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
## Chapter 13 — Complete file tree
|
|
676
|
+
|
|
677
|
+
```
|
|
678
|
+
slack blocks to jsx library/
|
|
679
|
+
├── .changeset/
|
|
680
|
+
├── .github/
|
|
681
|
+
├── dist/
|
|
682
|
+
│ ├── index.js index.mjs index.d.ts index.d.mts style.css
|
|
683
|
+
├── src/
|
|
684
|
+
│ ├── index.ts # Public exports
|
|
685
|
+
│ ├── message.tsx # <Message> (entry)
|
|
686
|
+
│ ├── header.tsx # App header subcomponent
|
|
687
|
+
│ ├── block_wrapper.tsx # Per-block wrapper
|
|
688
|
+
│ ├── style.css # Tailwind source
|
|
689
|
+
│ ├── components/
|
|
690
|
+
│ │ ├── index.tsx # getBlockComponent / getElementComponent
|
|
691
|
+
│ │ ├── blocks/
|
|
692
|
+
│ │ │ ├── section.tsx divider.tsx image.tsx context.tsx
|
|
693
|
+
│ │ │ ├── header.tsx actions.tsx input.tsx video.tsx
|
|
694
|
+
│ │ │ ├── file.tsx table.tsx markdown_block.tsx
|
|
695
|
+
│ │ │ ├── plan.tsx task_card.tsx context_actions.tsx
|
|
696
|
+
│ │ │ ├── alert.tsx card.tsx carousel.tsx (new in v1.1.0)
|
|
697
|
+
│ │ │ └── rich_text/
|
|
698
|
+
│ │ │ ├── rich_text.tsx
|
|
699
|
+
│ │ │ ├── rich_text_section.tsx
|
|
700
|
+
│ │ │ ├── rich_text_list_wrapper.tsx
|
|
701
|
+
│ │ │ ├── rich_text_section_color.tsx
|
|
702
|
+
│ │ │ ├── rich_text_section_emoji.tsx
|
|
703
|
+
│ │ │ ├── rich_text_section_link.tsx
|
|
704
|
+
│ │ │ ├── rich_text_section_user.tsx
|
|
705
|
+
│ │ │ ├── rich_text_section_usergroup.tsx
|
|
706
|
+
│ │ │ ├── rich_text_section_channel.tsx
|
|
707
|
+
│ │ │ └── rich_text_section_broadcast.tsx
|
|
708
|
+
│ │ ├── elements/ # 28 *_element.tsx files (see Ch. 5)
|
|
709
|
+
│ │ └── composition_objects/
|
|
710
|
+
│ │ ├── text_object.tsx
|
|
711
|
+
│ │ └── confirm_dialog.tsx
|
|
712
|
+
│ ├── store/
|
|
713
|
+
│ │ └── useGlobalContext.tsx # GlobalProvider + useGlobalData
|
|
714
|
+
│ ├── types/
|
|
715
|
+
│ │ ├── layout.ts elements.ts objects.ts rich_text_element.ts
|
|
716
|
+
│ └── utils/
|
|
717
|
+
│ ├── date.ts
|
|
718
|
+
│ ├── is_accessory_stacked.ts
|
|
719
|
+
│ ├── merge_classes.ts
|
|
720
|
+
│ ├── numbers.ts
|
|
721
|
+
│ ├── sanitize_for_slack.ts
|
|
722
|
+
│ ├── emojis/{parser.ts, list.ts}
|
|
723
|
+
│ └── markdown_parser/
|
|
724
|
+
│ ├── parser.tsx types.ts
|
|
725
|
+
│ ├── elements/{blockquote.tsx, code.tsx, paragraph.tsx}
|
|
726
|
+
│ ├── sub_elements/
|
|
727
|
+
│ │ ├── delete.tsx emphasis.tsx html.tsx inline_code.tsx
|
|
728
|
+
│ │ ├── link.tsx slack_broadcast.tsx slack_channel_mention.tsx
|
|
729
|
+
│ │ ├── slack_date.tsx slack_emoji.tsx
|
|
730
|
+
│ │ ├── slack_user_group_mention.tsx slack_user_mention.tsx
|
|
731
|
+
│ │ ├── strong.tsx text.tsx
|
|
732
|
+
│ └── tokenizers/
|
|
733
|
+
│ ├── slack_user_mention/
|
|
734
|
+
│ ├── slack_channel_mention/
|
|
735
|
+
│ ├── slack_user_group_mention/
|
|
736
|
+
│ ├── slack_broadcast/
|
|
737
|
+
│ ├── slack_date/
|
|
738
|
+
│ └── slack_emoji/
|
|
739
|
+
├── test-blocks/ # JSON fixtures (see Ch. 12)
|
|
740
|
+
├── package.json tsconfig.json tsup.config.ts
|
|
741
|
+
├── tailwind.config.js postcss.config.js .prettierrc
|
|
742
|
+
├── readme.md PUBLISH_GUIDE.md RELEASE_NOTES.md
|
|
743
|
+
├── .npmignore .npmrc .env .gitignore
|
|
744
|
+
└── pnpm-lock.yaml
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## Chapter 14 — Architectural conventions
|
|
750
|
+
|
|
751
|
+
Rules worth knowing before making changes:
|
|
752
|
+
|
|
753
|
+
1. **File naming:** snake_case for all files in `src/` (`section.tsx`, `button_element.tsx`).
|
|
754
|
+
2. **Dispatcher pattern:** never `switch` on `block.type` inside a block component. Always let `components/index.tsx` route. Returning `null` for unsupported types is intentional — it means forward-compat without crashes.
|
|
755
|
+
3. **Tailwind scope:** every visible component must render inside the `#slack_blocks_to_jsx` wrapper — otherwise `important` selectors don't match.
|
|
756
|
+
4. **Dark mode:** use Tailwind `dark:` variants. Don't read `theme` prop directly in components; rely on the `data-theme` attribute and CSS.
|
|
757
|
+
5. **Mentions:** never render raw `<@U123>` in components. Use the markdown parser so tokenizers and `hooks` fire.
|
|
758
|
+
6. **Hooks over forking:** anything that needs host-app integration (routing, avatars, emoji catalog) should be exposed via the `hooks` prop, not hardcoded.
|
|
759
|
+
7. **Externalization:** `react-markdown`, `remark-gfm`, `node-emoji` must stay external in tsup config — bundling them again will break Next.js ESM interop.
|
|
760
|
+
8. **Accessibility:** preserve the `accessibility_label` prop on buttons; use semantic tags (button, a, input) — this mirrors Slack's own a11y posture.
|
|
761
|
+
9. **Type-first:** every block/element addition requires a matching interface in `src/types/` before the component lands.
|
|
762
|
+
10. **Changesets:** any user-facing change needs a changeset before merge.
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## Chapter 15 — Change log of this doc
|
|
767
|
+
|
|
768
|
+
| Date | Author | Note |
|
|
769
|
+
| --- | --- | --- |
|
|
770
|
+
| 2026-04-17 | Claude + Mash | Initial knowledge base created from full source walkthrough at v1.0.3. |
|
|
771
|
+
| 2026-04-17 | Claude + Mash | Added Ch. 0 — How to use this knowledge base (usage, reading strategy, update triggers, update procedure, style rules, ownership). |
|
|
772
|
+
| 2026-04-17 | Claude + Mash | v1.1.0 release prep: added three new blocks (`alert`, `card`, `carousel`) matching Slack's 2026-04-16 Block Kit launch. Ch. 4 block count 14 → 17 (new §4.6 "Status & rich-container blocks"). Ch. 6.1 type list updated with `AlertBlock`, `CardBlock`, `CarouselBlock`, `CardImage`, `AlertLevel`. Ch. 12 gained `11-alert-card-carousel.json` fixture. Ch. 13 file tree updated. Changeset: `.changeset/add-alert-card-carousel-blocks.md`. |
|