Attribute Cascade Spec
Defines conformance examples for the attribute resolution order. The canonical cascade is documented in brand.md § 9. In short (highest priority first):
- Inline attrs (
{key="value"}on the element) - Template frontmatter
brand:partial override - Named styles (
{.name}) - Per-component defaults (
brand.defaults.<type>) - Compiler inference (e.g. images in flush sections, text in dark bg)
- Brand theme tokens (colors, typography, including
element-typography) - Built-in compiler defaults
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals, as shown here.
The cascade described here applies to block-level attributes ({key="value"}
on directives, headings, and inline blocks). Push frontmatter platform
keys (ios:, android:, web:) follow a separate interpolation rule —
see push.md § Frontmatter interpolation.
Implementations MUST interpolate string values inside those blocks against
runtime data; non-string scalars MUST pass through unchanged.
Per-Component Defaults
The brand.defaults: block in frontmatter sets default attributes for element
types. These MUST be applied automatically to every instance of that element
type unless overridden by inline attributes, named styles, or compiler
inference (in that resolution order). The same shape lives at
defaults: in brand.yaml itself; the per-template frontmatter override
nests under brand: to keep one canonical vocabulary.
Image defaults
---
subject: Test
brand:
defaults:
image: { padding: "0" }
---

.
<mj-image src="https://example.com/img.jpg" alt="Hero" padding="0px" border="none" />Button defaults
---
subject: Test
brand:
defaults:
button: { borderRadius: "0" }
---
[Click](https://example.com){button}
.
border-radius="0px"Inline attrs override defaults
---
subject: Test
brand:
defaults:
image: { padding: "0" }
---
{padding="spacious"}
.
padding="40px 32px"Named Styles
The brand.styles: block defines named attribute bundles applied via
{.name}. Same authoring shape as styles: at the brand.yaml root.
Basic style application
---
subject: Test
brand:
styles:
headline: { size: "4xl", weight: "bold", align: "center" }
---
THE WAIT IS OVER
{.headline}
.
font-size:36px
.
font-weight:700
.
text-align:centerStyle composability
Multiple styles MUST merge left-to-right. The later style MUST win on conflicts.
---
subject: Test
brand:
styles:
big: { size: "4xl" }
centered: { align: "center" }
---
Hello
{.big .centered}
.
font-size:36px
.
text-align:centerInline attrs override styles
---
subject: Test
brand:
styles:
headline: { size: "4xl", align: "center" }
---
Hello
{.headline size="sm"}
.
font-size:14px
.
text-align:centerStyles above defaults in cascade
---
subject: Test
brand:
defaults:
text: { align: "left" }
styles:
centered: { align: "center" }
---
Hello
{.centered}
.
text-align:centerCompiler Inference
Padding propagation — images in flush sections
Images inside a section with padding="none" inherit zero padding.
---
subject: Test
---
::: columns {padding="none"}
::: col

:::
:::
.
padding="0"Override still works:
---
subject: Test
---
::: columns {padding="none"}
::: col
{padding="spacious"}
:::
:::
.
padding="40px 32px"Dark background text color
Text in dark background sections defaults to white.
---
subject: Test
---
::: columns {bg="#000000"}
::: col
Hello dark world
:::
:::
.
color:#ffffffExplicit color overrides inference:
---
subject: Test
---
::: columns {bg="#000000"}
::: col
Hello dark world
{color="secondary-text"}
:::
:::
.
color:#71717aSection fusion — {flush}
Adjacent sections with {flush} are wrapped in <mj-wrapper>.
---
subject: Test
---
::: columns {bg="#f9f9f9" flush}
::: col
First section
:::
:::
::: columns {bg="#000000" flush}
::: col
Second section
:::
:::
.
<mj-wrapper
.
First section
.
Second section