fix: clean up chat UI layout and align theme to neutral white
- Replace beige background with clean neutral white (matches Grok aesthetic) - Remove hardcoded hex colors in thread.tsx - use CSS variables throughout - Remove scroll-to-bottom button that showed incorrectly after auto-send - Chat container now integrates visually with the page instead of floating Made-with: Cursor
This commit is contained in:
@@ -44,44 +44,38 @@
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
/* Vib'n warm beige background from logo */
|
||||
--background: oklch(0.96 0.015 80);
|
||||
--foreground: oklch(0.25 0.02 230);
|
||||
/* Slightly warmer white for cards */
|
||||
--card: oklch(0.99 0.01 80);
|
||||
--card-foreground: oklch(0.25 0.02 230);
|
||||
--popover: oklch(0.99 0.01 80);
|
||||
--popover-foreground: oklch(0.25 0.02 230);
|
||||
/* Vib'n dark blue from logo text */
|
||||
--primary: oklch(0.35 0.08 230);
|
||||
--primary-foreground: oklch(0.99 0 0);
|
||||
--secondary: oklch(0.92 0.015 80);
|
||||
--secondary-foreground: oklch(0.35 0.08 230);
|
||||
--muted: oklch(0.92 0.015 80);
|
||||
--muted-foreground: oklch(0.50 0.02 230);
|
||||
--accent: oklch(0.92 0.015 80);
|
||||
--accent-foreground: oklch(0.35 0.08 230);
|
||||
--radius: 0.5rem;
|
||||
--background: oklch(0.985 0 0);
|
||||
--foreground: oklch(0.09 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.09 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.09 0 0);
|
||||
--primary: oklch(0.09 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.94 0 0);
|
||||
--secondary-foreground: oklch(0.09 0 0);
|
||||
--muted: oklch(0.94 0 0);
|
||||
--muted-foreground: oklch(0.50 0 0);
|
||||
--accent: oklch(0.94 0 0);
|
||||
--accent-foreground: oklch(0.09 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
/* Softer borders on warm background */
|
||||
--border: oklch(0.88 0.015 80);
|
||||
--input: oklch(0.88 0.015 80);
|
||||
--ring: oklch(0.60 0.08 230);
|
||||
/* Vib'n brand colors for charts */
|
||||
--chart-1: oklch(0.70 0.15 60); /* Orange */
|
||||
--chart-2: oklch(0.70 0.12 210); /* Light blue */
|
||||
--chart-3: oklch(0.55 0.10 220); /* Medium blue */
|
||||
--chart-4: oklch(0.40 0.08 230); /* Dark blue */
|
||||
--chart-5: oklch(0.75 0.15 70); /* Yellow */
|
||||
/* Sidebar with warm tint */
|
||||
--sidebar: oklch(0.98 0.01 80);
|
||||
--sidebar-foreground: oklch(0.30 0.02 230);
|
||||
--sidebar-primary: oklch(0.35 0.08 230);
|
||||
--sidebar-primary-foreground: oklch(0.99 0 0);
|
||||
--sidebar-accent: oklch(0.92 0.015 80);
|
||||
--sidebar-accent-foreground: oklch(0.35 0.08 230);
|
||||
--sidebar-border: oklch(0.88 0.015 80);
|
||||
--sidebar-ring: oklch(0.60 0.08 230);
|
||||
--border: oklch(0.88 0 0);
|
||||
--input: oklch(0.88 0 0);
|
||||
--ring: oklch(0.70 0 0);
|
||||
--chart-1: oklch(0.70 0.15 60);
|
||||
--chart-2: oklch(0.70 0.12 210);
|
||||
--chart-3: oklch(0.55 0.10 220);
|
||||
--chart-4: oklch(0.40 0.08 230);
|
||||
--chart-5: oklch(0.75 0.15 70);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.09 0 0);
|
||||
--sidebar-primary: oklch(0.09 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.94 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.09 0 0);
|
||||
--sidebar-border: oklch(0.88 0 0);
|
||||
--sidebar-ring: oklch(0.70 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
@@ -91,14 +91,14 @@ function AtlasChatInner({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col rounded-2xl overflow-hidden bg-[#fdfdfd] dark:bg-[#141414] ring-1 ring-[#e5e5e5] dark:ring-[#2a2a2a]" style={{ height: "600px" }}>
|
||||
<div className="flex flex-col rounded-2xl overflow-hidden bg-card ring-1 ring-border" style={{ height: "600px" }}>
|
||||
{/* Minimal header bar */}
|
||||
<div className="flex items-center justify-end px-4 py-2.5 shrink-0 border-b border-[#e5e5e5] dark:border-[#2a2a2a]">
|
||||
<div className="flex items-center justify-end px-4 py-2.5 shrink-0 border-b border-border">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleReset}
|
||||
className="h-7 w-7 p-0 text-[#9a9a9a] dark:text-[#6b6b6b] hover:text-[#0d0d0d] dark:hover:text-white"
|
||||
className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground"
|
||||
title="Start over"
|
||||
>
|
||||
<RotateCcw className="w-3.5 h-3.5" />
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
ThreadPrimitive,
|
||||
} from "@assistant-ui/react";
|
||||
import {
|
||||
ArrowDownIcon,
|
||||
ArrowUpIcon,
|
||||
CheckIcon,
|
||||
ChevronLeftIcon,
|
||||
@@ -26,7 +25,7 @@ import { MarkdownText } from "./markdown-text";
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const Thread: FC = () => (
|
||||
<ThreadPrimitive.Root className="flex flex-col h-full bg-[#fdfdfd] dark:bg-[#141414] px-4">
|
||||
<ThreadPrimitive.Root className="flex flex-col h-full px-4">
|
||||
|
||||
{/* Empty state: centered welcome + composer */}
|
||||
<ThreadPrimitive.Empty>
|
||||
@@ -35,8 +34,8 @@ export const Thread: FC = () => (
|
||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-violet-500 to-indigo-600 flex items-center justify-center text-white font-bold text-base shadow-sm">
|
||||
A
|
||||
</div>
|
||||
<p className="text-sm font-medium text-[#0d0d0d] dark:text-white">Atlas</p>
|
||||
<p className="text-xs text-[#9a9a9a] dark:text-[#6b6b6b] max-w-xs">
|
||||
<p className="text-sm font-medium text-foreground">Atlas</p>
|
||||
<p className="text-xs text-muted-foreground max-w-xs">
|
||||
Your product strategist. Let's define what you're building.
|
||||
</p>
|
||||
</div>
|
||||
@@ -56,51 +55,33 @@ export const Thread: FC = () => (
|
||||
/>
|
||||
</ThreadPrimitive.Viewport>
|
||||
|
||||
{/* Scroll to bottom */}
|
||||
<div className="relative">
|
||||
<ThreadScrollToBottom />
|
||||
<Composer />
|
||||
</div>
|
||||
</ThreadPrimitive.Root>
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scroll to bottom
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const ThreadScrollToBottom: FC = () => (
|
||||
<ThreadPrimitive.ScrollToBottom asChild>
|
||||
<button className="absolute -top-10 right-4 flex h-8 w-8 items-center justify-center rounded-full bg-[#0d0d0d] dark:bg-white text-white dark:text-[#0d0d0d] shadow hover:opacity-80 transition-opacity">
|
||||
<ArrowDownIcon className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</ThreadPrimitive.ScrollToBottom>
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Composer — Grok pill style with inverted send button
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const Composer: FC = () => (
|
||||
<ComposerPrimitive.Root className="group/composer mx-auto w-full max-w-2xl mb-3">
|
||||
<div className="flex items-end gap-2 rounded-[26px] bg-[#f8f8f8] dark:bg-[#212121] ring-1 ring-[#e5e5e5] dark:ring-[#2a2a2a] px-4 py-3 transition-shadow focus-within:ring-[#c5c5c5] dark:focus-within:ring-[#3a3a3a]">
|
||||
<div className="flex items-end gap-2 rounded-[26px] bg-muted ring-1 ring-border px-4 py-3 transition-shadow focus-within:ring-ring/50">
|
||||
<ComposerPrimitive.Input
|
||||
placeholder="What do you want to build?"
|
||||
rows={1}
|
||||
autoFocus
|
||||
className="flex-1 resize-none bg-transparent text-[#0d0d0d] dark:text-white placeholder:text-[#9a9a9a] dark:placeholder:text-[#6b6b6b] text-sm leading-relaxed outline-none min-h-[24px] max-h-[120px]"
|
||||
className="flex-1 resize-none bg-transparent text-foreground placeholder:text-muted-foreground text-sm leading-relaxed outline-none min-h-[24px] max-h-[120px]"
|
||||
/>
|
||||
{/* Send button (hidden while running) */}
|
||||
<ThreadPrimitive.If running={false}>
|
||||
<ComposerPrimitive.Send asChild>
|
||||
<button className="h-8 w-8 rounded-full bg-[#0d0d0d] dark:bg-white text-white dark:text-[#0d0d0d] flex items-center justify-center shrink-0 hover:opacity-80 transition-opacity disabled:opacity-30 disabled:cursor-not-allowed">
|
||||
<button className="h-8 w-8 rounded-full bg-foreground text-background flex items-center justify-center shrink-0 hover:opacity-80 transition-opacity disabled:opacity-30 disabled:cursor-not-allowed">
|
||||
<ArrowUpIcon className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</ComposerPrimitive.Send>
|
||||
</ThreadPrimitive.If>
|
||||
{/* Cancel button (shown while running) */}
|
||||
<ThreadPrimitive.If running>
|
||||
<ComposerPrimitive.Cancel asChild>
|
||||
<button className="h-8 w-8 rounded-full bg-[#0d0d0d] dark:bg-white text-white dark:text-[#0d0d0d] flex items-center justify-center shrink-0 hover:opacity-80 transition-opacity">
|
||||
<button className="h-8 w-8 rounded-full bg-foreground text-background flex items-center justify-center shrink-0 hover:opacity-80 transition-opacity">
|
||||
<SquareIcon className="h-3 w-3 fill-current" />
|
||||
</button>
|
||||
</ComposerPrimitive.Cancel>
|
||||
@@ -115,7 +96,7 @@ const Composer: FC = () => (
|
||||
|
||||
const FollowupSuggestion: FC<{ suggestion: { prompt: string } }> = ({ suggestion }) => (
|
||||
<ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
|
||||
<button className="text-xs px-3 py-1.5 rounded-full ring-1 ring-[#e5e5e5] dark:ring-[#2a2a2a] text-[#9a9a9a] dark:text-[#6b6b6b] hover:ring-[#c5c5c5] dark:hover:ring-[#3a3a3a] hover:text-[#0d0d0d] dark:hover:text-white transition-all">
|
||||
<button className="text-xs px-3 py-1.5 rounded-full ring-1 ring-border text-muted-foreground hover:ring-ring/50 hover:text-foreground transition-all">
|
||||
{suggestion.prompt}
|
||||
</button>
|
||||
</ThreadPrimitive.Suggestion>
|
||||
@@ -135,7 +116,7 @@ const UserMessage: FC = () => (
|
||||
);
|
||||
|
||||
const UserText: FC<{ text: string }> = ({ text }) => (
|
||||
<div className="bg-[#0d0d0d] dark:bg-white text-white dark:text-[#0d0d0d] rounded-[20px] rounded-tr-[6px] px-4 py-2.5 text-sm leading-relaxed whitespace-pre-wrap">
|
||||
<div className="bg-foreground text-background rounded-[20px] rounded-tr-[6px] px-4 py-2.5 text-sm leading-relaxed whitespace-pre-wrap">
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
@@ -147,7 +128,7 @@ const UserActionBar: FC = () => (
|
||||
className="flex items-center gap-1 mt-1 justify-end opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<ActionBarPrimitive.Edit asChild>
|
||||
<button className="text-[10px] text-[#9a9a9a] dark:text-[#6b6b6b] hover:text-[#0d0d0d] dark:hover:text-white px-1.5 py-0.5 rounded transition-colors">
|
||||
<button className="text-[10px] text-muted-foreground hover:text-foreground px-1.5 py-0.5 rounded transition-colors">
|
||||
Edit
|
||||
</button>
|
||||
</ActionBarPrimitive.Edit>
|
||||
@@ -184,7 +165,7 @@ const AssistantActionBar: FC = () => (
|
||||
className="flex items-center gap-2 mt-1.5 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<ActionBarPrimitive.Copy asChild>
|
||||
<button className="flex items-center gap-1 text-[10px] text-[#9a9a9a] dark:text-[#6b6b6b] hover:text-[#0d0d0d] dark:hover:text-white px-1.5 py-0.5 rounded transition-colors">
|
||||
<button className="flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground px-1.5 py-0.5 rounded transition-colors">
|
||||
<MessagePrimitive.If copied>
|
||||
<CheckIcon className="h-2.5 w-2.5" />
|
||||
Copied
|
||||
@@ -196,7 +177,7 @@ const AssistantActionBar: FC = () => (
|
||||
</button>
|
||||
</ActionBarPrimitive.Copy>
|
||||
<ActionBarPrimitive.Reload asChild>
|
||||
<button className="flex items-center gap-1 text-[10px] text-[#9a9a9a] dark:text-[#6b6b6b] hover:text-[#0d0d0d] dark:hover:text-white px-1.5 py-0.5 rounded transition-colors">
|
||||
<button className="flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground px-1.5 py-0.5 rounded transition-colors">
|
||||
<RefreshCwIcon className="h-2.5 w-2.5" />
|
||||
Retry
|
||||
</button>
|
||||
@@ -214,15 +195,15 @@ const BranchPicker: FC = () => (
|
||||
className="flex items-center gap-1 mt-1 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<BranchPickerPrimitive.Previous asChild>
|
||||
<button className="text-[#9a9a9a] dark:text-[#6b6b6b] hover:text-[#0d0d0d] dark:hover:text-white transition-colors">
|
||||
<button className="text-muted-foreground hover:text-foreground transition-colors">
|
||||
<ChevronLeftIcon className="h-3 w-3" />
|
||||
</button>
|
||||
</BranchPickerPrimitive.Previous>
|
||||
<span className="text-[10px] text-[#9a9a9a] dark:text-[#6b6b6b]">
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
||||
</span>
|
||||
<BranchPickerPrimitive.Next asChild>
|
||||
<button className="text-[#9a9a9a] dark:text-[#6b6b6b] hover:text-[#0d0d0d] dark:hover:text-white transition-colors">
|
||||
<button className="text-muted-foreground hover:text-foreground transition-colors">
|
||||
<ChevronRightIcon className="h-3 w-3" />
|
||||
</button>
|
||||
</BranchPickerPrimitive.Next>
|
||||
|
||||
Reference in New Issue
Block a user