Bridge indexing · design system
Design system
Color — two axes
Axis 1 · HUE = runtime
L1 / bridge--l1
EVM L2--evm
Michelson L2--mich
alias / NAC--alias
feed / kernel--feed
system / legacy--sys
Axis 2 · ACCENT = indexed
indexed ✓--idx
planned ◔--planned
Encoding — legend
Legend
Hue
L1 / bridge
L2 EVM
L2 Michelson
kernel / feed
Decision
✓ indexed
◔ planned
not indexed
Status
legacy · remains in mainnet
Layout and verification
one object = one node
One on-chain object = one node in the standard format (§3 · Node dictionary). Paired events (Token Mint/Burn) — as a separate node.
self-check before showing
Mermaid captures the geometry (overflow / collisions / a “missed arrow” are impossible). Before showing — verify the render has no console errors and that line statuses/colors are in place; screenshot the diagram by uid (take_snapshot → take_screenshot).
Mermaid
flowchart TD
U(["User"]):::actor
K["<b class='t'>Kernel parse_routing</b>"]:::ker
subgraph R["L2 · EVM · ACTUAL"]
direction TB
E["<span class='glyph'>✓</span><b class='t'>Value Transfer</b><span class='s'>Blockscout · transaction · 0x…feed</span><span class='ix'>✓ etherlink.on_xtz_deposit</span><span class='md'>✚ EtherlinkDepositOperation</span>"]:::evm
F["<b class='t'>Event Deposit</b><span class='s'>Blockscout · event · 0xff…01</span>"]:::evm
E --> F
end
M["<span class='glyph'>◔</span><b class='t'>Event Deposit</b><span class='s'>TzKT · event · tz1Ke2h7…</span><span class='pln'>◔ planned: tezos.events</span>"]:::mich
L["<b class='t'>Value Transfer</b><span class='s'>Blockscout · transaction · 0x00…00</span>"]:::evm
K -->|has code → queue| E
K -->|Michelson · tz1| M
K -.->|old kernel| L
U -.->|initiates deposit| K
class E indexed
class M planned
class F faded
class L legacy
class L faded
class U faded
class K faded
classDef l1 fill:#e8836b1f,stroke:#e8836b,stroke-width:1.5px;
classDef evm fill:#5aa6e01f,stroke:#5aa6e0,stroke-width:1.5px;
classDef mich fill:#6fbf731f,stroke:#6fbf73,stroke-width:1.5px;
classDef ker fill:#d9a83c21,stroke:#d9a83c,stroke-width:1.5px;
classDef sys fill:#8b919c1f,stroke:#8b919c,stroke-width:1.5px;
classDef indexed stroke-width:2px;
classDef planned stroke-width:2px;
classDef faded stroke-width:1.5px;
classDef legacy stroke-width:1.5px;
classDef actor fill:transparent,stroke:#e8836b,stroke-width:1.5px;
linkStyle 0 stroke:#5aa6e0,stroke-width:2.4px;
linkStyle 1 stroke:#5aa6e0,stroke-width:2.4px;
linkStyle 2 stroke:#6fbf73,stroke-width:2.4px;
linkStyle 3 stroke:#5aa6e0,stroke-width:2.4px,stroke-dasharray:6 5,opacity:0.55;
linkStyle 4 stroke:#e8836b,stroke-width:2.4px,stroke-dasharray:6 5,opacity:0.55;
1 · Node HUE — classDef, applied inline
// fill = hue-bg (.12 alpha, 8-digit hex), stroke = hue classDef l1 fill:#e8836b1f,stroke:#e8836b,stroke-width:1.5px; classDef evm fill:#5aa6e01f,stroke:#5aa6e0,stroke-width:1.5px; classDef mich fill:#6fbf731f,stroke:#6fbf73,stroke-width:1.5px; classDef ker fill:#d9a83c21,stroke:#d9a83c,stroke-width:1.5px; classDef sys fill:#8b919c1f,stroke:#8b919c,stroke-width:1.5px; NODE["…"]:::evm // hue — inline on the node
⚠️ Pitfall: do not write
color: in classDef. Mermaid turns it into the rule .class span{color … !important}, which overrides all per-line colors inside the node (handler/source turn white). Text color is set only by themeCSS (§3); classDef is the node's border/fill.2 · DECISION — one line per node
class NODE indexed // ✓ teal glyph + teal handler line class NODE planned // ◔ amber glyph class NODE faded // not indexed (opacity .45) class NODE legacy // dashed border; multiple lines per node allowed
⚠️ Pitfall: a comma in
class is a list of NODES, not classes. class NODE evm,indexed reads as “class indexed for nodes NODE and evm” — the hue won't arrive. That's why hue is inline :::evm and decision is one line per class.3 · Node dictionary — HTML label + themeCSS
NODE["<span class='glyph'>✓</span><b class='t'>Event Withdrawal</b><span class='s'>Blockscout · event · 0xff…02</span><span class='ix'>✓ etherlink.on_withdraw</span><span class='md'>✚ EtherlinkWithdrawOperation</span>"]:::evm // lines: .t name · .s source (source · type · address) · .ix handler (teal) · .md model · .pln planned (amber) // themeCSS (inside mermaid.initialize) — line colors: .nodeLabel .t { display:block; white-space:nowrap; font-weight:600; color:#eef0f3; padding-right:20px } // name .nodeLabel .s { display:block; white-space:nowrap; font-size:9.5px; color:#7c828e } // source .nodeLabel .ix { display:block; font-size:9.5px; color:#3ed6c0 } // handler (teal) .nodeLabel .md { display:block; font-size:9.5px; color:#7c828e } // model .nodeLabel .pln { display:block; font-size:9.5px; color:#d9a83c } // planned (amber) .nodeLabel .glyph { position:absolute; top:-2px; right:0; font-size:17px; font-weight:700 } .node.indexed .nodeLabel .glyph { color:#3ed6c0 } // ✓ teal .node.planned .nodeLabel .glyph { color:#d9a83c } // ◔ amber .node.faded { opacity:.45 } .node.legacy rect { stroke-dasharray:5 4 }
Node lines do not wrap.
white-space:nowrap on every line + keeping them short: the node grows wider, not taller, and doesn't break the layout. source — strictly source · type · address, nothing more. The trailing name (method, event, entrypoint, notes like input=0x / 4-arg / → receiver) lives in the title and is not repeated in source: it only widens the node. The shorter the source line, the narrower the node — and node width, multiplied across side-by-side columns, is what drives the whole diagram's width.Address — hash or alias, no strict rule. Both read fine: a truncated hash (
0xff…01, tz1Ke2h7…) or a role alias (rollup, gateway, tez-ticketer). A hash earns its place when it distinguishes several similar contracts (0xff…01 XTZBridge vs 0xff…02 FABridge); an alias is clearer when there's a single well-known contract and the hash would distinguish nothing. Multi-word aliases are hyphenated.Title stays short. Forms:
Call Contract.method, Event Name, or a role label (Transfer Ticket, Value Transfer, Rollup cement). No runtime prefix (Tezos … — the hue already encodes the runtime) and no parenthetical qualifiers ((mint) / (burn) — disambiguation belongs in the source address).No stray "why / when" lines. A node says what is indexed. Free-text notes about conditions or phase (
only if code present, Block 3, proxy specified?, bridge.rs:401-507) don't read at node size and just clutter — if something needs explaining, extend the graphic, not the node. This isn't a ban on conditions or roadmap: recurring structural facts get a defined encoding here first (a hue, a glyph, a dedicated line class — the way roadmap is already carried by color + status glyphs), and only then appear in nodes. Until it's in this standard, it doesn't go in a node.4 · ARROW — linkStyle by declaration order
linkStyle 0 stroke:#5aa6e0,stroke-width:2.4px; // evm linkStyle 3 stroke:#5aa6e0,stroke-width:2.4px,stroke-dasharray:6 5,opacity:.55; // legacy
Mermaid trade-off: the condition label on the arrow stays neutral gray (per-label color matching the runtime hue isn't available out of the box). A deliberate concession for the mermaid medium.
5 · Actor — capsule (stadium node)
SP(["Service Provider"]):::actor class SP faded classDef actor fill:transparent,stroke:#e8836b,stroke-width:1.5px;
6 · Init — canonical block (extracted into mermaid-init.js)
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({
startOnLoad:false, securityLevel:'loose', theme:'base',
themeCSS, // the CSS string from §3
themeVariables:{
fontFamily:'JetBrains Mono, monospace', fontSize:'12px',
lineColor:'#565c67', clusterBkg:'transparent',
clusterBorder:'#1e212a', edgeLabelBackground:'#0e1014',
},
flowchart:{ htmlLabels:true, curve:'basis', nodeSpacing:42, rankSpacing:58, padding:10, useMaxWidth:false },
});
await mermaid.run({ querySelector:'.mermaid' });