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
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)]
```
````When the editor writes a page body it does this for you: a fence is always one backtick longer than the longest run of backticks in the code, and at least three, so code that holds a fence of its own never closes the block early. When the language itself holds a backtick, the editor writes a ~~~ fence instead. Any CommonMark fence reads back, ~~~ included.
What the output looks like
<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 |
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.
In a CMS page body a code block cannot sit inside a list item. On import it is lifted out to directly after the outermost list, in order, with nothing lost; the editor will not insert one there either. Indent a fence under a list item only in template markdown.
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.
Inline code
Single backticks inside a sentence:
Run `make build` before deploying.How it renders
Run make build before deploying.
If the snippet itself contains a backtick, use two backticks as the delimiter and leave a space on each side of the content:
The shell test is `` a`b `` and nothing else.How it renders
The shell test is a`b and nothing else.
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.
| Feature | Fenced block | Inline |
|---|---|---|
| Language class | class="language-x" on the <code> | none |
| Attributes accepted | Yes, on the opening fence | Read, but never rendered |
| Syntax highlighting | Yes | No |
| Copy button | Yes, on documentation pages | No |
| Editor control | Code Block (Mod-Shift-{) | the Code mark |
Fence attributes
Attributes go on the opening fence, after the language. Unlike tables, lists and blockquotes, a fence reads the general attribute syntax, so the .class shorthand works. An id does not: a {#install-example} is dropped like any user-written id. And in a CMS page body a code block keeps only its language and its data-id, so a class renders when the markdown comes from a template, but in a page body validation reports it as dropped:
```python {.install-example}
pip install leed
```<pre class="install-example"><code class="language-python">pip install leed</code></pre>Which forms are accepted where is the subject of Attributes — a fence is one of the permissive cases, and it is worth knowing that before you copy the same brace onto a table.
Where the highlighting happens
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.
Three consequences follow:
- 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.
- 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. The highlight.js bundle a page loads carries its 36 common languages, and the build also reads the
language-*classes on each page and adds the grammar file for any other language highlight.js publishes —hbs,dockerfile,nginx,tomland the rest — to that page alone. A fence labeled with any language highlight.js knows, or one of its aliases, therefore highlights on the published site whether or not it appears in the dropdown, and only the pages that use an extra language pay for its grammar.
Choosing a code theme
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 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 is the table of record for every gated feature.
Adding a language Leed does not ship
You need this only for a grammar highlight.js does not publish itself. Every language in highlight.js’s own distribution is already loaded on demand, as described above, so it needs no file and no setting.
Three steps, in this order:
- 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 globalhljs. - Add the name under Settings → General, in the Custom Highlighter Syntax field. The name must match the file:
hljs-nix.jsmeans addingnix. Names are letters, digits, hyphens and underscores, up to 32 characters, and must start with a letter or digit. - 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. |
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.
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 explains the rules that apply to everything else you put there.
Two fence languages that are not code
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.