Code Blocks

In the CMS this is the Code Block control (Mod-Shift-{), which inserts a block with a language picker in its corner. This page is the markdown it writes, what the build turns that into, and where the colors come from. Code blocks, every bundled language and every code theme are available on all plans; the one thing on this page that is not is adding a highlight.js language of your own, and it is badged where it appears.

Fenced code blocks

Three backticks, a language identifier, your code, three backticks.

```python
squares = [x * x for x in range(10)]

### How it renders {data-id="aa1ctkse"}

```python {data-id="z69dtj9m"}
squares = [x * x for x in range(10)]

Omit the language and the block still renders as preformatted text, but the highlighter is left to guess the grammar from the content — which it will sometimes get wrong in a way that looks like a bug. Name the language whenever there is one, and use a fence with no language deliberately for things that are not code: terminal transcripts, log excerpts, plain output.

2026-09-01T12:00:00Z build complete in 41.2s

To show three backticks inside a block — as every example on this page does — open the outer fence with four:

````markdown
```python
squares = [x * x for x in range(10)]
```

## What the output looks like {data-id="eauxbx6d"}

```html {data-id="nm1iydi9"}
<pre><code class="language-python">squares = [x * x for x in range(10)]</code></pre>
```

| Part | Example | Where it lands in the HTML |
|----------|----------|----------|
| Fence | ```` ``` ```` | becomes the `<pre>` and the `<code>` inside it |
| Language | `python` | `class="language-python"` on the `<code>` |
| Attributes | `{#install-example}` | on the `<pre>` — never on the `<code>` |
| Content | your code | the text of the `<code>`, HTML-escaped |
{data-id="zfsk20jw"}

Two details worth knowing. Attributes always move to the `<pre>`, so a stylesheet or a script that targets your fence attribute has to look at the wrapper, not the code. And a trailing newline inside the fence is stripped, so a block never ends in a blank line you did not intend. {data-id="yps99fvm"}

On documentation pages the build also wraps each block in a `div.code-block-wrapper` and adds a copy-to-clipboard button. You get that for free; there is no markup to write for it. {data-id="p1mh617u"}

## Inline code {data-id="mig5qjsg"}

Single backticks inside a sentence: {data-id="cacwxgzk"}

```markdown {data-id="xbd097l4"}
Run `make build` before deploying.
```

### How it renders {data-id="6i29jlv0"}

Run `make build` before deploying. {data-id="ldff88c0"}

If the snippet itself contains a backtick, use two backticks as the delimiter and leave a space on each side of the content: {data-id="po9s9oy9"}

```markdown {data-id="zmq68pm0"}
The shell test is `` a`b `` and nothing else.
```

### How it renders {data-id="clk6kufv"}

The shell test is `` a`b `` and nothing else. {data-id="hccj0gkg"}

The spaces are part of the delimiter, not part of the snippet: one leading and one trailing space is stripped, so the rendered `code` element holds `` a`b `` and nothing more. {data-id="dteqakxa"}

:::note Inline code takes no attributes {data-id="vqcveo7z"}

Braces written after a closing backtick — `` `make build`{#build-cmd} `` — are silently dropped. The braces disappear from the text and no attribute is set, on the published site and in the editor alike. There is no working form; if you need an anchor or a class on a snippet, put it on the paragraph or use a fenced block.

:::

| Feature | Fenced block | Inline |
|----------|----------|----------|
| Language class | `class="language-x"` on the `<code>` | none |
| Attributes accepted | Yes, on the opening fence | **No** |
| Syntax highlighting | Yes | No |
| Copy button | Yes, on documentation pages | No |
| Editor control | Code Block (`Mod-Shift-{`) | the Code mark |
{data-id="6buzhhm7"}

## Fence attributes {data-id="osuityrb"}

Attributes go on the opening fence, after the language. Unlike tables, lists and blockquotes, a fence reads the general attribute syntax, so `.class` and `#id` shorthand both work: {data-id="suuu4pvo"}

```markdown {data-id="tw4epz1a"}
```python {#install-example}
pip install leed
```
```

```html {data-id="hs3b063r"}
<pre id="install-example"><code class="language-python">pip install leed</code></pre>
```

Which forms are accepted where is the subject of [Attributes](pageid:96da6677-6331-4337-8b63-1f7e8732c47b) — a fence is one of the permissive cases, and it is worth knowing that before you copy the same brace onto a table. {data-id="kfotmwp3"}

## Where the highlighting happens {data-id="kf0mz6qy"}

Nothing is highlighted at build time. The build detects whether a page contains a `<pre>` with a `<code>` in it and, only for those pages, injects highlight.js into the `<head>` from cdnjs. In the reader’s browser, `hljs.highlightElement` then runs over every `pre > code` on the page, skipping any element with the class `math`. {data-id="v96i251f"}

Three consequences follow: {data-id="yg3nxpdf"}

- **A page with no code loads no highlighter.** The library is per-page and content-detected, not site-wide, so prose pages are not paying for it. {data-id="v0t8ky0q"}
- **The language class is the instruction.** `class="language-python"` tells highlight.js which grammar to use. With no class it falls back to auto-detection.
- **The site’s language coverage is wider than the editor’s picker.** The picker in the CMS lists fourteen languages, and the browser bundle registers roughly three dozen. A fence labeled with a language highlight.js knows will highlight on the published site whether or not it appears in the dropdown.

![The code block's language dropdown open in the editor, showing custom languages above the built-in list =172x458](/cdn-cgi/imagedelivery/CkSmQAGqpZ-mcWDDI6mu0w/2e08cf89-23ec-4fa9-e1fe-896c2ef5fe00/original){data-id="0snmba2i" data-assetid="cmnovhyx"}

## Choosing a code theme {data-id="prigcqla"}

The colors themselves are a documentation-set setting rather than anything in the markdown: you pick a code theme alongside the layout and color theme, and it applies to every code block on the site. [Themes, Fonts and Code Themes](pageid:92000d4b-eca4-4a89-bfa8-f16da9801a13) is where that choice lives, including the built-in themes and what it takes to write your own. Custom code themes are gated by plan; that page carries the current tier, and [Feature Availability by Plan](pageid:91a23ae6-1f36-44de-8b22-39e863397e3e) is the table of record for every gated feature. {data-id="vc1chssj"}

## Adding a language Leed does not ship {data-id="toswuddz"}

:::info Plan: Enterprise {data-id="klcwbn2p"}

Adding a highlight.js language is an Enterprise feature (`customSyntaxLanguages`). Code blocks, every bundled language and every code theme are free on all plans — this section is about extending the highlighter with a grammar the standard bundle does not include.

:::

Three steps, in this order: {data-id="1n0eipds"}

1. **Put the language file in your repository** at `static/js/hljs-<language>.js`. It is an ordinary highlight.js language module — the file the highlight.js project publishes for that grammar, which registers itself against the global `hljs`. {data-id="bjlek1zg"}
2. **Add the name under Settings → General**, in the **Custom Highlighter Syntax** field. The name must match the file: `hljs-nix.js` means adding `nix`. Names are letters, digits, hyphens and underscores, up to 32 characters, and must start with a letter or digit.
3. **Publish.** The next build looks for the file, fingerprints its contents into the script URL so a changed grammar is not served from cache, and adds it to the highlighter bundle for every page on the site that contains code.

| Step | Where | What can go wrong |
|----------|----------|----------|
| Add `hljs-<language>.js` | your site repository, under `static/js/` | The name has to match exactly. `static/javascript/` is a different folder and will not be found. |
| Add the name | Settings → General → Custom Highlighter Syntax | The field is visible on every plan, but the save is refused below Enterprise. See the warning below. |
| Publish | the normal publish and build | If the file is missing the build **warns and carries on** — the site ships and the language falls back to auto-detection. Check the build log rather than the page. |
{data-id="49lm2ng7"}

![The Custom Highlighter Syntax field on Settings → General, with languages added as chips =1492x174](/cdn-cgi/imagedelivery/CkSmQAGqpZ-mcWDDI6mu0w/a9733288-5157-4cfb-d91e-a7938156bf00/original){data-id="g0f6jcru" data-assetid="sohtdbnz"}

:::warning The field is visible below Enterprise; the save is not {data-id="xpjp3was"}

Nothing hides the Custom Highlighter Syntax input on a lower plan. You can type a language into it, and the request to save it is rejected server-side with the standard upgrade-required response. If a language you added does not appear after a reload, this is why. [When a Feature Is Gated](pageid:d1f01c61-21c3-41cb-8108-ba1266baaf0f) describes what that response looks like and what to do about it.

:::

Two behaviors make this safer than it sounds. **Adding** a language is what is gated — a company that already has languages configured keeps them, keeps loading them in the editor and keeps building with them if it later drops below Enterprise, and can still reorder or remove them. And a language you have added shows up **above** the built-in list in the code block’s language picker, so the two sources stay visually distinct. {data-id="snwb12zc"}

The `static/js/` folder these files live in is served with immutable caching, which is exactly why the build appends a content fingerprint to the URL; [Static Files and Caching](pageid:e42d5bac-a013-468b-b32a-cffc230cd644) explains the rules that apply to everything else you put there. {data-id="hnja3d2p"}

## Two fence languages that are not code {data-id="x0txlk2m"}

:::tip `math` and `mermaid` are handled before the highlighter {data-id="oa2d4dyj"}

A fence labeled `math` renders as a typeset equation and one labeled `mermaid` renders as a diagram. Both use the fence syntax on this page — including fence attributes — but neither is highlighted, and each has its own rendering library injected only into pages that use it. [Math](pageid:ff535c8f-0c87-45b6-b7fc-19ffd2411e7a) and [Diagrams](pageid:2b8f2a8c-ec69-443b-a05a-2ea8601c49b7) cover them.

:::

If you are producing a highlighted block from a Handlebars template rather than from page content, the escaping rules are different and are covered by [Template Formatting Reference](pageid:fadb2795-b459-41d9-8d59-0b10942ea092). {data-id="wbx352vc"}
ESC