Common pitfalls
Cross-capability pitfall reference — grouped by category. Each entry: symptom / cause / fix. When your doc doesn't render right or behaves oddly, look here first.
Per-capability pitfalls live in their own pages (Callout / Layout / etc.). This page covers cross-capability / high-frequency / high-impact issues only.
A. Markdown syntax layer
A1. Fenced code nesting — backtick conflict
Symptom: nested block fails to parse; outer ``` closes early.
Cause: inner fenced block uses the same backtick count as the outer.
Fix: outer and inner backtick counts must differ:
\````layout ← 4 backticks outer
:::card
\```interact ← 3 backticks inner
\```
:::
\````
A2. Directive ::: not closed
Symptom: the whole block doesn't render; cascading errors affect content after it.
Cause: :::card / :::tab / :::step / :::event missing matching ::: close.
Fix: every :::xxx open needs a matching ::: close — always paired.
A3. Callout interrupted by mid-section blank line
Symptom: only the first paragraph renders inside the callout.
Cause: a blank line (without >) inside a blockquote breaks the callout.
Fix: use a blank quoted line (just >) between paragraphs:
> [!INFO]
> First paragraph.
>
> Second paragraph (still inside the callout).
B. Wrong DSL choice
B1. Using callout for layout
Symptom: 3-5 callouts stack vertically and dominate the page.
Cause: callout is single-passage emphasis, not a layout tool.
Fix: use Layout grid + cards.
B2. Using layout for strict sequence
Symptom: 3 cards side by side, but users must follow 1→2→3 order.
Cause: layout shows peers; doesn't imply sequence.
Fix: use Stepper.
B3. Hiding core content in modal
Symptom: 80% of readers miss the key explanation because they don't click.
Cause: modal is "depth on demand"; hiding core content = effectively unwritten.
Fix: put core content directly in main text; modal hides only optional depth.
B4. Stepper used as timeline
Symptom: each "step" is actually a historical event, not a tutorial step.
Cause: stepper implies "learn in order"; timeline implies "time-axis display."
Fix: history / milestones → Timeline; tutorials / setup → stepper.
B5. Tabs used when all content must be seen
Symptom: tabs hide content — readers see only the default and miss 2-3 others.
Cause: tabs hide non-active content.
Fix: must-see-all → Layout grid showing simultaneously; tabs is for "pick one of equivalent views."
C. interact / state layer
C1. Cross-block state sharing — forgot namespace
Symptom: block 2's template shows {x} literal or throws "undefined."
Cause: by default each interact block is isolated — state isn't shared.
Fix: both blocks need the same namespace:
\```interact namespace foo
slider x 0 10 5 1
\```
\```interact namespace foo
template stl: [{x}]
\```
See Shared state.
C2. Same variable redeclared in same namespace
Symptom: undefined behavior (may overwrite, may error, may keep first).
Cause: each variable in a namespace is declared once.
Fix: declare all sliders / computeds in the first block; subsequent blocks reference only, don't redeclare.
C3. Variable name with special chars
Symptom: parse fails or unexpected behavior.
Cause: variable names allow only [A-Za-z0-9_].
Fix: use camelCase myVar or underscore my_var; no spaces / hyphens / specials.
C4. button value contains expression but no quotes
Symptom: parse fails or button behavior wrong.
Cause: when value contains {expr}, it must be quoted.
Fix:
button inc "+1" "{counter+1}" ← ✅
button inc "+1" {counter+1} ← ❌ Missing quotes
C5. Trying to do math on input value
Symptom: {age + 1} outputs "301" (string concatenation), not 31.
Cause: input always returns string.
Fix: numeric input → slider; if you must use input, cast: {parseFloat(age) + 1}.
D. mini DSL expressions
D1. Reference to undeclared variable
Symptom: template shows {y} literal or throws "undefined."
Cause: every template variable must first be declared via slider/input/select/toggle/computed.
Fix: check variable is declared in the same namespace (or block).
D2. Operator precedence confusion
Symptom: BMI / compound interest etc. computes wrong.
Cause: mini DSL precedence matches math / JS, but implicit precedence is easy to miscount.
Fix: use explicit parentheses:
computed bmi = weight / (height * height) ← ✅
computed bmi = weight / height ** 2 ← ⚠️ Easy precedence trap
D3. Used a function mini DSL doesn't support
Symptom: expression throws or returns undefined.
Cause: mini DSL isn't JS — Math.X / console.log / Date etc. don't exist.
Fix: see Computed function list.
D4. computed circular dependency
Symptom: all computeds error or show NaN.
Cause: a = b + 1 + b = a + 1 forms a cycle.
Fix: computeds can't form cycles. For "previous value" patterns, use button self-reference.
D5. Ternary nested too deep
Symptom: expression unreadable, error-prone, hard to maintain.
Cause: 3+ levels of nested ternary exceeds human readability.
Fix: split into multiple computeds, or use multi-line format:
computed grade = score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: "F"
E. Vega-Lite chart
E1. JSON syntax error
Symptom: chart doesn't render; console shows JSON parse error.
Cause: vega-lite spec is strict JSON — double-quote keys, comma between values, strict format.
Fix: use Vega-Lite Editor to validate.
E2. {var} placeholder in wrong position
Symptom: chart doesn't render or behaves unexpectedly.
Cause: replacement happens before JSON parse — numeric fields no quotes, string fields with quotes.
Fix:
- Numeric:
"domain": [-{maxY}, {maxY}] - String:
"mark": "{showArea ? "area" : "line"}"
E3. data.sequence range too large
Symptom: browser hangs.
Cause: > 5000 data points strains vega-lite rendering.
Fix: keep points < 5000; curves look smooth at < 500 points.
E4. transform.calculate using mini DSL syntax
Symptom: calculate expression doesn't work.
Cause: vega-lite uses vega expression, not fully compatible with mini DSL.
Fix: see vega expression docs.
F. SVG scenes
F1. Missing viewBox
Symptom: SVG scaling is unpredictable.
Cause: missing viewBox leaves internal coordinate system undefined.
Fix: always set viewBox: <svg viewBox="0 0 100 100">.
F2. Used <script> / <foreignObject> / on* events
Symptom: those elements get stripped; corresponding features don't work.
Cause: Rho's SVG sanitizer deliberately blocks them — safety design.
Fix: use pure declarative SVG (standard shapes + animate tags + placeholders); don't rely on JS.
F3. mini DSL expression uses Math.X
Symptom: Math.sin etc. throws.
Cause: mini DSL isn't JS; no Math global.
Fix: use sin(x) / cos(x) / pow(a,b) / etc. directly.
F4. Too many SVG elements
Symptom: animation stutters.
Cause: SVG is DOM — > 200 elements strains rendering.
Fix: reduce elements / simplify shapes / switch to a chart.
G. Timer / animation
G1. step too small
Symptom: browser hangs.
Cause: (max - min) / step > thousands of frames.
Fix: keep total frames in 100-1000 range.
G2. Timer paired with chart but calculate doesn't use timer var
Symptom: chart static, timer "runs for nothing."
Cause: calculate doesn't depend on {t}.
Fix: explicitly reference {t} in vega expression: "sin(datum.x + {t})".
G3. Multiple timers expected to be in strict sync
Symptom: two timers display out-of-sync phases.
Cause: each timer is independently scheduled — possible micro-drift.
Fix: use 1 timer + multiple computed derivations:
timer t 0 10 0.1 loop
computed t1 = t
computed t2 = (t + 5) % 10
H. Nesting / complexity
H1. Nesting 4+ levels
Symptom: plain-text readers can't see structure; Rho rendering performance drops.
Cause: each nest level runs the inner processor again.
Fix: keep ≤ 3 levels; redesign page structure.
H2. Same-type nesting (tabs in tabs / stepper in stepper)
Symptom: UI confusion; readers can't tell outer from inner.
Cause: two progress bars / two tab bars compete visually.
Fix: switch the inner to a different container (modal / accordion / list).
H3. interact inside modal
Symptom: closing and reopening the modal loses slider state.
Cause: hydration state typically clears when modal hides.
Fix: don't hide core demos in modals — put them in main text.
I. Plain-text fallback
I1. Using raw HTML in place of Rho DSL
Symptom: renders OK in Rho, but on GitHub / other readers the CSS doesn't render.
Cause: raw HTML is inconsistent across readers; GitHub strips style.
Fix: use Rho DSL (Layout / Modal / etc.) instead.
I2. Treating plain-text fallback as a security mechanism
Symptom: trying to hide sensitive info in modals — but plain-text shows everything.
Cause: fallback means "readers can still read," not "others can't see."
Fix: sensitive data shouldn't enter markdown — it shouldn't be in the doc at all.
I3. Using unlabeled emoji as structural signals
Symptom: plain-text readers see emojis without context, can't understand structure.
Cause: emojis carry visual meaning; in plain text they're just characters.
Fix: use Callout etc. as structured containers.
J. Performance
J1. Too many interact blocks on one page
Symptom: slow page load; operations feel laggy.
Cause: each interact block hydrates independently; hydration cost adds up.
Fix: merge related interacts via namespace sharing; or split across multiple pages.
J2. Charts with many data points + high-frequency timers
Symptom: dragging sliders / timer updates cause browser lag.
Cause: each frame re-renders N data points.
Fix: reduce points + increase step (lower frame rate).
K. AI / LLM generation
K1. LLM treats mini DSL as JS
Symptom: generated computed has Math.sin / if/else / function.
Cause: LLM defaults to JS knowledge.
Fix: in prompt explicitly say: "use Rho mini DSL — NOT JavaScript. Functions: pow, sqrt, sin, cos, abs, ... See [Rho LLM Protocol]."
K2. LLM forgets namespace
Symptom: multiple interact blocks meant to share state but actually isolated.
Cause: LLM may not know namespace is required.
Fix: give namespace examples in the prompt. See Rho AI / LLM Guide.
K3. LLM writes invalid directive order
Symptom: :::card outside a layout / :::step outside a stepper.
Cause: LLM doesn't know directives need their parent containers.
Fix: prompt emphasizes "directives must be inside their proper container."
See also
- Plain-text fallback principle — Fallback philosophy
- Nested DSL — Nesting rules
- Shared state (namespace) — State sharing
- "Common pitfalls" sections in each capability doc
- Examples gallery — See correct usage patterns