[@layout]: ../docs.layout.md

# Blocks

A Block is a meaningful section of a Source, separated by a `---` line. It keeps
one idea, example, or task step together and can have its own identity, data,
and layout.

The [methodology](../methodology.md) connects readable files into paths. Blocks
apply that attention to the sequence within a page.

In [Player](../learning/player.reference.md), Blocks and Beats choreograph learning
and focus. A Block frames a topic or activity; Beats guide attention through its
text, media, and interactions. Player derives Beats from normal content, and
components can provide explicit steps such as books or chess moves.

Headings organize the text within a Block. Add a separator when the reader moves
to a new topic or activity.

---

## Choose a useful boundary

A good Block makes sense as a unit: an explanation with its example, a task with
its instructions and expected result, or a question with the context needed to
answer it.

- **Guides:** give each substantial step a Block. Keep its command, explanation,
  and expected output together.
- **References:** group related syntax and examples. Start a new Block for a new
  concept or contract; keep short notes and related links with their topic.
- **Lessons:** separate the explanation, worked example, and practice activity
  when each is a distinct part of the learning sequence.
- **Component sources:** keep each component's explanation, implementation
  fences, and usage example in its defining Block. Put a full composition example
  in a separate Block at the end.

Use enough context to make each Block understandable. A short page may need only
one Block. Split a long Block where the reader's task changes, rather than at a
fixed word count or every paragraph.

---

## Write a sequence

This page has an introduction, an example, and a practice activity. The example
keeps its explanation and code together. The practice Block has a stable id so
another page can link to it as `lesson.md#practice`.

Block IDs and heading IDs share the page’s fragment namespace. Block IDs take
priority: a heading named “Practice” inside a Block with `id: practice` receives
`practice-1`. Repeated headings also receive an available numbered suffix.
Explicit HTML IDs must also be unique. Do not repeat `id="practice"` on a heading
inside that Block; the Block already supplies the fragment target. Compilation and
lint diagnose collisions in compiled Block HTML and identify both elements and
their owning Blocks.

````md
# Your first function

A function names a reusable operation. We'll write one, then change it.

---

## Greet a reader

This function returns a greeting using the name you pass in.

```js
function greet(name) {
  return `Hello, ${name}!`
}

greet("Ada") // "Hello, Ada!"
```

---

<!-- id: practice -->

## Try another name

Call `greet` with your own name. The returned greeting should include it.
````

---

## Blocks and Player flow

Player moves through **Beats** within each Block: headings, paragraphs, list
items, code, and other meaningful elements. A Block can contain several Beats.
Interactive components can define their own Beats, such as positions in a chess
replay.

Focus guides attention through those Beats. Slides uses Blocks as presentation
sections, with Beats providing the steps inside each section. A heading alone
adds structure within the current Block; `---` starts the next section.

After a substantial edit, read the page normally, then try Focus and Slides.
Check that each section has enough context, related code and explanation stay
together, and long sections have useful stopping points. Keep large tables and
code examples intact when readers need to examine them as a whole.

Try the [Player example](../../examples/play.demo.md) to see the same Source in
reading, Focus, and Slides modes.

---

## Identity, data, and layout

Place Block topmatter in an HTML comment at the start of a Block:

```md
---

<!--
id: key-idea
layout: center
-->

## One idea at a time

A Block gives related content a shared identity and layout.
```

Keep a first Block's metadata comment before link definitions such as `[@layout]`
and `[@origin]`. Definitions placed above the comment make it ordinary body content,
so its `id` and `type` are not Block metadata. For example:

```md
<!-- id: overview
 type: summary -->

[@layout]: ./site.layout.md
[@origin]: ./template.page.md

# My page
```

Keep Source-wide definitions in an introductory Block that save Actions do not
replace. Definitions at the end of a file belong to its last Block's body.
`tx.source(id).updateBlock(blockId, { body })` replaces that entire body, including
its definitions. A data-only update preserves the body; a body replacement must
retain any definitions it still needs. Updating Source frontmatter with `data()`
re-serializes the frontmatter, so YAML comments and flow-list formatting are not
preserved as an editing contract.

Use explicit ids for Blocks that other pages link to, include, or reference for
progress. Keep those ids stable as the content moves or changes. Positional ids
are useful for ordinary rendering but change when earlier Blocks are inserted.

| Syntax                           | Meaning                                                                         |
| -------------------------------- | ------------------------------------------------------------------------------- |
| `---` with blank lines around it | Starts a new Block. Separators inside code fences are examples, not boundaries. |
| `<!-- id: x -->`                 | One-line topmatter at the start of a Block.                                     |
| `<!--` YAML map `-->`            | Multi-line topmatter. The first line must be a `key:` pair.                     |

| Topmatter | Meaning                                                         |
| --------- | --------------------------------------------------------------- |
| `id`      | Block id, addressed as `file.md#id`. Defaults to its position.  |
| `type`    | Block type for queries. Defaults to `block`.                    |
| `layout`  | `center`, `wide`, `media-start`, or `media-end`.                |
| `sticky` | `true` keeps the opening Block visible while reading; defaults to `false`. |
| any other | Authored data, available to `blocks` queries as `where` fields. |

Frontmatter at the beginning of a Source describes the whole page; its `---`
delimiters are not Block boundaries. Heading ids stay unique across the Source.
`pmx lint` reports invalid topmatter YAML and unknown layouts.

A `media-start` or `media-end` Block places a standalone image, video, iframe,
or `figure` beside its prose. Wrap an illustrated component and its caption in
`figure` to use the same layout. The media spans the rendered prose rows, so
spacing adapts when you add or remove paragraphs.

The [scroll storytelling example](../../examples/scroll-story/story.demo.md)
combines this layout with a literate SVG component and progressive entrances.

---

## Groups and displays

Consecutive Blocks can share a `group`. Put `display` on its first Block to choose
an installed Plugin's presentation:

```md
<!--
group: install
display: tabs
title: Bun
icon: terminal
-->

## Install with Bun

Run `bun install`.

---

<!-- group: install -->

## npm

Run `npm install`.
```

Install Tabs with `bun run pmx plugins add tabs`. The two Blocks become panels
labelled Bun and npm. The first opens by default. Each panel keeps its authored
heading, content, and `layout`. A group's name is local to its Source; an unmarked
Block or different name ends the run. Use a new name for another group.

Only the first member declares `display`. Move that declaration if you move or
delete the first member. Without a display, the group stays a normal sequence.
`pmx lint` reports interrupted names, misplaced displays, and unavailable displays.
Malformed groups remain readable while editing.

Display labels use Block metadata: `title` overrides the first heading. Block
`description` and `image` likewise override their first paragraph and image.
These values do not inherit from Source frontmatter. Tabs accepts an optional
shared `icon` name and requires a title or heading for each member.

Tabs supports arrow keys, Home/End, and ordinary keyboard focus. Switching panels
preserves mounted content. Explicit Block IDs keep selection stable across live
reordering. A Block or heading link opens the containing panel before scrolling.
Tab clicks do not add history entries or save personal state.

Print and no-JavaScript reading show the complete sequence. Focus and Slides
present every member as a normal Block. A whole-Source include preserves groups
inside its own boundary; a single-Block include stays standalone. Included Block and Markdown-heading
IDs are scoped to the occurrence so repeated groups do not collide.

Plugins can contribute other [Block displays](../extend/plugins.guide#add-a-block-display).
Grouping does not impose Tabs' selection or Player behavior on other displays.

---

## Sticky opening Block

Keep a compact Source header visible while the rest of the document scrolls:

```md
<!-- sticky: true -->

# Changes log

Changes to PathMX, most recent first.

---

## Latest update

The entry goes here.
```

The whole opening Block stays beneath the site header, within the Source’s
column. Links to later Blocks and headings leave room for it. Keep the opening
content brief, especially for phones; it does not collapse into a smaller title.
`sticky` composes with `layout` and accepts only `true` or `false`.
Only the opening Block may opt in. Included Blocks do not bring sticky behavior
into another Source. Focus, Slides, and print present it as an ordinary Block.

---

## Related

- [Sources](./sources.reference.md) · [Queries](./queries.reference.md)
- [Block layouts](../../examples/block-layouts.demo.md) · [Components](./components.reference.md)

---

## Conditional Blocks

Use `when:` in Block topmatter to make a Block depend on another Block's output.
References use `$block-id.output-name`; each listed condition must match.

```yaml
id: preparation
when:
  $approach.mode: { in: [solo, together] }
  $rating.score: { gte: 4 }
```

Scalar values mean equality: `$approach.mode: solo` is equivalent to
`$approach.mode: { eq: solo }`.

| Comparison | Meaning |
| --- | --- |
| `eq: value` | Equal to the scalar value |
| `in: [value, value]` | Equal to one of the listed scalar values |
| `contains: value` | Array includes this scalar value |
| `containsAny: [value, value]` | Array includes at least one listed scalar value |
| `lt: number` / `lte: number` | Less than / less than or equal to the number |
| `gt: number` / `gte: number` | Greater than / greater than or equal to the number |

Each comparison object contains one operator. `in` lists must be nonempty. An empty `containsAny` list never matches.
Array comparisons use strict scalar equality and do not match scalar outputs;
`in` still compares a scalar against alternatives.
Numbers, strings and booleans retain their types: the string `"4"` does not satisfy
`gte: 4`. Numeric operands must be finite. Missing output never matches; an explicit
`null` can match scalar equality or `eq: null`.

An inactive Block contributes no rendered content, active outputs or completion
requirements. Its saved answers remain available if the Block becomes active again.
Circular Block dependencies are rejected.
