Skip to content

fix: prevent layout shifts in code blocks during streaming#216

Merged
AnthonyRonning merged 1 commit intomasterfrom
fix-jumpy-codeblocks
Aug 26, 2025
Merged

fix: prevent layout shifts in code blocks during streaming#216
AnthonyRonning merged 1 commit intomasterfrom
fix-jumpy-codeblocks

Conversation

@AnthonyRonning
Copy link
Copy Markdown
Contributor

@AnthonyRonning AnthonyRonning commented Aug 26, 2025

Fixed critical issue where code blocks would cause the entire layout to jump/flash during message streaming, particularly on mobile. This was causing accessibility issues.

The root cause was that as content streamed in, the browser would recalculate whether horizontal scrolling was needed, changing the code block height and causing layout reflow.

Changes:

  • Set code elements to use width: max-content with min-width: 100% to maintain consistent dimensions
  • Changed display from inline-block to block for proper width calculations
  • Set overflow-x: visible on code elements to let parent handle scrolling
  • Added proper width constraints to pre elements
  • Wrapped PreCode component in full-width container
  • Disabled transitions to prevent animation flicker

This ensures code blocks maintain consistent height regardless of content length, preventing the disorienting jumping effect during streaming.

Fixes #209

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Style

    • Improved Markdown code block layout: full-width display with horizontal scrolling only, no vertical scroll.
    • Ensured code content doesn’t clip and respects padding for consistent sizing.
    • Disabled transitions on highlighted code for snappier rendering.
  • Bug Fixes

    • Prevented layout breakage by allowing long words/tokens to wrap in Markdown.
    • Removed unintended scrollbars and clipping inside code blocks for clearer readability.

Fixed critical issue where code blocks would cause the entire layout to jump/flash
during message streaming, particularly on mobile. This was causing accessibility
issues including triggering seizures for some users.

The root cause was that as content streamed in, the browser would recalculate
whether horizontal scrolling was needed, changing the code block height and
causing layout reflow.

Changes:
- Set code elements to use width: max-content with min-width: 100% to maintain consistent dimensions
- Changed display from inline-block to block for proper width calculations
- Set overflow-x: visible on code elements to let parent handle scrolling
- Added proper width constraints to pre elements
- Wrapped PreCode component in full-width container
- Disabled transitions to prevent animation flicker

This ensures code blocks maintain consistent height regardless of content length,
preventing the disorienting jumping effect during streaming.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 26, 2025

Walkthrough

CSS and React component updates adjust Markdown code block layout and scrolling. chat.css modifies overflow, sizing, and transitions for pre/code blocks. markdown.tsx wraps code blocks in a full-width container, enables horizontal scrolling on pre, and adds word-break styling to the Markdown container.

Changes

Cohort / File(s) Summary
Markdown code-block CSS
frontend/src/chat.css
Adjusted pre/code overflow (horizontal auto, vertical hidden), enforced full-width sizing with box-sizing, set code to block display with min-width: 100%, removed transitions on .hljs/pre, and made overflow visible within code elements.
Markdown rendering component
frontend/src/components/markdown.tsx
Wrapped code blocks in a full-width div; applied w-full and overflow-x-auto to pre; retained copy button; added wordBreak: "break-word" to Markdown container.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant CV as ChatView
  participant MD as MarkdownRenderer
  participant PC as PreCode
  participant CSS as Browser/CSS

  U->>CV: Open chat with Markdown message
  CV->>MD: Render Markdown content
  MD->>PC: Render code block component
  Note over PC,CSS: New: wrap in w-full div and pre with overflow-x-auto
  PC->>CSS: Apply updated CSS rules (pre/code sizing & overflow)
  CSS-->>U: Display full-width code block with horizontal scroll

  U->>PC: Click "Copy" button
  PC-->>U: Clipboard updated (unchanged behavior)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I hop through code with whiskers keen,
Wide blocks stretch in glossy sheen.
No vertical scroll to steal the show,
But sideways slides let syntax flow.
A tap to copy—swift and bright—
Carrots of code, formatted right! 🥕💻

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-jumpy-codeblocks

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

This PR addresses a critical accessibility and user experience issue where code blocks would cause layout shifts and flashing during message streaming, particularly problematic on mobile devices. The changes implement a comprehensive solution across two files to prevent browser reflow calculations from affecting code block dimensions as content streams in character by character.

The fix works by establishing a two-tier sizing strategy: the parent pre element maintains fixed dimensions and handles horizontal scrolling, while child code elements use width: max-content with min-width: 100% to accommodate any content length without triggering layout recalculation. In markdown.tsx, the PreCode component is wrapped in a full-width container with proper overflow handling, and the main Markdown container receives wordBreak: 'break-word' styling. The corresponding CSS changes in chat.css modify the display properties from inline-block to block, set overflow-x: visible on code elements while maintaining overflow-x: auto on pre elements, and disable transitions with transition: none to prevent animation flicker.

This architectural approach ensures code blocks maintain stable dimensions regardless of content length, integrating well with the existing Tailwind CSS system and ReactMarkdown rendering pipeline. The solution preserves all existing functionality including the copy-to-clipboard feature while eliminating the disorienting visual effects that were causing seizures for some users.

Confidence score: 4/5

  • This PR addresses a critical accessibility issue with a well-architected solution that maintains existing functionality
  • Score reflects thorough problem analysis and systematic fix across both component structure and CSS styling
  • Pay close attention to the CSS overflow handling changes to ensure they work correctly across all browsers and devices

2 files reviewed, no comments

Edit Code Review Bot Settings | Greptile

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
frontend/src/chat.css (4)

774-786: Solid container-side scroll and width stabilization; add mobile scrolling/overscroll hints

The switch to overflow-x: auto on pre plus width/min-width: 100% and box-sizing: border-box is the right fix to prevent width re-computation and height jitter. On iOS/Android, consider adding momentum scrolling and overscroll containment to isolate horizontal panning from the page.

Apply this diff inside the same rule:

 .markdown-body .highlight pre,
 .markdown-body pre {
   padding: 16px 16px 8px 16px;
   overflow-x: auto;
   overflow-y: hidden;
   font-size: 85%;
   line-height: 1.45;
   border-radius: 6px;
   direction: ltr;
   width: 100%;
   min-width: 100%;
   box-sizing: border-box;
+  -webkit-overflow-scrolling: touch; /* smoother horizontal scroll on iOS */
+  overscroll-behavior-x: contain;    /* avoid parent/page scroll interference */
 }

Please verify on mobile Safari and Chrome Android while content streams to ensure no parent scroll bounce occurs.


790-801: Block-level code with max-content width is correct; minor note on scrollbar appearance on Windows

Setting the inner code to display: block, white-space: pre, width: max-content with min-width: 100% ensures stable height while streaming and delegates horizontal scroll to the parent pre. One caveat: on Windows (non-overlay scrollbars), when a very long line first appears, the horizontal scrollbar may pop in and increase container height by a few pixels. If that micro-shift is still noticeable in QA, consider forcing the gutter by using overflow-x: scroll on the pre container.

You can toggle this later if needed:

-.markdown-body .highlight pre,
-.markdown-body pre {
-  overflow-x: auto;
+.markdown-body .highlight pre,
+.markdown-body pre {
+  overflow-x: scroll; /* reserves scrollbar space on Windows */

Trade-off: always-visible scrollbar on some platforms.


1083-1088: Duplicate .markdown-body pre code declarations — consolidate to reduce cascade surprises

.markdown-body pre code is defined twice (earlier at Lines 788–801 and here again). While the later rule wins (padding: 1em, box-sizing: border-box), the duplication increases maintenance burden and makes future edits brittle.

Unify into a single declaration near the first occurrence:

-.markdown-body pre code {
-  display: block;
-  overflow-x: visible;
-  padding: 1em;
-  box-sizing: border-box;
-}
+/* Single, canonical rule for code inside pre */
+.markdown-body pre code,
+.markdown-body pre tt {
+  display: block;
+  margin: 0;
+  padding: 1em;
+  line-height: inherit;
+  background-color: transparent;
+  border: 0;
+  white-space: pre;
+  width: max-content;
+  min-width: 100%;
+  overflow-x: visible;
+  box-sizing: border-box;
+}

This keeps intent in one place and avoids padding/white-space drift.


1094-1099: Disabling transitions is fine for flicker prevention; consider motion-preference scoping

turning off transitions on pre/hljs prevents animation flicker during streaming. If you want to retain subtle theming transitions elsewhere, scope to users who prefer reduced motion or limit to properties known to flicker (e.g., background-color).

Two options:

  1. Respect user preference:
-.markdown-body .hljs,
-.markdown-body pre {
-  @apply text-foreground bg-muted;
-  transition: none;
-}
+@media (prefers-reduced-motion: reduce) {
+  .markdown-body .hljs,
+  .markdown-body pre {
+    transition: none;
+  }
+}
  1. Limit the transitioned properties globally:
-.markdown-body .hljs,
-.markdown-body pre {
-  @apply text-foreground bg-muted;
-  transition: none;
-}
+.markdown-body .hljs,
+.markdown-body pre {
+  @apply text-foreground bg-muted;
+  transition-property: none; /* or: background-color, color */
+}
```<!-- review_comment_end -->

</blockquote></details>
<details>
<summary>frontend/src/components/markdown.tsx (2)</summary><blockquote>

`255-258`: **Pre container matches CSS intent; consider overscroll containment for mobile**

The pre has w-full overflow-x-auto which aligns with chat.css. To further isolate horizontal panning from the page on mobile, consider adding overscroll containment.


```diff
-<pre ref={ref} className="w-full overflow-x-auto">
+<pre ref={ref} className="w-full overflow-x-auto overscroll-x-contain">

This mirrors the CSS suggestion and prevents the page from catching horizontal gestures inside code blocks.


660-662: Prefer overflowWrap to improve cross-browser long-token wrapping (non-code content)

wordBreak: "break-word" works but is non-standard-ish; overflowWrap is the standards-based property and plays nicer with CJK and punctuation. Using both maximizes compatibility while code blocks remain unaffected due to white-space: pre.

   style={{
     fontSize: `${props.fontSize ?? 16}px`,
     fontFamily: props.fontFamily || "inherit",
-    wordBreak: "break-word"
+    wordBreak: "break-word",
+    overflowWrap: "anywhere"
   }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between dd650a2 and 5b1ed49.

📒 Files selected for processing (2)
  • frontend/src/chat.css (3 hunks)
  • frontend/src/components/markdown.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use 2-space indentation, double quotes, and a 100-character line limit for formatting
Use camelCase for variable and function names
Use try/catch with specific error types for error handling

Files:

  • frontend/src/components/markdown.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript typing and avoid any when possible

Files:

  • frontend/src/components/markdown.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build-ios
  • GitHub Check: build-linux
  • GitHub Check: browseruse-tests
  • GitHub Check: build-macos (universal-apple-darwin)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (1)
frontend/src/components/markdown.tsx (1)

226-226: Wrapping PreCode in a full-width container is the right structural fix

The w-full wrapper avoids inline-block width calc edge cases and helps keep the scroll container stable while streaming.

@AnthonyRonning
Copy link
Copy Markdown
Contributor Author

@TestFlight build

@github-actions
Copy link
Copy Markdown
Contributor

🚀 TestFlight deployment triggered! Check the Actions tab for progress.

@github-actions
Copy link
Copy Markdown
Contributor

✅ TestFlight deployment completed successfully!

@marksftw
Copy link
Copy Markdown
Contributor

Works great in Safari on Mac, iOS, and the TestFlight build.

Only small thing I noticed is that I can’t horizontally scroll in a code block until the full output is done. Understandable. I’m okay with that.

@github-actions
Copy link
Copy Markdown
Contributor

🚀 TestFlight deployment triggered! Check the Actions tab for progress.

@AnthonyRonning AnthonyRonning merged commit 6f54294 into master Aug 26, 2025
7 of 8 checks passed
@AnthonyRonning AnthonyRonning deleted the fix-jumpy-codeblocks branch August 26, 2025 18:33
@github-actions
Copy link
Copy Markdown
Contributor

✅ TestFlight deployment completed successfully!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Output gets jumpy

2 participants