Skip to content

Why Markdown Breaks in Right-to-Left AI Output

10 min read · updated August 11, 2026

The model returned well-formed Markdown in Arabic or Hebrew. The rendered HTML has bullets on the left, a table with its columns in the wrong order, a bolded phrase whose punctuation jumped, and a code block with the semicolons on the wrong side. All of it comes from one omission, and one wrapper fixes most of it.

The gap in the format

CommonMark defines block and inline structure and says nothing about text direction. There is no syntax for it, no attribute, no convention. A Markdown document is direction-neutral, which means the direction comes entirely from whatever HTML the renderer emits and whatever container that HTML lands in. If nothing declares a direction, the container defaults to left-to-right and every neutral character in the document resolves against that.

That single default is the cause of most of what follows. The text itself is stored correctly — logical order, right characters — and the bidi algorithm is applying a paragraph direction nobody meant to choose. It is worth internalising that these are not Markdown bugs. The same content pasted into a dir="rtl" element is fine.

The constructs that break, one by one

List markers

Bullets and numbers are drawn by the ::marker pseudo-element, which sits on the inline-start side of the list item — the left in an LTR container. An Arabic list with left-hand bullets is the most visible symptom and the one people report. Setting direction on the <ul> or <ol> moves the markers to the right and moves the indentation with them, provided the indentation is padding-inline-start and not padding-left. Ordered list numbers stay left-to-right within themselves, which is correct.

Emphasis at the start of a line

**bold** at the start of an RTL line renders as a <strong> element whose boundaries are new run boundaries. The asterisks themselves are consumed by the parser, so the visible problem is the punctuation immediately after the bolded phrase — a colon in a **Label:** value line is a neutral between the end of a strong run and the start of another, and it resolves by N1 or N2 and can land on the far side of the label. In an LTR container it will reliably go to the wrong place; in a correctly directed container it will not.

Links

[text](url) becomes an anchor whose text may be RTL and whose href is not displayed. The visible failure is when the link text is the URL, which is common in model output. That case has enough going on to deserve its own page: why RTL layout breaks on long URLs.

Code, inline and fenced

Source code is left-to-right whatever surrounds it. An unisolated <code> span inside RTL prose will move its brackets and operators; an unisolated <pre> block will right-align and scatter the punctuation of every line. Both need dir="ltr" unconditionally, and the fenced block needs text-align: left as well, because inheriting text-align: start from an RTL ancestor right-aligns code that is left-to-right.

Blockquotes and horizontal rules

The blockquote’s vertical rule is drawn with a border. Written as border-left it stays on the left in an RTL document, which is the wrong side; written as border-inline-start it follows. Nested blockquotes compound it. The same applies to any decoration hung off a physical side anywhere in your Markdown stylesheet.

Tables, where the fix is also a trap

Setting dir="rtl" on a <table> reverses the visual order of the columns: the first column in source order is drawn at the right. For an Arabic table that is exactly right — the reader starts at the right, so the first column should be there.

It is also how you produce a genuinely broken table. Model output frequently mixes an RTL label column with columns of code identifiers, version numbers, English enum values or file paths. Under a table-wide RTL direction, those cells inherit RTL and their contents resolve against it, so a cell containing max_tokens: 4096 or a path with slashes reorders inside the cell. The fix is a direction on the table for column order and an isolate on the cells whose content is left-to-right.

<table dir="rtl">
  <thead>
    <tr><th>الوصف</th><th dir="ltr">Parameter</th></tr>
  </thead>
  <tbody>
    <tr>
      <td>…</td>
      <td dir="ltr" style="unicode-bidi: isolate; text-align: left">
        max_tokens
      </td>
    </tr>
  </tbody>
</table>

The column alignment markers in the Markdown table syntax — :---, ---: — are another physical-side trap: they emit text-align: left and right, which do not mirror. A model asked for a right-aligned numeric column produces markers that pin the numbers to a physical side, and in an RTL table that is the side the reader does not start from. If you control the renderer, map those markers to start and end when the table is RTL.

Fixing it in the renderer

  1. Wrap the rendered HTML in a container that declares direction and language explicitly — dir="rtl" lang="ar" — set from the language you requested, not detected from the output.
  2. Add unicode-bidi: plaintext to paragraphs, list items and table cells, so each block resolves its own direction from its own first strong character. This is what makes a mixed-language document behave, and it is one line.
  3. Force dir="ltr" and text-align: left on <pre> and <code>, with unicode-bidi: isolate on the inline form.
  4. Audit the Markdown stylesheet for physical properties. Every left, right, margin-left, padding-right, border-left and text-align: left is a candidate for the logical equivalent.
  5. Test with a document that mixes both directions in every construct — an RTL list containing an English item, a table with a code column, a heading with a trailing bracketed English gloss. A pure-Arabic test document passes while the real content fails.

What to ask the model for

There is little point asking a model to solve a problem the format cannot express. What does help is constraining it away from the constructs that are hardest to render, and telling it not to hand-fix direction itself.

Reply in Arabic, in Markdown.

- Do not insert Unicode bidi control characters. The renderer
  sets direction.
- Keep any Latin-script identifier, path, URL or number inside
  a backtick code span.
- Do not use table column alignment markers (:--- or ---:).
- Do not use nested lists more than two levels deep.

The backtick instruction does double duty: it is the one Markdown construct that maps cleanly onto the isolation you need, so an identifier in a code span arrives already marked as “left-to-right island” and your renderer can act on it without guessing. It also stops the model helpfully translating parameter names.