fix: prevent layout shifts in code blocks during streaming#216
fix: prevent layout shifts in code blocks during streaming#216AnthonyRonning merged 1 commit intomasterfrom
Conversation
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>
WalkthroughCSS 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
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)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
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 unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 hintsThe 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 WindowsSetting 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 codedeclarations — consolidate to reduce cascade surprises
.markdown-body pre codeis 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 scopingturning 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:
- 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; + } +}
- 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.
📒 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
anywhen 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 fixThe w-full wrapper avoids inline-block width calc edge cases and helps keep the scroll container stable while streaming.
|
@TestFlight build |
|
🚀 TestFlight deployment triggered! Check the Actions tab for progress. |
|
✅ TestFlight deployment completed successfully! |
|
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. |
|
🚀 TestFlight deployment triggered! Check the Actions tab for progress. |
|
✅ TestFlight deployment completed successfully! |
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:
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
Bug Fixes