Skip to content

fix(bidi): stop pinning paragraphs and quotes to a left-to-right base

MacroscopeApp / Macroscope - UI Consistency failed Aug 16, 2026 in 3m 52s

UI Consistency: 2 issues found

Two direction-inheritance gaps in the web markdown chrome, both introduced by the new per-block RTL resolution:

  • apps/web/src/index.css (L1797-1799): .chat-markdown pre is pinned to direction: ltr, but the .chat-markdown-codeblock wrapper around it is not. Inside an Arabic li, blockquote, or table cell the wrapper inherits RTL, so the justify-between code-block header mirrors while its physical pr-1.5/pl-3 padding does not. The table container is pinned as a whole (L1612) and mobile wraps the entire code block in an LTR view; suggested pinning .chat-markdown .chat-markdown-codeblock.
  • apps/web/src/components/ChatMarkdown.tsx (L566): dir="auto" on CollapsibleTrigger mirrors only the summary row; the panel body still inherits LTR from the Collapsible root and keeps its ps-6 inset on the left, so an Arabic details title and its body hang off opposite edges. Suggested moving dir="auto" to the Collapsible root.

Previously flagged items (raw-HTML-only plugin wiring, unconditional list end padding, dropped summary dir, table scroller direction, inline-code bidi isolation, the shared margin selector pinning p/blockquote) are resolved in this revision.

Details

Note

Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.

Reviewed files in scope: apps/web/src/index.css, apps/web/src/components/ChatMarkdown.tsx, apps/web/src/markdown-text-direction.ts, apps/web/src/markdown-text-direction.test.tsx. Mobile changes were read for cross-client consistency only.

Checks performed:

  • Traced dir prop forwarding through the p, li, ol, blockquote, td/th component overrides in ChatMarkdown (all spread props, so the plugin's dir reaches the DOM; the alert branch drops props but the plugin deliberately skips data-alert blockquotes).
  • Verified data-rtl-item naming survives hast property casing (dataRtlItem -> data-rtl-item) and matches the new CSS attribute selectors.
  • Checked the list gutter model: ul/ol pinned LTR, padding-inline-start unchanged for LTR lists, end-side gutter added only where an RTL item exists, and the task-list checkbox margin-inline reproducing the previous physical margins in LTR.
  • Checked table path: .chat-markdown-table-container pinned LTR covers the Base UI ScrollArea viewport and the footer chrome row; text-align: start on cells resolves against each cell's own dir="auto".
  • Checked inline code isolation (direction: ltr; unicode-bidi: isolate) against the RTL-paragraph neutral-reordering case.
  • Searched for other .chat-markdown descendants with physical-side layout that can now inherit RTL; the code-block chrome and the details panel inset were the two that mirror inconsistently.

🔍 Agent activity — 1 tool call

💭 Looking at the PR changes to see if they've resolved the earlier issues—the table container rule, inline code isolation, and list padding via the data-rtl-item attribute all seem to be in place. Now I'm checking the current head for any new problems, particularly around how the direction rules interact with RTL content. The list container is pinned to LTR with direction: ltr, but individual items have dir="auto" so they should resolve their own text direction independently—though I need to verify this doesn't break RTL item markers. nt this is — if it's a table cell, the start alignment works fine since the table container has direction ltr. For the MarkdownDetails component, I added dir="auto" and text-start, but now I'm noticing the CollapsibleTrigger is a flex row with a chevron icon that might not behave as intended in RTL contexts — the flex direction would flip but the chevron glyph itself might still point the wrong way. Also realizing the mobile files are outside the scope of these changes anyway. For the task list checkbox margins, I'm checking the conversion from the original shorthand to the new margin-inline approach — the values map correctly for LTR (left gets the negative gutter, right gets 0.35em), but I need to verify how this behaves when the list item has dir="auto" and resolves to RTL. The blockquote styling looks good with border-inline-start and padding-inline-start handling the directional rail properly. But I'm noticing line 1663 changed from a hardcoded padding-left: 1.25rem to using the CSS variable with a fallback — that's a subtle behavior shift I should investigate by looking at the actual file and checking what selector that applies to. I also need to verify how the data-rtl-item attribute works in the AST. Actually, there's another thing to consider: the rehypeAutoTextDirection plugin sets the dir property, but in ChatMarkdown with parseRawHtml enabled, the plugin runs after sanitization. Without raw HTML, the plugin array only contains the direction plugin, whereas before it was undefined. I should check if there were other rehype plugins being applied in the non-raw case that I might be missing. I'm noticing a potential issue with how direction properties cascade when there are nested lists. Setting direction: ltr on the parent ul or ol works fine, but when a list item has dir="auto", nested lists inside it might not behave as expected—the LTR direction could conflict with the RTL context of the parent li. For tables, I need to verify that the .chat-markdown-table-container class is actually applied to the outer wrapper, and I'm checking how text-align: start resolves for table cells with dir="auto". Since the cell's own direction determines how start aligns, an Arabic cell would correctly align right, which seems intentional. For task list items, the checkbox margin uses the list gutter variable, and when the item is RTL, the negative margin-inline-start hangs the checkbox into the right gutter as expected. The first-letter detection correctly identifies Arabic text in the task item to set the RTL flag. I'm noticing a minor divergence: firstLetter checks the first letter, but dir="auto" technically uses the first strong character, though this is acknowledged in the comments. Now I need to verify whether the plugin actually runs on data-alert blockquotes by checking the property name used in this codebase, and I should also look at whether other markdown renderers exist that use the .chat-markdown class without the plugin applied. I'm looking at specific sections of the ChatMarkdown file to understand the table container structure, details section, and plugin configuration across different line ranges. Looking at the CollapsibleTrigger with the chevron icon in a flex row — when the summary is in Arabic and the layout flips to RTL, the chevron ends up on the right side of the text, but it still points rightward when collapsed, which creates a visual inconsistency since in RTL a collapsed disclosure should point left instead. The rotation animation works fine for the open state, pointing downward, but that initial chevron direction in the collapsed state feels off. I'm weighing whether this counts as a concrete regression or just an aesthetic inconsistency. Before this change, Arabic details were rendered LTR, so this RTL mirroring is actually new behavior. The real issue is that the chevron iconography needs special handling — either applying a 180-degree rotation in RTL mode or flipping it horizontally so that when the 90-degree open rotation is applied, it points upward instead of downward. It's a subtle but important detail for proper RTL support. Now I'm checking the padding on the CollapsiblePanel content — the ps-6 is already using logical spacing, which is correct. In fact, if I put dir="auto" only on the root and remove it from the trigger, the root would skip over descendants that have their own dir attribute set, so it would read the first strong character from the summary text itself — which should give me the right directionality for the whole component without the mismatch between the chevron and the body indent. I should verify that the Collapsible root actually forwards the dir prop through to the rendered div, and then check whether this inconsistency is worth fixing — it's a real issue introduced by the change, though the visual impact depends on how the RTL layout actually renders with the current padding and alignment. Let me scan for other potential problems with the CSS rules, particularly around how inline styles and CSS variables interact with directional properties. Adding more styles... I should also check if ChatMarkdown is the only place where ReactMarkdown gets used with these rehypePlugins, or if there are other instances where the configuration might be overridden. I need to check whether the custom li renderer in ChatMarkdown is passing through the dir prop, especially for task list items that might have their own checkbox handler. The blockquote renderer for alerts also needs to be examined to see if it's properly forwarding props rather than destructuring them away. The renderers pass through the dir attribute for regular elements like paragraphs, list items, and non-alert blockquotes. For alert blockquotes, the plugin sets a dataAlert property that survives sanitization since the schema allows it on blockquotes. I'm checking whether this property persists correctly in both the raw path and the non-raw path where remarkGithubAlerts is applied. The alert container itself uses physical padding and borders, which is fine since it's not RTL-aware. List items properly pass through the dir attribute, and I'm now examining the interaction between the global direction: ltr rule on lists and RTL task items — specifically how the checkbox positioning works with margin-inline-start in RTL contexts. I'm also considering whether the text-align: start change on table cells might be over-reaching: GFM tables with mixed Arabic and English content could become visually ragged if cells align differently per-language, whereas previously they all aligned left. This could be a regression depending on the desired behavior. The MarkdownDetails trigger with dir="auto" and text-start should work correctly for Arabic text, and I'm continuing to verify other components like ScrollArea styling. I'm noticing that the CSS rule for .chat-markdown pre sets direction to left-to-right, and since shiki generates <pre class="shiki"> elements, that selector applies to the code blocks. The .chat-markdown-codeblock wrapper also contains a header with the code title. When a fenced code block appears inside an RTL list item or blockquote, the wrapper div inherits the RTL direction. The header uses flexbox with justify-between, which reverses the flex direction in RTL, so the title and action buttons swap sides. But the padding values like pr-1.5 and pl-3 are physical units that don't flip with the direction change—so now I'm seeing an inconsistency where the layout mirrors but the padding doesn't adjust accordingly, creating misalignment in the code block header when it's nested in RTL content. The PR author pinned the pre element itself to direction: ltr on web, but the wrapper div (.chat-markdown-codeblock) that contains the header and pre isn't pinned. This means the header row still mirrors in RTL contexts, whereas the table container got the full treatment with direction: ltr applied to the whole thing including its footer chrome. The fix should apply direction: ltr to the code block wrapper, not just the inner pre element, to match how tables are handled. The code block header uses padding and flexbox to maintain proper alignment in both LTR and RTL contexts, though the header itself doesn't inherit the direction property. The pre elements are explicitly set to LTR direction to ensure the scroll starts from the left, which is applied consistently across both the general markdown code blocks and the specific chat markdown code blocks. The fix would be to apply direction: ltr to the .chat-markdown .chat-markdown-codeblock container itself rather than just the pre element, matching the approach used for table containers. This is important because code blocks commonly appear inside RTL list items or blockquotes in chat (like numbered lists with code examples), so the direction inheritance from parent RTL elements is a realistic scenario introduced by the recent changes. For inline elements like file-link chips within RTL paragraphs, the mirroring behavior is probably acceptable. Now I'm checking the MarkdownDetails component — the trigger has dir="auto" while the panel uses ps-6 padding, which means the trigger gets mirrored in RTL but the body content inherits LTR from the document root, creating a mismatch where the chevron and title appear on the right but the body stays indented from the left. The solution is to move dir="auto" to the Collapsible root element instead of just the trigger, so both the trigger and panel resolve their direction together. Since the root's auto resolution reads the first strong character from the trigger text (skipping descendants with their own dir), this should work correctly. The Base UI Collapsible component spreads unknown props to its rendered element, so forwarding dir should be fine. I'll suggest this as a small unified diff since the root element isn't in the current diff but the trigger is. I'm weighing whether this is worth flagging—I've already reported three rounds of issues and they've fixed them, so I don't want to be too aggressive with noise. But the check defaults to failure and this is a concrete violation introduced by the changed line: the dir="auto" on the trigger creates a mirrored header over a non-mirrored body, plus the chevron points right in RTL. Let me verify the codeblock issue is solid and scan for other violations of equal weight. Looking at other candidates: the MarkdownTable container direction is fine, but the Alert callout has a similar problem—the container stays LTR while individual paragraphs get dir="auto", creating a mix of left border rail with right-aligned text in Arabic. ely, so the physical border would stay on the left even in RTL. The PR updated the blockquote's border to use logical properties but left the alert variant with physical borders—an inconsistency since alerts are blockquote-based. Now I'm checking the other elements: headings get dir="auto", which is fine, and the rest like hr and paragraphs look good. So my main findings are the codeblock chrome mirroring issue and the details panel's inconsistent handling between the indent and trigger. Let me verify whether the codeblock is actually affected by RTL ancestors. The ancestors that could resolve to RTL are list items with dir="auto", blockquotes with dir="auto", and table cells that might have dir="auto" even though the table container is pinned to LTR. A codeblock inside a table cell is definitely possible. This confirms the issue. The mobile client explicitly wraps code blocks in direction: "ltr", so the web version should match that for consistency across clients. Now I need to find the right line numbers in the diff to add comments—they have to be on the RIGHT side of the changed lines. The codeblock fix is around line 1797-1799 in index.css where direction: ltr; is being added to the pre element. Nested lists in RTL items get left padding even though they're marked as LTR, creating an extra 1.25rem gap on the left side—visually minor but the comment acknowledges this trade-off. Top-level Arabic lists also have unnecessary left inset since the ul itself is LTR, though this is acceptable for the overall approach. The holdsRightToLeftItem check correctly looks only at direct children, but there's a potential issue: the plugin sets dataRtlItem on lists after rehypeSanitize runs, which could strip that attribute in the raw-HTML path. Actually, that's fine since the plugin runs after sanitization. The dataRtlItem property serializes correctly to data-rtl-item in the hast-to-jsx output, which the tests confirm. Now checking whether dir="auto" applies properly to list items inside .chat-markdown-details. Looking at the padding issue more carefully — ps-6 on line 577 means padding-inline-start, which resolves to left padding in LTR context since the panel inherits the Collapsible root's direction. The fix is to move dir="auto" to the Collapsible root itself, then provide a unified diff showing the addition. Since this spans multiple hunks across the component structure, I'll describe the fix in prose instead. Now I need to write out the two comments concisely — the first one addresses the pre element being pinned LTR while the MarkdownCodeBlock wrapper uses physical padding properties that flip incorrectly in RTL contexts. Adding a CSS rule... The hunk includes the replaced className and three added lines, so line 566 is definitely part of the diff. Now I'm marking this as a failure due to the findings and posting the review.

1. modify_pr — Two direction-inheritance gaps in the web markdown chrome, both introduced by this PR's new RTL blocks.


Agent Credits: 110 credits