Modal

Hide off-thread depth content behind a click-to-expand overlay — keep the main text compact while letting curious readers retrieve detail on demand.


When to use

  • Optional depth ("expand to see derivation" math proofs)
  • Large images / long code (don't bloat the page)
  • History / footnotes / acknowledgments (don't affect the main thread but should be accessible)
  • Risk disclosure / legal fine print (must exist but isn't featured)
  • Media (videos / large images, save first-load bandwidth)
  • Must-read content (anything readers must see while scanning) → use Callout
  • Required forms (modal close discards state — bad for forms) → put Interact inline
  • Core explanations (hiding makes readers miss it) → inline in main text

Basic syntax

```modal trigger="show technical details"
Hidden content here —
- detailed algorithm
- edge cases
- performance analysis
```

Renders: an inline "show technical details" link (underlined / with icon); click → centered overlay shows the modal content; click ✕ or background to close.


All options

Block-level: ```modal

Option Type Default Purpose
trigger string (required) Text of the trigger link
width enum medium Modal width (small / medium / large / full)

Quote trigger if it has spaces: trigger="show details".


Examples

Example 1: optional math derivation

**Compound interest formula**: final value = principal × (1 + rate)^years

```modal trigger="Math derivation (optional)"
**Why (1+r)^n?**

End of year 1 principal: P × (1+r)
End of year 2 principal: P × (1+r) × (1+r) = P × (1+r)²
End of year n principal: P × (1+r)ⁿ

**Why not P + n × r × P (simple interest)?**

Simple interest ignores "interest earning interest." The key in compound interest is that the (1+r) factor applies to each year's running total, not just the original principal.
```

Renders: main text shows the clean formula; readers wanting derivation click "Math derivation (optional)" to expand. Beginners aren't drowned in proofs.

Example 2: large image

System architecture overview (click for full diagram):

```modal trigger="🖼️ Full architecture diagram (5000×3000, all microservices)" width=full
*(Full architecture diagram, 5000×3000, embedded here as a normal markdown image.)*

Legend:
- Blue: HTTP services
- Red: async queues
- Green: databases
- Purple: cache layer
```

Renders: large image isn't shown inline (avoids slowing first paint); click triggers the modal to expand the full-width diagram + legend.

Example 3: long code / full script

Quick start (one command):

```bash
python setup_project.py --init
```

````modal trigger="📜 Full setup script"
```py
#!/usr/bin/env python3
"""Full project setup script."""
import os
import sys
import json

def init_project(name):
    os.makedirs(name, exist_ok=True)
    config = {"name": name, "version": "0.1.0"}
    with open(f"{name}/config.json", "w") as f:
        json.dump(config, f, indent=2)
    print(f"Project {name} initialized.")

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--init":
        init_project("my-project")
```
````

Renders: main text shows the minimal one-line command; readers wanting the full script expand the modal to see the complete source.

Example 4: legal / risk disclosure

Signing up means you agree to our terms.

```modal trigger="📄 Full Terms of Service + Privacy Policy" width=large
**Terms of Service (v3.2, 2026-05-14)**

Section 1. Service definition...
Section 2. User responsibilities...
Section 3. ...

**Privacy Policy (v2.1)**

We collect...
We will not...
```

Renders: main text is a brief consent prompt; full terms expand on demand (satisfies "must exist + must be accessible" without overwhelming the signup flow).


Plain-text fallback behavior

Modal is a fenced code block ```modal. In readers without support:

```modal trigger="show technical details"
hidden content here
```

→ shows as a code block with modal lang — readers see the trigger text + all the content; no "hidden" effect.

Per-reader breakdown:

Reader Render
Rho Trigger link + click pops up overlay
GitHub web Code block (modal as lang label, trigger + all content as plain text)
Obsidian Same
VS Code default preview Same
cat / less Plain text

⚠️ Special note: because the fallback is "expand into a code block," modal content is fully visible in non-supporting readers. Good (readers don't lose content), but means: don't use modal to hide private / sensitive info. It's a UX-level "tidying" tool, not a security-level "hiding" tool.


Common pitfalls

1. trigger text too short / unclear

```modal trigger="more"
the actual hidden complex content
```

→ "more" doesn't tell readers what they'll see. trigger should clearly describe "what'll expand":

  • ✅ "View full derivation"
  • ✅ "📜 Full setup code"
  • ✅ "📄 Full Terms of Service"
  • ❌ "more" / "click here" / "..." / "details"

2. Hiding core content in a modal

```modal trigger="how the algorithm works"
(Full algorithm explanation, 1000 words)
```

→ Readers probably won't click — they'll miss the core. Rule: if 70%+ readers need to see it, don't modal it — put it inline. Modal is for "optional depth," not "must-read but folded."

3. Modal inside modal

```modal trigger="Outer"
```modal trigger="Inner"   ← ❌ Modal in modal — overlay-on-overlay UX disaster
content
```
```

→ User clicks outer modal, finds another link to click for the second layer — readers get lost. Improvement: inline the inner modal's content directly in the outer modal.

4. interact inside modal

````modal trigger="try this slider"
```interact
slider x range=0..10 step=1 default=5
template stl:
[Selected] -> [{x}]
```
````

✅ Technically supported by Nested DSL. But:

  • Closing and reopening the modal usually resets slider state
  • Readers expect "demos" to be visible directly, not hidden in modals
  • For a core demo, don't modal it — put it in the main text

5. Form in a modal expecting submission

````modal trigger="fill this form"
```interact
input email default=""
input name default=""
```
````

→ Rho's interact state is only in the reader's memory — not persisted, not sent to a backend. Closing the modal discards all input — user filled it for nothing. Conclusion: modals aren't for forms. Put forms inline + clearly state "demo only, no real submission."

6. trigger contains unescaped quotes

```modal trigger="what if you say "hello""   ← ❌ Nested quotes break parsing

→ Use full-width "" or avoid nesting:

```modal trigger="what if you say \"hello\""   ← ✅ Escaped
```modal trigger="what if you say "hello""    ← ✅ Full-width
```

7. Short content in a modal

```modal trigger="click here"
Just one sentence.
```

→ "Click → overlay → see one sentence → close" is more annoying than just reading the sentence. Use modal only for content big enough to "bloat the page" (≥ 5 lines / large image / long code / table).


Modal vs Callout vs Tabs — how to choose

Scenario Use
Off-thread optional depth Modal
Must-read emphasized passage Callout
Multiple views, pick one (no hiding needed) Tabs
Strict tutorial-like sequence Stepper

See also

Open in Rho MD →