7. Materials and Themes
7.1 Slots as dependency injection
Section titled “7.1 Slots as dependency injection”The structure never writes a concrete block name. It carries mat_slot injection points, and a
theme binds values to slots and selectors, the way CSS or dependency injection does. That
separates structure (where the walls are) from style (which blocks, what detailing).
def cottage class=house size=9x7: floor id=floor mat_slot=floor walls id=walls class=outer mat_slot=wall height=4 roof id=roof kind=gable mat_slot=roof window id=front_windows class=small side=front y=2 repeat=2 mat_slot=glass
theme medieval: slot wall -> @cobblestone slot roof -> @spruce_stairs walls[class=outer] -> trim=@spruce_log # part detailing, via a selector (reserved) window[class=small] -> frame=@spruce_woodThe cascade. A member collects the bindings of every selector row it matches, in source order, so when two rows bind the same key the member keeps the later value. CSS applies the same rule to two rules of equal weight. Bindings are reserved (below), so today the cascade decides what a member collects and nothing that is built.
Rows whose attributes partly overlap rely on that: window[class=small,side=front] refines
window[class=small] for the members it selects, and the members only the wider row selects keep
the wider row’s binding.
Two rows that select the same members are different. Same keyword and same attributes means they
match member for member, so no member keeps the earlier row’s value of a key they both bind: that
value is dead text whatever a binding comes to mean. That is
E_DUPLICATE_SELECTOR (Lint §11.1). Sameness is by meaning: attribute
order does not count, and class= / id= / mat_slot= values compare as label text, so
window[class=small] and window[class="small"] are one selector. Rows that coincide but bind
different keys are not reported. They compose, and splitting a long binding list over two lines is
allowed.
Selector bindings are reserved. Which keys a row may bind on each keyword, and what each one
paints, is not specified yet, and no lowering reads a binding: the two selector rows above build the
same cottage as a theme without them, and no member’s block depends on them. Until that is
specified the compiler reports each binding as an unreached key, W_IGNORED_ARGUMENT
(Lint), whatever its key or value, on every row whose keyword it knows, matched or
not and in an applied theme or not. A row whose keyword it does not know gets E_UNKNOWN_KEYWORD
instead. A binding’s value is not resolved as a block either, because what the key would paint is
not specified: frame=@no_such_block is reported exactly as frame=@spruce_wood or frame=42 is,
with or without --target, and no binding makes check, compile or info refuse a build. The
rest of a row is checked as before: E_THEME_SELECTOR_UNMATCHED and E_DUPLICATE_SELECTOR apply
to it exactly as their own entries say.
def, theme, and site are unified by the same slot-bearing Component mechanism
(Components, Editing, and Multi-building).
7.2 Canonical vocabulary
Section titled “7.2 Canonical vocabulary”A theme binds canonical tokens, not raw block IDs. The backend resolves the ID, state names,
state values, and serialization per (edition, version)
(Versioning and Editions). An LLM never needs to know pillar_axis,
little-endian NBT, or Bedrock’s weirdo_direction.
Tokens come in two tiers:
| Tier | Example | What it means |
|---|---|---|
| Canonical block token | @oak_planks, @water_cauldron, @oak_log[axis=x] |
A specific meaning in Minecraft. Silent meaning-breaking downgrades are forbidden, so @water_cauldron may never become cauldron. |
| Abstract material token | @floor.wood.broadleaf, @roof.dark_wood |
An aesthetic choice. Theme policy MAY downgrade these (oak ↔ birch). |
theme cottage: slot floor -> @floor.wood.broadleaf # abstract: resolved by target and policytheme exact_oak: slot floor -> @oak_planks # canonical: pinned 1:17.3 Mappings across version and edition
Section titled “7.3 Mappings across version and edition”A canonical token absorbs five patterns. The resolution table’s structure is in Versioning and Editions.
| Pattern | Example | Policy |
|---|---|---|
| Rename 1:1 | @dirt_path (was grass_path) |
Auto-resolve. |
| Split 1:N | @cauldron[fluid=water] → water_cauldron |
Separate by meaning token. |
| Merge N:1 | @oak_slab (was wooden_slab{variant}) |
Resolve per target. |
| New | @cherry_planks |
Needs a requires >= floor. |
| Deleted | Absent in the target version | Hard error plus alternatives. |
Only ID, state, and serialization differences may be absorbed. Concept absence and game-behaviour differences are not.