fix: polish chat thread layout, scroll architecture, and accessibility#42
fix: polish chat thread layout, scroll architecture, and accessibility#42
Conversation
… selector styles - Add disableHoverablePopup to Tooltip components across sidebar, header, user menu, model selector, chat input, and message actions to prevent unwanted hover-to-popup behavior - Make header background transparent at 2xl breakpoint for cleaner wide layouts - Remove redundant bg-background from layout wrapper (header handles it) - Style model selector triggers with text-lg and font-normal - Support render prop on Button with automatic nativeButton: false - Add ChatGPT header analysis research docs Co-authored-by: Cursor <cursoragent@cursor.com>
…ip position Remove hasScrollAnchor prop threading through message components (conversation, message, message-user, message-assistant, multi-conversation) and the min-h-scroll-anchor class. Change model selector tooltip side from bottom to right. Co-authored-by: Cursor <cursoragent@cursor.com>
…mantics Add skip-to-content link, ARIA live regions, and sr-only message labels for screen readers. Switch message containers to article elements with data-turn/data-message-id attributes. Introduce CSS custom properties (--thread-content-max-width, --thread-content-margin) for consistent thread width. Add mask-based reveal animation for message actions. Bump reasoning and loader text sizes. Add thinking states test page and a11y announce utility. Update design reference docs. Co-authored-by: Cursor <cursoragent@cursor.com>
…rchitecture Introduce ScrollRoot/ScrollRootContent primitives that wrap use-stick-to-bottom with a React context, replacing the ChatContainer abstraction. Simplify chat and multi-chat layouts with sticky positioning instead of motion layout animations. Update ScrollButton with a dual-context hook for backward compatibility. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace static --thread-content-max-width (48rem) and --thread-content-margin (1.5rem) with responsive container-query values matching ChatGPT's system: - Base (narrow): 40rem max-width, 1rem margin - @container main ≥640px: 1.5rem margin - @container main ≥1024px: 48rem max-width, 4rem margin Update fallback values in all consuming components to match the new base. Co-authored-by: Cursor <cursoragent@cursor.com>
Remove bg-background from header, making it fully transparent so content scrolls behind it. Add a subtle 1px bottom shadow using the --border token for visual separation. At 2xl viewport (≥1536px), the separator also disappears for a fully immersive feel. Remove backdrop-blur-sm as ChatGPT doesn't use blur on the header. Co-authored-by: Cursor <cursoragent@cursor.com>
Add more breathing room below the last message before the composer (32px → 96px), matching ChatGPT's generous pb-25 (100px) spacing. Applied to both single-chat and multi-model conversation views. Co-authored-by: Cursor <cursoragent@cursor.com>
Messages now dissolve into the composer area via a 32px gradient overlay (from-background to transparent) positioned above the sticky input, matching ChatGPT's content-fade pattern. Applied to both single-chat and multi-model views. Co-authored-by: Cursor <cursoragent@cursor.com>
On touch devices (pointer: coarse), icon buttons grow from 32/36px to 40px for comfortable tap targets, and the header gets slightly more padding (8px → 10px). Follows Apple HIG guidance for minimum tap target sizes. Applied to message action buttons (copy, edit, regenerate), scroll-to-bottom button, and header inner padding. Co-authored-by: Cursor <cursoragent@cursor.com>
The composer was missing px-[var(--thread-content-margin)] which messages apply as inset padding. This caused the composer to render at the full max-width while messages were narrower by 2×margin — especially visible at the ≥1024px breakpoint where margin is 4rem (8rem total difference). Co-authored-by: Cursor <cursoragent@cursor.com>
Unify thread/composer width tokens and restore predictable overflow anchoring so scrolling and message actions stay stable across breakpoints and input modes. Co-authored-by: Cursor <cursoragent@cursor.com>
Refine thread/composer spacing and bottom fade behavior to prevent overlap artifacts while improving scroll button positioning and dark-mode composer shadow rendering. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the previous thread bottom soft-mask utility with a content-fade pseudo-element so the sticky composer fade is cleaner and easier to maintain. Co-authored-by: Cursor <cursoragent@cursor.com>
…nboarding Simplify textarea padding, make the sticky composer and disclaimer conditional on active conversation so the onboarding view stays clean. Align multi-chat bottom container with single-chat layout patterns. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryReplaced Improved accessibility with skip-to-content link, ARIA live regions for announcements, semantic Issues found:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[LayoutApp] --> B[ScrollRoot Context Provider]
B --> C[Header - transparent bg, scroll shadow]
B --> D[Main Content Area]
D --> E{Multi-model enabled?}
E -->|No| F[Chat Component]
E -->|Yes| G[MultiChat Component]
F --> H[ScrollRootContent]
G --> I[ScrollRootContent]
H --> J[Conversation - article elements]
I --> K[MultiConversation - article elements]
J --> L[Messages with semantic HTML]
K --> M[Response Cards]
F --> N[Sticky Composer with fade]
G --> O[Sticky Composer with fade]
N --> P[ScrollButton - dual context]
O --> P
style B fill:#e1f5ff
style H fill:#e1f5ff
style I fill:#e1f5ff
style P fill:#fff4e1
Last reviewed commit: 5b71d55 |
There was a problem hiding this comment.
5 issues found across 39 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="components/ui/scroll-button.tsx">
<violation number="1" location="components/ui/scroll-button.tsx:24">
P2: `useStickToBottomContext()` is called conditionally, which violates the Rules of Hooks and can break if the provider tree changes (hook order changes across renders). Prefer splitting into two components or a context consumer that selects which hook-based component to render so hooks are always called unconditionally.</violation>
</file>
<file name="lib/a11y/announce.ts">
<violation number="1" location="lib/a11y/announce.ts:7">
P3: This exported `announce` utility is currently unused in the codebase, so it’s dead code. Consider either wiring it into the new a11y flow or removing it until there’s a call site.</violation>
</file>
<file name="app/layout.tsx">
<violation number="1" location="app/layout.tsx:65">
P2: The skip-to-content link targets `#main-content`, but no element in the app has that id, so the link won’t move focus. Add `id="main-content"` to the main content container (e.g., the primary `<main>` element) or update the link target to an existing id.</violation>
</file>
<file name=".agents/design/chatgpt-reference/chatgpt-conversation-html-structure.md">
<violation number="1" location=".agents/design/chatgpt-reference/chatgpt-conversation-html-structure.md:18">
P3: Use `data-testid` consistently for test selectors to match the attribute table and avoid confusing readers about the actual DOM attribute name.</violation>
</file>
<file name="app/components/chat/reasoning.tsx">
<violation number="1" location="app/components/chat/reasoning.tsx:1">
P2: Add the "use client" directive so this hook-using component is treated as a Client Component in the App Router.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
.agents/design/chatgpt-reference/chatgpt-conversation-html-structure.md
Outdated
Show resolved
Hide resolved
- Use explicit @[64rem]/main container query instead of @lg/main - Fix skip link href to #main to match actual landmark id - Remove unused aria-live regions and lib/a11y/announce utility - Delete old app/components/chat/reasoning.tsx (superseded by components/ui/reasoning.tsx) - Refactor ScrollButton to avoid conditional hook call (split into inner + legacy wrapper) - Fix data-testid attributes in ChatGPT reference doc Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Comprehensive UI/UX overhaul of the chat thread layout, scroll behavior, and accessibility across single-chat and multi-model views. This branch aligns our layout system with ChatGPT-style patterns using container queries, CSS custom properties, and sticky positioning.
Scroll Architecture
ChatContainerwithScrollRoot/ScrollRootContentprimitives wrappinguse-stick-to-bottomvia React contexthasScrollAnchor,min-h-scroll-anchor) that was causing layout instabilityThread Layout System
--thread-content-max-width,--thread-content-margin) for consistent thread widthChat Composer
Header
Accessibility
<article>elements withdata-turn/data-message-idattributesa11y/announceutilityTouch & Responsive
Other
/test/thinking-states)ui-component-matchskill for design reference workflowsTest plan
/test/thinking-statespage renders all thinking state variantsMade with Cursor
Summary by cubic
Polished the chat thread layout and scroll behavior with a new ScrollRoot, aligned thread/composer widths, and improved accessibility and header visuals across single and multi-model chats. Also cleaned up container queries, the skip link, and scroll button hooks.
New Features
Refactors
Written for commit 82e5a28. Summary will update on new commits.