Code block docs Controller
Fenced code surface with optional language chip and copy control — server-emitted chrome for docs and samples. Syntax colour is build-time token spans (Python), not a browser highlighter.
Layer: L1 surface · Recipe: unset — see docs/agent/pick-a-surface.md. Curriculum: AGENTS.md; pick matrix: docs/agent/pick-a-surface.md; blast radius: CONSUMER_MAP.md.
def greet(name: str) -> str:
"""Return a friendly hello."""
return f"Hello, {name}"
Copy this
<figure class="code" data-code data-language="python">
<div class="code__meta"><span class="code__lang">python</span><button type="button" class="code__copy" data-code-copy aria-label="Copy code to clipboard"><span class="code__copy-idle">Copy</span><span class="code__copy-done">Copied</span></button></div>
<pre class="code__pre" tabindex="0" role="region" aria-label="Python example"><code class="code__source">def greet(name: str) -> str:
"""Return a friendly hello."""
return f"Hello, {name}"
</code></pre>
</figure>Server exchange
This Hyperpart has no server exchange — it is presentation or client chrome only. htmx does not issue a request on this part's behalf. If you put an affordance (hx-*) on a control that uses this markup, that action's exchange belongs to the action, not this part. See Swap contract for host-owned envelopes.
Swap contract
Agent-visible HTMX topology (ADR-0054 / decision 0012). exchange envelope = what the response may re-emit relative to the persistent slot (body_only | outer | none | host_owned | document). dual-lock validates part markup only — not this envelope. Stem: stems/morph-safe-hypermedia.md; decision: docs/decisions/0012-swap-identity-contract.md.
No host HTMX exchange on this part — presentation or client chrome only. exchange envelope: n/a.
If a host wraps this markup in hx-*, that host owns the swap contract (sole identity + envelope). Prefer innerMorph / outerMorph for stable slots; replacement for flash; body-only responses under inner swaps.
Envelope response examples
This part has no owned exchange (envelope n/a). If a host adds hx-*, that host’s envelope applies — typically body_only:
<!-- Host wraps this presentation part with hx-* (host owns envelope) -->
<!-- Prefer: hx-swap="innerMorph" hx-target="#panel-body" -->
<!-- Server returns body_only interior for #panel-body -->
<div class="dz-stack">content…</div>
Do not re-own the slot:
<!-- WRONG: server returns the presentation root with a new id every poll -->
<div id="code-root" data-dz-region>…</div>
How to use it
Seams
- figure[data-code] → div.code__meta → pre.code__pre > code.code__source
- meta flex row: optional .code__lang, optional [data-code-copy] (CSS margin-inline-start:auto keeps copy trailing)
- build-time token spans (.code__tok--*) for Python and HTML colour
- surface uses light-dark() — follows [data-theme], not always-dark
Do / Don't
| Do | Don't |
|---|---|
| emit figure > .code__meta > (lang? + copy?) > pre > code | absolute-position the copy control or invent a one-off toolbar |
Pitfalls
- do not absolute-position .code__copy — it drifts left inside nested min-width:0 containers (Hyperpart detail pages); use the meta flex row
- do not put copy/lang as direct children of the figure without .code__meta
- do not ship a browser syntax engine for static docs — highlight at build (python + html/svg/xml only today)
- copy must read textContent (not innerHTML) so spans never paste
Keyboard / AT
- pre is a keyboard-scrollable region (tabindex=0)
- copy button is a real button with aria-label
DOM contract
What the emitted HTML must satisfy — the table is the required surface; Python under contracts/ is the package-internal dual-lock CI runs (tests/test_contracts.py), not an app route. Standalone HTMX4: implement the API so responses match this markup. Dazzle: the agent emits SSR that already satisfies it. Do not invent attrs outside these tables. For request/response wiring see Server exchange.
contracts/code.py
Required in the DOM: root [data-code] (part code). Emit only these attributes — inventing extras is fine only if controllers ignore them; omitting required ones fails CI (tests/test_contracts.py).
| Node | Attr | Constraint |
|---|---|---|
[data-code] | data-code | present (any value) |
[data-code-copy] | data-code-copy | present (any value) |
Module source
Import path is monorepo/package-local (from contracts._kit import …). Source-token form often uses data-*; gallery demos above are unprefixed. Do not copy this into app routes.
"""HYPERPART: code — fenced code surface (root + optional copy control)."""
from contracts._kit import DomContract, Node, Present
DOM_CONTRACT = DomContract(
part="code",
root="[data-dz-code]",
nodes=(
Node("[data-dz-code]", attrs={"data-dz-code": Present()}),
# Copy is optional chrome; when present the attr marks the control.
Node("[data-dz-code-copy]", attrs={"data-dz-code-copy": Present()}),
),
)
__all__ = ["DOM_CONTRACT"]
Notes
figure.code[data-code] → div.code__meta (optional lang + optional copy) → pre.code__pre → code.code__source. Copy is pushed trailing by CSS (margin-inline-start: auto on the button) — do not absolute-position it (nested part-page flex/min-width:0 chains left-shift absolute children). The gallery builder runs a stdlib highlighter (site/highlight.py) for Python and HTML into code__tok--* spans; copy uses textContent so spans never paste. Omit the lang span when there is no language; omit the copy button when display-only (keep the meta bar if a lang chip remains). Scheme: the surface follows the page theme via light-dark() (light code on light pages, dark on dark) — not always-dark, so dense docs stay scannable.Source files
One logical Hyperpart, 4 code items (CSS layered, JS bundled). Bound by HYPERPART: code — python tools/hyperpart.py code lists them.
site/registry.py · contracts/code.py · components/code.css:1 · controllers/dz-code.js