Slack Channel Conformance Spec
Conformance tests for the Norq Slack compiler. The compiler takes Markdown input and produces Slack Block Kit JSON — a JSON array of block objects.
Each example block specifies input markdown and one or more expected output
fragments. A fragment is a substring that must appear in the serialized Block
Kit JSON array string. The test runner compiles each input with
serde_json::Value::Null as data and checks that every fragment appears in
serde_json::to_string(&out.blocks).
Format:
- Opening fence: 32 backticks followed by
example channel=slack - Input markdown (first section, before the first
.separator) .separator line- One or more expected fragments, each separated by
.lines - Closing fence: 32 backticks
Paragraphs
Basic paragraph
A plain paragraph compiles to a section block with a mrkdwn text object.
The section type and mrkdwn sub-type are both required by Block Kit.
Hello from Slack.
.
"type":"section"
.
"type":"mrkdwn"
.
Hello from Slack.Multiple paragraphs produce multiple section blocks
Each paragraph in the input compiles to its own section block. Paragraphs
are not batched together (unlike the email compiler).
First paragraph.
Second paragraph.
.
"text":"First paragraph."
.
"text":"Second paragraph."Headings
Markdown heading compiles to a header block
A markdown heading (#) compiles to a header block with plain_text. Slack
header blocks render as large, bold text at the top of a message section.
# Build Passed
.
"type":"header"
.
"type":"plain_text"
.
"text":"Build Passed"All heading levels (#, ##, ###, etc.) produce the same header block
type — Slack Block Kit has only one header block with no level distinction.
## Subsection Title
.
"type":"header"
.
"text":"Subsection Title"Directives
:::header compiles to a header block
The :::header directive also emits a header block with plain_text. It is
equivalent to a markdown heading as far as Block Kit output is concerned.
:::header
Deployment Complete
:::
.
"type":"header"
.
"type":"plain_text"
.
"text":"Deployment Complete":::action compiles to an actions block with a button
The :::action directive emits an actions block. It contains a single
button element with style: "primary". Only the first link found inside the
directive body is extracted — additional links are silently ignored.
:::action
[View Report](https://example.com/report)
:::
.
"type":"actions"
.
"style":"primary"
.
"text":"View Report"
.
"url":"https://example.com/report"When the directive contains multiple links, only the first link produces a button. The second link is not emitted.
:::action
[Primary](https://example.com/primary) [Secondary](https://example.com/secondary)
:::
.
"url":"https://example.com/primary"The style field is always "primary" regardless of any variant parameters
on the directive.
:::footer compiles to a context block
The :::footer directive emits a context block. Context blocks render as
small text at the bottom of a message. The element inside the context block
uses the mrkdwn type.
:::footer
Sent by Acme Notifications · [Unsubscribe](https://example.com/unsub)
:::
.
"type":"context"
.
"type":"mrkdwn"
.
Sent by Acme Notifications:::callout compiles to a section with an emoji prefix
The :::callout directive emits a section block. The content is prefixed
with an :information_source: emoji shortcode (rendered by Slack as ℹ️).
:::callout
Your free trial expires in 3 days.
:::
.
"type":"section"
.
":information_source: Your free trial expires in 3 days.":::highlight compiles to a bold section
The :::highlight directive emits a section block where the entire content
is wrapped in Slack bold markers (*...*).
:::highlight
Action required: verify your email address.
:::
.
"type":"section"
.
"text":"*Action required: verify your email address.*":::fields compiles to a section with a fields array
The :::fields directive parses lines of the form Key: Value and emits a
section block with a fields array. Each field is a mrkdwn object where
the key is bolded: *Key:* Value.
:::fields
Order ID: ORD-1234
Status: Shipped
:::
.
"type":"section"
.
"text":"*Order ID:* ORD-1234"
.
"text":"*Status:* Shipped"Inline Formatting
Bold text
**bold** in the source compiles to Slack mrkdwn *bold* (single asterisks).
Slack does not use double asterisks for bold.
This is **important** text.
.
*important*Italic text
_italic_ (or *italic*) in the source compiles to Slack mrkdwn _italic_
(underscores). The mrkdwn italic marker is always underscore regardless of
which Markdown italic syntax is used in the source.
This is _emphasized_ text.
.
_emphasized_Strikethrough text
~~struck~~ in the source compiles to Slack mrkdwn ~struck~ (single tildes).
Slack uses single tildes while Markdown uses double.
This feature is ~~deprecated~~ removed.
.
~deprecated~Inline code
Inline code `code` compiles to backtick-wrapped text, which Slack
renders in monospace.
Run `npm install` to start.
.
`npm install`Links
Markdown links [label](url) compile to Slack mrkdwn link syntax <url|label>.
Visit [our documentation](https://docs.example.com) for details.
.
<https://docs.example.com|our documentation>Block-Level Elements
Thematic break compiles to a divider block
A thematic break (--- or ***) compiles to a {"type":"divider"} block.
Before the divider.
---
After the divider.
.
"type":"divider"Inline image renders as a mrkdwn link
A standalone  in Markdown is parsed as an inline image inside a
paragraph. The Slack compiler renders inline images as mrkdwn link syntax
<url|alt> inside a section block. This preserves the clickability while
being compatible with Block Kit's text-only section type.

.
"type":"section"
.
<https://example.com/screenshot.png|Product screenshot>When alt text is empty, the image renders as <url> without the label portion.

.
"type":"section"
.
<https://example.com/hero.png>:::media directive produces an image block
To render a native Slack image block (which displays the image inline), wrap
the image in a :::media directive. The image block includes image_url and
alt_text fields.
:::media

:::
.
"type":"image"
.
"image_url":"https://example.com/screenshot.png"
.
"alt_text":"Product screenshot"Unordered list compiles to a mrkdwn section
An unordered list compiles to a single section block. Each item is prefixed
with a dash (-) and the items are joined with newlines.
- First item
- Second item
- Third item
.
"type":"section"
.
- First item\n- Second item\n- Third itemOrdered list compiles to a mrkdwn section
An ordered list compiles to a single section block. Each item is prefixed
with its 1-based index followed by a period.
1. Step one
2. Step two
3. Step three
.
1. Step one\n2. Step two\n3. Step threeBlockquote compiles to a mrkdwn section with > prefix
A blockquote compiles to a section block where each inner paragraph is
prefixed with > in the mrkdwn text.
> This is important context.
.
"type":"section"
.
"> This is important context."Code block compiles to a mrkdwn section with triple backticks
A fenced code block compiles to a section block. The content is wrapped in
triple backtick fences in the mrkdwn string (Slack renders this as a monospace
code block). Language hints are ignored — Slack does not support syntax
highlighting.
```
git push origin main
```
.
"type":"section"
.
"text":"```\ngit push origin main\n```"Expression Interpolation
Data expressions resolve to their values
Template expressions {{path}} are resolved against the data object and
interpolated into the mrkdwn text. Special characters in the resolved value
(&, <, >) are escaped to &, <, > respectively to
prevent Slack from mis-parsing the mrkdwn.
Your order {{order_id}} has shipped.
.
"type":"mrkdwn"
.
Your order has shipped.(The order_id path resolves to empty string when data is null, so only the
surrounding text appears in the output.)