--- title: Blocks — reusable page-function compositions type: reference audience: human + agent authority: E1 architecture — the blocks tier: what it is, the canon-vs-block admission rule, the B contract, the promotion path status: current --- # Blocks A **block** is a named composition of canon components performing a **page function** — a sticky site header, a hero, a pricing section, an app shell. It sits ABOVE the UIX layers and OUTSIDE the component canon, at `src/uix/blocks/` (alias `$blocks`). It consumes the framework; the framework never references it. The live inventory is the `src/uix/blocks/` tree — never a list in a doc. Blocks exist so that **assembly knowledge does not dilute the canon**: the catalog's value is contract density (events, ARIA, keyboard, tokens other layers consume); a block's value is _correct assembly_ — layout, landmarks, heading hierarchy, responsive behavior, content slots. A block brings no new primitives; it brings the right way to put existing ones together. The tier map, for orientation: | Tier | Value | Contract | Enters through | | ------------------------------ | ------------------------------- | ------------------------------------- | --------------------------------------------------------------------------- | | **Canon** (`src/uix/`) | contract density others consume | morfo + acceptance matrix | the 9-phase route ([`building-a-component.md`](../building-a-component.md)) | | **Packs** (`src/packs/`) | parameterized leaf decoration | P contract ([`packs.md`](./packs.md)) | `packs:check` | | **Blocks** (`src/uix/blocks/`) | page-function composition | B contract (below) | `blocks:check` | ## The admission rule (canon vs block) > **Canon** = anything with contract surface — a real event, a state machine, > a `data-*` attribute CSS needs to select, a widget a11y obligation. It is > built FIRST as a canonical component through the 9-phase route; the block > then composes it. > > **Block** = composition only. If building a block reveals a missing > behavior, that is a **gap to flag and promote** — never a privilege to grow > inside the block. What a block DOES own semantically: **landmarks and document structure** (`header/nav/main/aside/footer`, heading hierarchy, skip links, region labels). Components cannot know page context; blocks can. That is their only a11y surface of their own. A block MAY hold local view-state (the active tab, a monthly/yearly toggle) through the public props/events of the components it composes. What it may NOT do is materialize that state with DOM/CSS of its own that would need contract — that is the admission rule firing. ## Coordination: a block owns its section's state, shape and words > Decision of 2026-07-31, from the tier's own evidence: **«if in the end it is > a list of components… the block is coordination, state, and data too».** Composition alone is not a floor high enough. When the state of a section lives OUTSIDE the block, every consumer re-derives it at the point of use — each `disabled` is its own expression — and the result is **holes**: an action that cannot be taken and nothing on screen saying why. That is not a block; it is a grid with instructions. So a block that has a real state — one where something can be blocked, in flight, or done — owns three things: 1. **The state of its section**, as ONE derived machine, exhaustive by construction. Its parts READ it (through context); none recomputes it and none invents a `disabled` of its own. Exhaustive means a `Record` that forces a sentence for every blocked state — a mute state becomes impossible to add, instead of merely discouraged. 2. **The default shape of its data** — the schema the section asks for. A contact section that cannot say what a contact form asks is a grid, not a block. The app replaces it by passing its own. 3. **The words of the states it owns**, as idlangrefs through the translator (`#?blocks..|English fallback`) — the same door the canon uses for its own `texts:`. Register nothing and the English fallback shows; an app takes the words over by registering the `blocks` namespace in its langs schema. **This does not apply to every block.** Most are layout: `hero`, `cta`, `feature-grid`, `feature-split`, `stats-band`, `testimonials`, `faq`, `content-section` place content and own nothing. There is nothing to coordinate there, and inventing state for them would be the opposite mistake. The rule fires where a section can be blocked or in flight — `contact` is the worked example (`src/uix/blocks/contact/state.ts`). The admission rule still governs: coordination is derived state in TypeScript, not new DOM or CSS. The moment a block wants a `data-*` attribute for CSS to select, or an event with a semantic family, that is canon surface and it is promoted — not grown inside the block. ## Hard boundaries 1. **Dependency direction is one-way**: `src/uix/blocks/*` may import `$uix`, `$adom`, the public arts and `$libs/forms` (the door to `createForm`, which the canon `Form` does not re-export and soma consumes the same way — sanctioned 2026-08-05, when the audit found the contract and the code disagreeing about `contact`); nothing in the canon (`src/uix/{morfo,soma,sema,eidos,active-uix,langs}`) nor in `src/{arts,libs,packs}` may import from `src/uix/blocks/`. Deleting the tier must leave `npm run check` green — blocks is a tier under `src/uix/`, not a fifth layer. 2. **No morfo, no matrix**: a block declares no morfo, ships no sema pack, and takes no row in `component:audit`. Its quality floor is the B contract below, guarded by `blocks:check`. 3. **Layout-components-first**: layout is composed from the canon layout components (`Container / Section / Stack / Flex / Grid / AutoGrid / Wrap / Group / Separator / AspectRatio / Surface`) and their props. A block ships no `.css` file; a scoped `