Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions assets/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,12 @@ if (headings.length > 0 && tocLinks.length > 0) {

// ── Code copy buttons ──
// Goldmark wraps highlighted code in .highlight > pre; plain code is just pre.
// Code blocks with a title or line highlights render with Chroma's
// table-mode line numbers, which emits a second <pre> for the gutter
// column (no <code> inside). Skip it so it doesn't get its own (broken —
// it would copy line numbers, not code) button.
document.querySelectorAll('.prose pre').forEach(pre => {
if (!pre.querySelector('code')) return;
const btn = document.createElement('button');
btn.className = 'code-copy';
btn.textContent = 'copy';
Expand Down
66 changes: 66 additions & 0 deletions assets/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,72 @@ body {
margin: 0;
}

/* ── Code block: title bar and line highlighting (#6) ── */
.prose .code-block {
margin: 1.25rem 0;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
background: var(--code-bg);
}

.prose .code-block .code-title {
font-family: var(--font-mono);
font-size: 0.7rem;
font-weight: 500;
letter-spacing: 0.04em;
color: var(--text-2);
background: var(--bg-alt);
padding: 0.45rem 1.5rem;
border-bottom: 1px solid var(--border);
/* show the user a full filename even when the underlying string is long */
word-break: break-all;
}

.prose .code-block .code-body {
/* reset the outer .prose pre margin so the border doesn't double up */
margin: 0;
}

/* Chroma's line numbers are rendered inline (WithLineNumbers, no
LineNumbersInTable) — the "chroma" class lands on the <pre> itself, not
on a wrapping <div>, so there's a single element to reset here. */
.prose .code-block .code-body .chroma {
margin: 0;
border: 0;
border-radius: 0;
background: transparent;
}

/* highlighted lines: a soft accent wash that works in both themes.
Chroma's own generated CSS already sets `.chroma .line { display: flex }`
(see chroma.css) so every line is a full-width flex row — we only need
to add the background on top of that, not redeclare layout. Overriding
display here (e.g. inline-block) breaks Chroma's layout for consecutive
highlighted lines.

The wash bleeds into .prose pre's own 1.5rem horizontal padding (same
negative-margin/padding technique mkdocs-material's .hll and VitePress's
.highlighted use) so it reaches the card's edges instead of stopping at
the code text's own inset.

Selector is deliberately as deep as the real DOM nesting (.prose
.code-block .code-body .chroma .highlight-line — 5 classes): Chroma's
own light-theme .hl rule is now scoped under :root:not([data-theme=
"dark"]) (see ChromaCSS), which makes it 4 classes deep, so anything
shallower than 5 here would lose to Chroma's grey and never show. */
.prose .code-block .code-body .chroma .highlight-line {
background: var(--accent-dim);
margin: 0 -1.5rem;
padding: 0 1.5rem;
}

[data-theme="dark"] .prose .code-block .code-body .chroma .highlight-line {
/* The light-theme accent-dim is too saturated for dark mode — dial
it back so token colours stay legible on top of it. */
background: rgba(96, 165, 250, 0.08);
}

.code-copy {
position: absolute;
top: 0.6rem;
Expand Down
81 changes: 81 additions & 0 deletions docs/guide/03-markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,84 @@ dark_code_theme = "github-dark"
```

Any [Chroma style](https://xyproto.github.io/splash/docs/) is valid. Light and dark themes are emitted as separate CSS classes, toggled by `[data-theme]` on `<html>`.

### Filename titles

Add `title="…"` to the opening fence to render a filename label above the block:

```go title="cmd/root.go"
package main

func main() {}
```

Renders as a `<figure class="code-block">` with a `<div class="code-title">cmd/root.go</div>` header bar.

**Syntax:**

````markdown
```go title="cmd/root.go"
package main
```
````

The title can be wrapped in either single or double quotes. Use the opposite quote style if the title itself contains quotes (e.g. `title='has "quotes" in it'`).

### Line highlighting

Add `{n,m-p,…}` to the opening fence to highlight specific lines (1-indexed, counted from the first line of the block). Use a comma to list individual lines, and a hyphen to specify a range:

```go {2,4-6}
line 1
line 2
line 3
line 4
line 5
line 6
```

Each highlighted line gets a `<span class="highlight-line">` class with a soft background wash and an accent bar to its left.

**Syntax:**

````markdown
```go {2,4-6}
line 1
line 2
line 3
line 4
line 5
line 6
```
````

### Combining both

The two attributes can be combined in any order:

```python title="example.py" {1,3-4}
print("a")
print("b")
print("c")
print("d")
```

**Syntax:**

````markdown
```python title="example.py" {1,3-4}
print("a")
print("b")
print("c")
print("d")
```
````

### Behaviour notes

- A plain code block (no `title=` or `{}`) is rendered exactly as before — the new extension is a no-op for the common case.
- Empty `title=""` is treated as no title.
- Invalid range tokens (`{notanumber}`) are silently ignored; the rest of the expression still applies.
- Out-of-range line numbers are silently ignored — no error, no broken page.
- Reverse ranges (`{5-2}`) are silently ignored.
- D2 diagram blocks (```` ```d2 ````) are caught by the D2 extension before the code-attribute one sees them; adding `title=` or `{}` to a D2 block does not change its behaviour.
6 changes: 6 additions & 0 deletions internal/parser/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@
//
// - D2 diagrams: fenced code blocks with language "d2" are compiled to
// inline SVG pairs (light + dark) using the D2 Go library.
//
// - Code-block title and line highlighting: fenced code blocks accept
// title="…" and {n,m-p} attributes on the opening fence. The title is
// rendered as a label bar above the block; the range is applied as a
// class="highlight-line" on the matching lines. Implementation lives in
// highlight.go (NewCodeAttrsExtension).
package parser
Loading
Loading