Date: March 29, 2026
Author: Kirk Siqveland / CyberTamer / AIKode
Status: Working Draft
Previous: Rich-Render Text Format v0.5 (March 26, 2026)
RichRender is a compact binary-compatible format for styled text. It extends UTF-8 with inline rendering metadata, replacing verbose HTML markup with small, efficient style descriptors called Style-Pips.
.pip hybrid document model allows HTML and RichRender to coexist, with RichRender handling styled content regions where markup overhead is highest.RichRender is designed as a method to extend HTML, providing compression and improved style clarity and conformance within the existing HTML ecosystem.
RichRender is the canonical format at the center of a three-format ecosystem:
.mdr) — human authoring and reading format. Markdown extended for rich-text display, with 1:1 mapping to RichRender..pip) — the core binary format. The Rich-Render Text Format. The canonical source of truth for styled content..html) — RichRender inflated to HTML. Not the full breadth of HTML, only the rendered version of what RichRender expresses. Browser and email client output.Libraries provide paired 1:1 conversions between these formats:
The format works comfortably at all three levels according to the user's needs, while remaining a single uniform format.
RichRender is one format at four depths. Each tier is a superset of the one below. The continuation flag chain makes this work — each tier reads one more byte when it is ready to.
Style-Pip — The style descriptor bytes that define visual formatting (bold, italic, color, font, etc.). Bytes one through three are spec-defined; additional bytes are preamble-defined via bitmap declarations. The Style-Pip is the atomic unit of visual style in RichRender. Each pip is a complete style declaration — it describes the full rendered state, not a delta from the previous style.
Style span (STX/ETX, 0x02/0x03) — A frame that applies a Style-Pip to a run of text. Visual formatting. STX opens a span and implicitly closes any previous style; ETX is used exclusively for nested close (see Section 3.5).
Inline element (SO/SI, 0x0E/0x0F) — A frame that wraps content in a semantic HTML inline element (code, kbd, em, strong, etc.).
Block element (SOH/EOT, 0x01/0x04) — A frame that wraps content in a structural HTML block element (p, div, table, h1–h6, etc.). Can contain other blocks, inline elements, and style spans.
Meta region (0xFE/0xFF) — A non-printing data region carrying element attributes, comments, or other metadata. May appear after any frame opener or standalone in the content stream.
Preamble — A Q-SET formatted header declaring color palettes, font palettes, extended bitmap layouts, and custom elements. May be inline or in an external .rrc file. Not required for Bit-Wise or Lite-Wire tiers.
Frame — General term for any open/close pair (style span, inline element, or block element).
Inflation — The process of converting RichRender content to Render-HTML for rendering in standard clients.
Deflation — The process of converting HTML content to RichRender for compact storage or transmission.
Wire format — RichRender optimized for transmission. Uses nested frames for semantic clarity and structural efficiency. Preferred for AI consumption.
Editor format — RichRender optimized for character-level editing. Uses flat consecutive spans. An intermediate representation, not a wire format.
Resuming pip — A Style-Pip that immediately follows the ETX close of a nested span, explicitly declaring the style that continues after the nested span ends. Eliminates the need for style stack maintenance in parsers.
The Style-Pip is a variable-length descriptor carried by style spans (STX frames). It describes the visual formatting applied to the span's content. Each pip is a complete description of the rendered style — not a delta or modification of a previous state.
The spec defines three bytes. Byte four and beyond are preamble-defined for documents requiring custom properties.
| Bit | Name | Description |
|---|---|---|
| 7 | continuation | 1 = byte two follows; 0 = this is the only style byte |
| 6 | vertical-hi | High bit of vertical/variant field |
| 5 | vertical-lo | Low bit of vertical/variant field |
| 4 | underline | 1 = underline |
| 3 | strikethrough | 1 = strikethrough |
| 2 | italic | 1 = italic |
| 1 | bold | 1 = bold |
| 0 | reserved | Must be 0. Reserved for future use |
Vertical/variant field (bits 6–5):
| Value | Meaning |
|---|---|
| 00 | Normal |
| 01 | Superscript |
| 10 | Subscript |
| 11 | Highlight (default yellow; byte two can specify color) |
Pip value 0x00: All bits clear, including continuation. This represents the default unstyled state — no decorations, no variant, no extended bytes. Equivalent to plain text.
Present only when byte one's continuation flag (bit 7) is set.
| Bit | Name | Description |
|---|---|---|
| 7 | continuation | 1 = byte three follows; 0 = style ends here |
| 6–5 | background | Background/highlight color (2 bits, index values 0–3) |
| 4–1 | color | Foreground color index (4 bits, index values 0–15) |
| 0 | font | Font family (0 = serif, 1 = sans-serif) |
Background color values (bits 6–5):
| Value | Meaning |
|---|---|
| 00 | None (transparent) |
| 01 | Yellow highlight |
| 10 | Green highlight |
| 11 | Pink highlight |
Font bit (bit 0):
| Value | Meaning |
|---|---|
| 0 | Serif (system default: Times, Georgia, etc.) |
| 1 | Sans-serif (system default: Arial, Helvetica, etc.) |
Monospace is handled by the code inline element frame, not the style pip. Additional fonts (cursive, display, condensed) are preamble-defined.
Highlight interaction with byte one: When byte one highlight is set (bits 6–5 = 11) without byte two, the default yellow highlight applies — one byte total. When byte two is present, the background color field (bits 6–5) specifies the highlight color. When byte two background bits are set but byte one highlight is NOT set, the background color still applies. Both set: byte two background takes precedence.
Present only when byte two's continuation flag (bit 7) is set.
| Bit | Name | Description |
|---|---|---|
| 7 | continuation | 1 = byte four follows (preamble-defined) |
| 6–4 | size | Font size index (3 bits, index values 0–7) |
| 3 | reserved | Must be 0. Reserved for future use |
| 2 | double-underline | 1 = double underline |
| 1 | overline | 1 = overline |
| 0 | small-caps | 1 = small-caps |
Font size table (spec-defined defaults):
| Index | Size | Typical Use |
|---|---|---|
| 0 | default (1em) | Body text |
| 1 | 2em | ~h1 |
| 2 | 1.5em | ~h2 |
| 3 | 1.17em | ~h3 |
| 4 | 1em (explicit) | ~h4 |
| 5 | 0.85em | Caption / small |
| 6 | 0.75em | Fine print |
| 7 | 0.6em | Legal / footnote |
Preamble can override the default size table.
Double underline interaction: When both byte-one underline (bit 4) and byte-three double underline (bit 2) are set, double underline wins. The inflator checks double first, skips single if present. Three states: none, single, double.
Present only when byte three's continuation flag is set. Bit 7 remains the continuation flag; bits 6–0 are defined entirely by preamble bitmap declarations. The spec assigns no meaning to any bits in byte four or beyond.
Style spans use STX (0x02) and ETX (0x03) with distinct roles:
STX (style open): Declares a new style. A new STX implicitly ends the previous style — no explicit close is needed for sequential style changes.
ETX (nested close): Used exclusively to close a nested span. A clear mask pip immediately follows, declaring which bits to remove from the current style. The parser computes current &= ~pip. Special case: ETX [0x00] clears all style (returns to unstyled) — since clearing no bits would be a no-op, 0x00 is given this special meaning.
Sequential styles — STX only, no ETX:
STX [bold] bold text STX [italic] italic text STX [0x00] normal textEach STX replaces the previous style. The parser maintains a single current-style variable.
Nested styles — ETX with clear mask:
STX [italic] The quick STX [bold+italic] brown fox ETX [bold] jumps overThe parser reads strictly forward:
(bold+italic) &= ~bold → italic continuesThe inner pip carries bold AND italic — the full rendered state. The clear mask after ETX names only what is being removed. The encoder never needs to look back to compute a resuming style; the decoder does one bitwise operation.
ETX [0x00] — clear all:
STX [bold+italic] styled text ETX [0x00] back to normalSpecial case: 0x00 means clear all bits. Parser logic: if (pip == 0) style = 0; else style &= ~pip;
Structural boundaries: Block close (EOT), inline element close (SI), or end of stream all implicitly end the current style. No ETX needed.
Benefits of this model:
RichRender uses three distinct frame pairs. All follow the same structural pattern:
[opener] [descriptor] [optional meta region] [content] [closer]Purpose: Visual text formatting.
Structure:
0x02 [style-pip] [optional 0xFE type meta 0xFF] [content] ...Purpose: Semantic inline HTML wrappers — elements whose identity carries meaning beyond visual formatting.
Structure:
0x0E [element-index] [optional 0xFE type meta 0xFF] [content] 0x0FPurpose: Structural containers — block-level HTML elements.
Structure:
0x01 [element-index] [optional 0xFE type meta 0xFF] [content] 0x04Frames follow strict full-enclosure nesting. No overlapping. Every opened frame must close before its containing frame closes.
Containment hierarchy:
Style span nesting: Nesting of style spans is permitted and encouraged in wire format for semantic clarity. The outer span establishes a style scope; inner spans modify within it. Nested spans carry the complete rendered style in their pip. ETX closes the nested span and a clear mask pip follows (see Section 3.5).
Both flat consecutive spans and properly nested spans are valid RichRender. See Section 11 for the editor-to-wire pipeline.
Element indices are numbers from 1–127 (0 is reserved). Bit 7 is reserved as the ARIA flag (Section 6), leaving indices 1–127 for elements. The spec defines the base set at fixed positions. Preamble declarations extend the list beyond the last spec-defined index.
Block and inline elements use separate index namespaces — the opener byte (SOH vs SO) determines which table to look up.
Sectioning and structure:
| Index | Element |
|---|---|
| 1 | div |
| 2 | section |
| 3 | article |
| 4 | aside |
| 5 | nav |
| 6 | header |
| 7 | footer |
| 8 | main |
Headings:
| Index | Element |
|---|---|
| 9 | h1 |
| 10 | h2 |
| 11 | h3 |
| 12 | h4 |
| 13 | h5 |
| 14 | h6 |
Text containers:
| Index | Element |
|---|---|
| 15 | p |
| 16 | pre |
| 17 | blockquote |
Lists:
| Index | Element |
|---|---|
| 18 | ul |
| 19 | ol |
| 20 | li |
| 21 | dl |
| 22 | dt |
| 23 | dd |
Tables:
| Index | Element |
|---|---|
| 24 | table |
| 25 | thead |
| 26 | tbody |
| 27 | tfoot |
| 28 | tr |
| 29 | th |
| 30 | td |
| 31 | caption |
| 32 | colgroup |
| 33 | col |
Media and interactive:
| Index | Element |
|---|---|
| 34 | figure |
| 35 | figcaption |
| 36 | details |
| 37 | summary |
| 38 | fieldset |
| 39 | legend |
Other:
| Index | Element |
|---|---|
| 40 | hr |
| 41 | form |
| 42 | dialog |
| 43 | pagebreak |
Indices 44–127 are available for preamble-defined custom block elements.
Headings — dual mechanism: At the Lite-Wire and Wire tiers, headings use block element indices 9–14. At the Bit-Wise tier (no block frames), headings are expressed as style patterns (size + bold) and recognized by the inflator. See Section 13.6.
| Index | Element | Notes |
|---|---|---|
| 1 | code | Code fragment |
| 2 | kbd | Keyboard input |
| 3 | mark | Highlighted text |
| 4 | abbr | Abbreviation |
| 5 | cite | Citation |
| 6 | samp | Sample output |
| 7 | var | Variable |
| 8 | ins | Insertion |
| 9 | del | Deletion |
| 10 | dfn | Definition term |
| 11 | time | Date/time |
| 12 | span | Generic inline |
| 13 | em | Emphasis (semantic — distinct from pip italic) |
| 14 | strong | Strong emphasis (semantic — distinct from pip bold) |
| 15 | a | Anchor/link — URL carried in meta region |
| 16 | q | Inline quotation |
| 17 | bdi | Bidirectional isolate |
| 18 | bdo | Bidirectional override |
| 19 | data | Machine-readable value |
| 20 | ruby | Ruby annotation |
| 21 | rt | Ruby text |
| 22 | rp | Ruby parenthesis |
| 23 | wbr | Word break opportunity |
| 24 | output | Output |
| 25 | meter | Meter |
| 26 | progress | Progress |
Indices 27–127 are available for preamble-defined custom inline elements.
Links example:
0x0E [15] 0xFE [0x00] href="https://example.com" 0xFF display text 0x0F
SO a meta attr attribute content end content SIAccessibility is a first-class concern. RichRender supports ARIA through two mechanisms: roles via a compact spec-defined index, and attributes via the meta region.
ARIA roles attach to element frames (both block and inline) via bit 7 of the element index byte.
Without ARIA:
0x01 [element-index]
bit 7 = 0, bits 6–0 = element numberWith ARIA role:
0x01 [element-index] [aria-role-byte]
bit 7 = 1, bits 6–0 = element numberWhen bit 7 is set, the next byte is an ARIA role index from the table below. Zero overhead when ARIA is not used.
| Index | Role | Index | Role |
|---|---|---|---|
| 1 | alert | 2 | alertdialog |
| 3 | application | 4 | banner |
| 5 | button | 6 | cell |
| 7 | checkbox | 8 | columnheader |
| 9 | combobox | 10 | complementary |
| 11 | contentinfo | 12 | definition |
| 13 | dialog | 14 | directory |
| 15 | document | 16 | feed |
| 17 | figure | 18 | form |
| 19 | grid | 20 | gridcell |
| 21 | group | 22 | heading |
| 23 | img | 24 | link |
| 25 | list | 26 | listbox |
| 27 | listitem | 28 | log |
| 29 | main | 30 | marquee |
| 31 | math | 32 | menu |
| 33 | menubar | 34 | menuitem |
| 35 | menuitemcheckbox | 36 | menuitemradio |
| 37 | navigation | 38 | none |
| 39 | note | 40 | option |
| 41 | presentation | 42 | progressbar |
| 43 | radio | 44 | radiogroup |
| 45 | region | 46 | row |
| 47 | rowgroup | 48 | rowheader |
| 49 | scrollbar | 50 | search |
| 51 | searchbox | 52 | separator |
| 53 | slider | 54 | spinbutton |
| 55 | status | 56 | switch |
| 57 | tab | 58 | table |
| 59 | tablist | 60 | tabpanel |
| 61 | term | 62 | textbox |
| 63 | timer | 64 | toolbar |
| 65 | tooltip | 66 | tree |
| 67 | treegrid | 68 | treeitem |
Indices 69–127 available for preamble-defined custom roles.
ARIA attributes (aria-label, aria-describedby, aria-live, etc.) carry string values and are handled through the meta region as standard element attributes:
0x01 [nav+ARIA] [37] 0xFE [0x00] aria-label="Main navigation" 0xFF content 0x04
SOH bit7=1 nav meta attr end EOTNo special mechanism is needed — ARIA attributes are just attributes.
0xFE and 0xFF are permanently invalid in UTF-8, allowing RichRender to repurpose them as delimiters for non-printing metadata regions.
0xFE [type-byte] [content] 0xFFThe type byte determines interpretation. Bit 7 is a continuation flag (reserved for future use). Bits 6–0 define the region type.
| Type Byte | Meaning |
|---|---|
| 0x00 | Element attributes — the content is attribute text as it would appear inside an HTML opening tag |
| 0x01 | Comment — non-rendering annotation, parsers may skip entirely |
Types 0x02–0x7F are reserved for future use.
A meta region may appear in two positions:
After a frame descriptor — immediately following the Style-Pip (in a style span) or element index byte and optional ARIA role byte (in element frames). The meta region content is associated with that frame's element.
Standalone — anywhere in the content stream outside of a frame descriptor position. Typically used for comments and annotations.
The structure is identical in both positions. The type byte is always present.
Paragraph with attributes:
0x01 [15] 0xFE [0x00] id="intro" class="lead" 0xFF Content here 0x04
SOH p meta attr end EOTLink with href:
0x0E [15] 0xFE [0x00] href="https://example.com" title="Example" 0xFF Click here 0x0F
SO a meta attr end SIInline comment:
Content before 0xFE [0x01] TODO: review this section 0xFF content after
meta cmnt endStyled span with attributes:
0x02 [pip:bold] 0xFE [0x00] data-ref="note3" 0xFF Bold text ...
STX meta attr endSince 0xFE and 0xFF cannot appear in valid UTF-8 byte sequences, there is no collision risk with text content. No escaping mechanism is required. If a RichRender stream encounters these bytes outside of the meta region structure, the data is malformed.
0xFE and 0xFF should never appear outside of a RichRender-formatted stream. They are permanently invalid in UTF-8 and any system passing content outside of RichRender context must ensure these bytes are not present.
The spec defines standard default palettes for all color and font fields. Documents using only the defaults need no preamble. Preamble declarations override these defaults when a document requires custom values.
| Index | Color | Hex |
|---|---|---|
| 0 | Default (inherit) | — |
| 1 | Black | #000000 |
| 2 | Dark gray | #555555 |
| 3 | Red | #CC0000 |
| 4 | Dark red / Maroon | #800000 |
| 5 | Orange | #DD6600 |
| 6 | Green | #008800 |
| 7 | Dark green | #005500 |
| 8 | Blue | #0066CC |
| 9 | Dark blue / Navy | #003366 |
| 10 | Purple | #7700AA |
| 11 | Teal | #008888 |
| 12 | Brown | #885500 |
| 13 | Light gray | #AAAAAA |
| 14 | White | #FFFFFF |
| 15 | Gold / Yellow | #CCAA00 |
| Index | Color |
|---|---|
| 0 | None (transparent) |
| 1 | Yellow highlight |
| 2 | Green highlight |
| 3 | Pink highlight |
| Value | Family |
|---|---|
| 0 | Serif (system default: Times, Georgia, etc.) |
| 1 | Sans-serif (system default: Arial, Helvetica, etc.) |
| Index | Size | Typical Use |
|---|---|---|
| 0 | default (1em) | Body text |
| 1 | 2em | ~h1 |
| 2 | 1.5em | ~h2 |
| 3 | 1.17em | ~h3 |
| 4 | 1em (explicit) | ~h4 |
| 5 | 0.85em | Caption / small |
| 6 | 0.75em | Fine print |
| 7 | 0.6em | Legal / footnote |
The preamble declares per-document overrides: custom color palettes, font palettes, extended Style-Pip bitmaps, and custom element definitions. A preamble is not required for Bit-Wise or Lite-Wire tier documents using only spec defaults.
The preamble is a Q-SET structure using Set|Tag delimiters:
{SETTAG:RICHRENDER}
[COLORS]
0 | #1a1a2e
1 | #e94560
2 | #0f3460
[FONTS]
0 | Palatino
1 | Futura
[SIZES]
1 | 3em
2 | 2em
[STYLE-PIP-4]
bit6-3 | custom-color
bit2-0 | reserved
[BLOCK-ELEMENTS]
44 | my-container
45 | sidebar-panel
[INLINE-ELEMENTS]
27 | my-tooltip
28 | data-badge
{/SETTAG/}[COLORS] entries override the spec default foreground palette (Section 8.1) at matching indices. Unspecified indices retain the spec defaults.[FONTS] entries override the font bit meaning. With a preamble, the font bit selects between the two preamble-defined families.[SIZES] entries override the spec default size table (Section 8.4) at matching indices.Groups named [STYLE-PIP-N] (where N is the byte number, starting at 4) define the bit layout for Style-Pip bytes beyond byte three. Each key|value pair maps bit positions to their meaning. Bit 7 is always the continuation flag and should not be declared.
Groups named [BLOCK-ELEMENTS] and [INLINE-ELEMENTS] extend the spec-defined element tables. Index numbers must start after the last spec-defined index for each category (44+ for blocks, 27+ for inlines).
The preamble may appear:
.pip document, before any content..rrc file, referenced by the document via Q-SET's include() mechanism.A .pip document without a preamble is valid — it uses spec-defined defaults for all palettes, sizes, and element tables.
A .pip document may reference an external .rrc file, carry its own inline preamble, or both (with inline declarations overriding external ones).
| Byte | ASCII Name | RichRender Use |
|---|---|---|
| 0x01 | SOH | Block element frame OPEN |
| 0x02 | STX | Style span OPEN (implicitly closes previous style) |
| 0x03 | ETX | Nested style span CLOSE (clear mask pip follows) |
| 0x04 | EOT | Block element frame CLOSE |
| 0x0E | SO | Inline element frame OPEN |
| 0x0F | SI | Inline element frame CLOSE |
| Byte | Use |
|---|---|
| 0xFE | Meta region OPEN — permanently invalid in UTF-8, zero collision risk |
| 0xFF | Meta region CLOSE — permanently invalid in UTF-8, zero collision risk |
| Byte | ASCII Name | Reason |
|---|---|---|
| 0x00 | NUL | String terminator in C and many toolchains |
| 0x09 | TAB | Standard whitespace — see Section 14 |
| 0x0A | LF | Line feed |
| 0x0D | CR | Carriage return |
All other control characters (0x05–0x08, 0x0B–0x0C, 0x10–0x1F) are unassigned in this version. They have no RichRender meaning and should not appear in a RichRender stream. Future versions may assign roles to some of these.
RichRender defines a transmission (wire) format. Editor implementations may use a different internal representation optimized for character-level manipulation.
An editor may expand any RichRender frame into per-character Style-Pip pairs. Each UTF-8 glyph is followed by its own Style-Pip. Consecutive characters sharing a style carry a pip value of 0x00 (all flags clear, no continuation) — one byte overhead per character meaning "same as before."
This is an implementation strategy, not a wire format. The spec does not mandate internal representation.
A wire-rendering agent takes editor output and optimizes it for transmission:
The transformation between editor format and wire format is lossless. Expanding wire-format frames to per-character pips and collapsing them back produces identical output. No information is lost in either direction.
.pip DocumentA .pip file is a hybrid document, analogous to PHP — HTML provides the page skeleton, and RichRender handles styled content inline where it is most efficient.
The structural HTML remains as HTML: <!DOCTYPE>, <head>, <meta>, <script>, page layout. Inside content regions where HTML would normally use verbose inline styles and nested <span> elements, RichRender takes over using its frame system.
This means:
.pip documents are largely valid HTML with embedded RichRender sections..rrc Context FileA .rrc (RichRender Context) file is an external Q-SET preamble. It contains palette declarations, bitmap definitions, and custom element mappings that can be shared across multiple .pip documents.
This mirrors modern web practice where CSS, JS, and configuration files are external and included. A .rrc file is consumed at build or serve time — it is never delivered to the client as a separate request.
A server may serve the same content differently based on the HTTP Accept header:
.pip content with RichRender sections intact.RichRender streams are not valid UTF-8 due to the use of 0xFE and 0xFF. A dedicated MIME type is required for proper content negotiation and transport:
application/richrender — for RichRender content.application/richrender-context — for .rrc preamble files (if served independently).Inflation is the process of converting RichRender content to Render-HTML for rendering in standard clients.
A style span inflates to a <span> with appropriate styling. All output elements carry mdr- prefixed classes for lossless round-trip back to RichRender.
Byte one flags:
| Pip Flag | CSS Output | Render-HTML Class |
|---|---|---|
| bold | font-weight: bold | mdr-b |
| italic | font-style: italic | mdr-i |
| strikethrough | text-decoration: line-through | mdr-s |
| underline | text-decoration: underline | mdr-u |
| superscript | vertical-align: super; font-size: smaller | mdr-sup |
| subscript | vertical-align: sub; font-size: smaller | mdr-sub |
| highlight | background-color: yellow (default) | mdr-hl |
Byte two fields:
| Pip Field | CSS Output | Render-HTML Class |
|---|---|---|
| foreground color | Resolved from palette → color: #rrggbb | mdr-fg |
| background color | Resolved from palette → background-color: ... | mdr-bg |
| font | font-family: serif or font-family: sans-serif | mdr-font |
Byte three fields:
| Pip Field | CSS Output | Render-HTML Class |
|---|---|---|
| font size | Resolved from size table → font-size: ... | mdr-sz |
| small-caps | font-variant: small-caps | mdr-sc |
| overline | text-decoration: overline | mdr-ol |
| double underline | text-decoration: underline double | mdr-du |
Multiple text-decoration flags combine: text-decoration: underline line-through overline.
Double underline overrides single underline when both are set.
Inline and block element frames inflate to their corresponding HTML tags:
0x0E [3] 0xFE [0x00] data-ref="7" 0xFF Hello 0x0F
→ <mark data-ref="7">Hello</mark>
0x01 [15] 0xFE [0x00] id="intro" class="lead" 0xFF Content 0x04
→ <p id="intro" class="lead">Content</p>When an element carries an ARIA role:
0x01 [nav+ARIA] [37] 0xFE [0x00] aria-label="Main" 0xFF content 0x04
→ <nav role="navigation" aria-label="Main">content</nav>Comment meta regions (type 0x01) are inflated to Render-HTML comments or omitted entirely, at the inflater's discretion:
0xFE [0x01] Editor note 0xFF → <!-- Editor note --> or nothingAn inflater may optionally generate a stylesheet that maps common pip patterns to CSS classes, reducing inline style repetition in the inflated output.
At the Bit-Wise tier, headings are expressed as style patterns rather than block element frames. The inflator recognizes these patterns and emits semantic heading tags:
| Pattern | Emitted Tag | Render-HTML Class |
|---|---|---|
| bold + size index 1 (2em) | <h1> | mdr-set-h1 |
| bold + size index 2 (1.5em) | <h2> | mdr-set-h2 |
| bold + size index 3 (1.17em) | <h3> | mdr-set-h3 |
| bold + size index 4 (1em explicit) | <h4> | mdr-set-h4 |
At the Lite-Wire and Wire tiers, block element headings (indices 9–14) produce <h1> through <h6> with class mdr-h1 through mdr-h6.
The mdr-set-* vs mdr-h* class distinction enables lossless round-trip: mdr-set-h1 reconstructs as bold + size pips, mdr-h1 reconstructs as a block frame.
Tab (U+0009) is stored as the raw byte 0x09 in the text stream. No special control character is needed — 0x09 is outside the RichRender control character range.
Stored as a normal character entry with pip bytes. Rendered in the editor via white-space: pre-wrap on the editing area, which preserves tab stops.
HTML has no native tab stops. Browsers collapse \t to a single space in normal rendering. The Render-HTML inflator emits a styled span for tabs:
<span class="mdr-tab" style="display:inline-block;width:2em"></span>The mdr-tab class allows CSS customization. The default width is 2em.
Tab rendering is a policy decision, not a format decision. Future implementations may support configuration for tab width, tab replacement (e.g., sequences), or CSS tab-size property. The wire format is stable — only the inflator's rendering changes.
All Render-HTML output from a RichRender inflator carries mdr- prefixed classes for lossless round-trip back to RichRender. See Section 13 for the complete class-to-pip mapping.
A fully Render-HTML document declares itself in <head>:
<meta name="richrender" content="full" data-tier="bitwise">The data-tier attribute indicates the maximum pip depth used: bitwise (bytes 1–3) or wire (full frame structure).
When Render-HTML regions exist inside a larger HTML document (CMS page, email template, web app), each region is wrapped:
<div data-mdr="root" data-mdr-tier="bitwise">
<!-- clean Render-HTML, mdr- classes throughout -->
</div>Multiple data-mdr="root" regions may appear in one document. Each is independently round-trippable.
HTML outside data-mdr="root" boundaries is not Render-HTML. The spec does not mandate how implementations handle it. Three strategies are documented as guidance for implementers:
<strong> → bold bit, <h2> → size 2 + bold). Lossy but practical for paste-from-Word and similar workflows. Implementation decides what maps and what gets dropped.Implementations may combine strategies — best-effort with passthrough fallback for elements that don't map cleanly. The choice is application-specific and should not be constrained by the spec.
This section is implementation guidance, not core spec. It describes a pattern for editors handling pasted content with colors outside the default palette.
The spec defines 16 default foreground colors and 4 background colors. Pasted content often arrives with colors outside the default set. An editor that discards these colors violates user expectations; an editor that permanently alters the spec palette breaks round-trip fidelity for other documents.
Editors maintain a document-specific mutable palette (the "editor palette") separate from the spec defaults. The editor palette is a sparse array — only slots that differ from the spec defaults are populated.
Resolution: for each index, check the editor palette first; if empty, fall back to the spec default. This applies at render time only — the pip bytes store the same index either way.
The editor palette is per-document, editor-only state. It is not encoded in the wire format. A document saved as a wire stream uses only the spec default colors or preamble overrides.
hr is block element index 40; pagebreak is block element index 43. No control characters needed.include() mechanism handles external .rrc file references.application/richrender (see Section 12.4).mdr-set-h*), Wire uses block frames (→ mdr-h*). Round-trip disambiguated by class prefix.The lowest-friction adoption path requires no client changes and no standards adoption. An email provider or content host installs the deflator/inflator pair as a server-side storage optimization:
.pip format for storage.The value proposition is immediate and measurable: significant storage reduction on HTML email content with zero client-facing impact. This is an infrastructure cost optimization, not a format adoption decision.
Once the inflator/deflator infrastructure is in place on the server, native delivery to capable clients is a minimal additional step:
Accept: application/richrender)..pip content directly, skipping inflation.The server is the smart layer. Senders never need to know the recipient's client capabilities. The server stores one canonical .pip copy and serves the appropriate format per request.
With server infrastructure and client delivery in place, authoring tools can compose directly in RichRender:
.pip and send to the server in native format..pip documents natively.The server-side email path is one of several independent adoption vectors:
.pip authoring tool, producing content ready for any output path (web, email, AI exchange).Each path is independent. Progress on one does not block or require progress on the others.
RichRender is created by Kirk Siqveland. Specification developed in collaboration with Claude (Anthropic).