Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
black in dark mode, which produces a hard seam against the
composer's bg-background. -->
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#252525" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1a1a1a" />
<title>ex</title>
<!-- Pages override the title via useDocumentTitle on mount; this
baseline is shown briefly before the React tree commits. -->
Expand Down Expand Up @@ -53,7 +53,7 @@
font-family: system-ui, -apple-system, sans-serif;
z-index: 9999;
}
html.dark #boot-splash { background: #2c2829; }
html.dark #boot-splash { background: #1a1a1a; }
#boot-splash .boot-spinner {
width: 20px;
height: 20px;
Expand Down
4 changes: 2 additions & 2 deletions src/components/chat/MessageInput.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ describe('MessageInput browser behavior', () => {
}, { timeout: 20000 });
});

// CTA tokens: light theme CTA is #231F20 (near-black), dark theme is
// CTA tokens: light theme CTA is #0B0B0B (near-black), dark theme is
// #DE5D83 (brand pink). Lock both so a future palette tweak can't
// accidentally re-pink the light-mode send button.
it('paints the send button near-black in light mode', async () => {
Expand All @@ -417,7 +417,7 @@ describe('MessageInput browser behavior', () => {
await vi.waitFor(() => {
const rgb = parseRGB(getComputedStyle(send).backgroundColor);
expect(rgb).not.toBeNull();
// Near-black (#231F20 → rgb(35,31,32))
// Near-black (#0B0B0B → rgb(11,11,11))
expect(rgb![0]).toBeLessThan(60);
expect(rgb![1]).toBeLessThan(60);
expect(rgb![2]).toBeLessThan(60);
Expand Down
2 changes: 1 addition & 1 deletion src/components/chat/MessageItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@ function MessageItemImpl({

{!isEditing && !message.deleted && (
<div
className="absolute right-2 -top-3 flex items-center gap-0.5 rounded-md border bg-background shadow-sm transition-opacity touch:hidden"
className="absolute right-2 -top-3 flex items-center gap-0.5 rounded-md border border-border bg-background shadow-sm dark:border-border-strong transition-opacity touch:hidden"
style={{ opacity: toolbarVisible ? 1 : 0 }}
data-actions-pinned={actionsMenuOpen ? 'true' : 'false'}
data-actions-visible={toolbarVisible ? 'true' : 'false'}
Expand Down
12 changes: 6 additions & 6 deletions src/components/chat/UnfurlCard.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,28 +225,28 @@ describe('UnfurlCard browser behaviour', () => {
useUnfurlMock.mockReturnValue({ data: { url: 'https://example.org', title: 'X' }, isLoading: false });
const screen = await render(<UnfurlCard url="https://example.org" messageId="m-1" isAuthor={false} />);
const s = cardStyle(screen);
// Subtle border #E9E9E9 ≈ rgb(233,233,233) on ALL sides (no dark left bar).
// Subtle border #E3E3E2 ≈ rgb(227,227,226) on ALL sides (no dark left bar).
expect(s.borderLeftColor).toBe(s.borderTopColor);
const [r, g, b] = s.borderTopColor.match(/\d+/g)!.map(Number);
expect(r).toBeGreaterThan(210);
expect(g).toBeGreaterThan(210);
expect(b).toBeGreaterThan(210);
});

it('fills the web card with the base background in light mode (#FFFFFF)', async () => {
it('fills the web card with the base background in light mode (#FCFCFB)', async () => {
document.documentElement.classList.remove('dark');
useUnfurlMock.mockReturnValue({ data: { url: 'https://example.org', title: 'X' }, isLoading: false });
const screen = await render(<UnfurlCard url="https://example.org" messageId="m-1" isAuthor={false} />);
expect(cardStyle(screen).backgroundColor).toBe('rgb(255, 255, 255)');
expect(cardStyle(screen).backgroundColor).toBe('rgb(252, 252, 251)');
});

it('fills the web card with the base background in dark mode (#231F20)', async () => {
it('fills the web card with the base background in dark mode (#1A1A1A)', async () => {
document.documentElement.classList.add('dark');
try {
useUnfurlMock.mockReturnValue({ data: { url: 'https://example.org', title: 'X' }, isLoading: false });
const screen = await render(<UnfurlCard url="https://example.org" messageId="m-2" isAuthor={false} />);
// #231F20 → rgb(35, 31, 32)
expect(cardStyle(screen).backgroundColor).toBe('rgb(35, 31, 32)');
// #1A1A1A → rgb(26, 26, 26)
expect(cardStyle(screen).backgroundColor).toBe('rgb(26, 26, 26)');
} finally {
document.documentElement.classList.remove('dark');
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/chat/UnfurlCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export function UnfurlCard({
// Per the design spec the web (OpenGraph) card is bg/base with a
// uniform subtle border (no coloured left accent) — matches the
// GitHub card in the reference screenshots.
className="flex gap-3 overflow-hidden rounded-md border border-border bg-background p-2"
className="flex gap-3 overflow-hidden rounded-md border border-border bg-background p-2 dark:border-border-strong"
>
{safeImg(preview.image) && !imageBroken && (
<img
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export function AppLayout({ children }: AppLayoutProps) {

<div className="relative flex min-h-0 flex-1 overflow-hidden bg-background">
<aside
className="relative hidden shrink-0 bg-sidebar text-sidebar-foreground lg:block"
className="relative hidden shrink-0 bg-sidebar text-sidebar-foreground lg:block border-r border-sidebar-border"
style={{ width: sidebarWidth }}
data-app-chrome="true"
data-keyboard-surface="sidebar"
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/AppTopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export function AppTopBar({ onOpenChannels, channelsButtonHidden }: AppTopBarPro
// as the safety net for windows squeezed to the column's minimum,
// and the grid shifts the field off-centre rather than burying the
// toggle when side minimums no longer fit.
className="grid h-12 mobile:h-14 w-full shrink-0 grid-cols-[1fr_minmax(0,36rem)_1fr] compact:grid-cols-[1fr_minmax(0,17rem)_1fr] items-center gap-2 border-b border-border bg-sidebar px-2 mobile:px-3 text-sidebar-foreground [-webkit-app-region:drag] [&_button,&_a,&_input]:[-webkit-app-region:no-drag]"
className="grid h-12 mobile:h-14 w-full shrink-0 grid-cols-[1fr_minmax(0,36rem)_1fr] compact:grid-cols-[1fr_minmax(0,17rem)_1fr] items-center gap-2 border-b border-border bg-chat-top px-2 mobile:px-3 text-sidebar-foreground [-webkit-app-region:drag] [&_button,&_a,&_input]:[-webkit-app-region:no-drag]"
data-testid="app-shell-header"
data-app-chrome="true"
// The global search field lives on this sidebar-coloured strip, so the
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/ChannelRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export function ChannelRow({
className={({ isActive }) =>
`relative flex flex-1 min-w-0 items-center gap-2 rounded-md py-1.5 pl-2 pr-12 text-sm transition-colors touch:pr-16 mobile:h-12 mobile:py-0 mobile:pl-3 mobile:pr-20 mobile:text-base ${
isActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: hasUnread
? 'font-bold text-white hover:bg-white/10'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/ConversationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function ConversationRow({
className={({ isActive }) =>
`relative flex flex-1 min-w-0 items-center gap-2 rounded-md py-1.5 pl-2 pr-12 text-sm transition-colors touch:pr-16 mobile:h-12 mobile:py-0 mobile:pl-3 mobile:pr-20 mobile:text-base ${
isActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: hasUnread
? 'font-bold text-white hover:bg-white/10'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/Sidebar.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ describe('Sidebar browser render — rich fixtures', () => {
const activityLink = document.querySelector('a[href="/activity"]') as HTMLAnchorElement;
expect(activityLink).not.toBeNull();
expect(activityLink.className).toContain('font-semibold');
expect(activityLink.className).toContain('bg-background');
expect(activityLink.className).toContain('bg-sidebar-accent');
});

it('shows an error message when category creation fails with a non-Error rejection', async () => {
Expand Down
10 changes: 5 additions & 5 deletions src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ export function Sidebar({ onClose }: SidebarProps) {
className={({ isActive }) =>
`relative flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors mobile:h-12 mobile:px-3 mobile:py-0 mobile:text-base ${
isActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
}`
}
Expand All @@ -974,7 +974,7 @@ export function Sidebar({ onClose }: SidebarProps) {
className={({ isActive }) =>
`relative flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors mobile:h-12 mobile:px-3 mobile:py-0 mobile:text-base ${
isActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
}`
}
Expand All @@ -998,7 +998,7 @@ export function Sidebar({ onClose }: SidebarProps) {
className={() =>
`relative flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors mobile:h-12 mobile:px-3 mobile:py-0 mobile:text-base ${
directoryActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
}`
}
Expand All @@ -1013,7 +1013,7 @@ export function Sidebar({ onClose }: SidebarProps) {
className={({ isActive }) =>
`relative flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors mobile:h-12 mobile:px-3 mobile:py-0 mobile:text-base ${
isActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
}`
}
Expand All @@ -1035,7 +1035,7 @@ export function Sidebar({ onClose }: SidebarProps) {
aria-current={aiHubActive ? 'page' : undefined}
className={`relative flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors mobile:h-12 mobile:px-3 mobile:py-0 mobile:text-base ${
aiHubActive
? 'bg-background text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
? 'bg-sidebar-accent text-white font-semibold before:absolute before:inset-y-1 before:left-0 before:w-[3px] before:rounded-full before:bg-sidebar-foreground before:content-[""]'
: 'text-gray-300 hover:bg-white/10 hover:text-white'
}`}
>
Expand Down
2 changes: 1 addition & 1 deletion src/components/threads/ThreadCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ export function ThreadCard({ summary, title, deepLink, currentUserId, unread = f
{unread && (
<span
data-testid="thread-card-unread"
className="rounded-full bg-brand px-2 py-0.5 text-[11px] font-semibold text-brand-foreground"
className="rounded-full bg-brand-strong px-2 py-0.5 text-[11px] font-semibold text-brand-foreground"
>
Unread
</span>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const badgeVariants = cva(
// single-digit counts — it grows naturally for double-digit
// via `min-w-4` rather than `min-w-5`.
brand:
"bg-brand text-brand-foreground [a]:hover:bg-brand-hover rounded-sm h-4 min-w-4 px-1 py-0 leading-none",
"bg-brand-strong text-brand-foreground [a]:hover:bg-brand-strong-hover rounded-sm h-4 min-w-4 px-1 py-0 leading-none",
},
},
defaultVariants: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const buttonVariants = cva(
// neutral `primary` accents (focus rings, avatar fallbacks, @mention
// pills, switches), which stay neutral in both themes.
default:
"bg-primary text-primary-foreground [a]:hover:bg-primary/80 dark:bg-brand dark:text-brand-foreground dark:[a]:hover:bg-brand-hover",
"bg-primary text-primary-foreground [a]:hover:bg-primary/80 dark:bg-brand-strong dark:text-brand-foreground dark:[a]:hover:bg-brand-strong-hover",
outline:
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
secondary:
Expand Down
Loading
Loading