feat: support dark mode

This commit is contained in:
Li Xin
2025-04-20 11:18:05 +08:00
parent ce130e7160
commit a57db4fa4a
18 changed files with 952 additions and 53 deletions

View File

@@ -41,7 +41,7 @@ export function ConversationStarter({
}}
>
<div
className="cursor-pointer rounded-2xl border bg-[rgba(255,255,255,0.5)] px-4 py-4 text-gray-500 transition-all duration-300 hover:bg-[rgba(255,255,255,1)] hover:text-gray-900 hover:shadow-md"
className="bg-card text-muted-foreground cursor-pointer rounded-2xl border px-4 py-4 opacity-75 transition-all duration-300 hover:opacity-100 hover:shadow-md"
onClick={() => {
onSend?.(question);
}}

View File

@@ -1,10 +1,20 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
export function FavIcon({ url, title }: { url: string; title?: string }) {
import { cn } from "~/lib/utils";
export function FavIcon({
className,
url,
title,
}: {
className?: string;
url: string;
title?: string;
}) {
return (
<img
className="h-4 w-4 rounded-full bg-slate-100 shadow-sm"
className={cn("bg-accent h-4 w-4 rounded-full shadow-sm", className)}
width={16}
height={16}
src={new URL(url).origin + "/favicon.ico"}

View File

@@ -93,23 +93,23 @@ export function InputBox({
);
return (
<div className={cn("relative rounded-[24px] border bg-white", className)}>
<div className={cn("bg-card relative rounded-[24px] border", className)}>
<div className="w-full">
<AnimatePresence>
{feedback && (
<motion.div
ref={feedbackRef}
className="absolute top-0 left-0 mt-3 ml-2 flex items-center justify-center gap-1 rounded-2xl border border-[#007aff] bg-white px-2 py-0.5"
className="bg-background border-brand absolute top-0 left-0 mt-3 ml-2 flex items-center justify-center gap-1 rounded-2xl border px-2 py-0.5"
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0 }}
transition={{ duration: 0.2, ease: "easeInOut" }}
>
<div className="flex h-full w-full items-center justify-center text-sm text-[#007aff] opacity-90">
<div className="text-brand flex h-full w-full items-center justify-center text-sm opacity-90">
{feedback.option.text}
</div>
<CloseOutlined
className="cursor-pointer text-[9px]"
className="cursor-pointer text-[9px] opacity-60"
onClick={onRemoveFeedback}
/>
</motion.div>
@@ -144,15 +144,12 @@ export function InputBox({
<Button
variant="outline"
size="icon"
className={cn(
"h-10 w-10 rounded-full",
responding ? "bg-button-hover" : "bg-button",
)}
className={cn("h-10 w-10 rounded-full")}
onClick={handleSendMessage}
>
{responding ? (
<div className="flex h-10 w-10 items-center justify-center">
<div className="h-4 w-4 rounded-sm bg-red-300" />
<div className="bg-foreground h-4 w-4 rounded-sm opacity-70" />
</div>
) : (
<ArrowUpOutlined />

View File

@@ -81,7 +81,7 @@ export function MessageListView({
"flex h-full w-full flex-col overflow-hidden pt-4",
className,
)}
scrollShadowColor="#f7f5f3"
scrollShadowColor="var(--app-background)"
>
<ul className="flex flex-col">
{messageIds.map((messageId) => (
@@ -205,10 +205,10 @@ function MessageListItem({
return (
<div
className={cn(
`flex w-fit max-w-[85%] flex-col rounded-2xl px-4 py-3 shadow-xs`,
`flex w-fit max-w-[85%] flex-col rounded-2xl px-4 py-3 shadow`,
message.role === "user" &&
"text-primary-foreground rounded-ee-none bg-[#007aff]",
message.role === "assistant" && "rounded-es-none bg-white",
"text-primary-foreground bg-brand rounded-ee-none",
message.role === "assistant" && "bg-card rounded-es-none",
className,
)}
>
@@ -249,7 +249,7 @@ function MessageListItem({
}
}, [openResearchId, researchId]);
return (
<Card className={cn("w-full bg-white", className)}>
<Card className={cn("w-full", className)}>
<CardHeader>
<CardTitle>
<RainbowText animated={state !== "Report generated"}>
@@ -259,7 +259,7 @@ function MessageListItem({
</CardHeader>
<CardFooter>
<div className="flex w-full">
<RollingText className="flex-grow text-sm opacity-50">
<RollingText className="text-muted-foreground flex-grow text-sm">
{state}
</RollingText>
<Button onClick={handleOpen}>
@@ -302,7 +302,7 @@ function PlanCard({
);
}, []);
return (
<Card className={cn("w-full bg-white", className)}>
<Card className={cn("w-full", className)}>
<CardHeader>
<CardTitle>
<h1 className="text-xl font-medium">
@@ -336,7 +336,7 @@ function PlanCard({
<CardFooter className="flex justify-end">
{!message.isStreaming && interruptMessage?.options?.length && (
<motion.div
className="flex gap-2"
className="flex gap-4"
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: 0.3 }}
@@ -383,7 +383,7 @@ function PodcastCard({
}, [message.isStreaming]);
const [isPlaying, setIsPlaying] = useState(false);
return (
<Card className={cn("w-[508px] bg-white", className)}>
<Card className={cn("w-[508px]", className)}>
<CardHeader>
<div className="text-muted-foreground flex items-center justify-between text-sm">
<div className="flex items-center gap-2">

View File

@@ -1,10 +1,10 @@
.animated {
background: linear-gradient(
to right,
rgba(0, 0, 0, 0.3) 15%,
rgba(0, 0, 0, 0.7) 35%,
rgba(0, 0, 0, 0.7) 65%,
rgba(0, 0, 0, 0.3) 85%
rgb(from var(--card-foreground) r g b / 0.3) 15%,
rgb(from var(--card-foreground) r g b / 0.75) 35%,
rgb(from var(--card-foreground) r g b / 0.75) 65%,
rgb(from var(--card-foreground) r g b / 0.3) 85%
);
-webkit-background-clip: text;
background-clip: text;

View File

@@ -161,7 +161,10 @@ function WebSearchToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
key={`search-result-${i}`}
className="flex h-40 w-40 gap-2 rounded-md text-sm"
>
<Skeleton className="h-full w-full rounded-md bg-gradient-to-tl from-slate-50 to-slate-200" />
<Skeleton
className="to-accent h-full w-full rounded-md bg-gradient-to-tl from-slate-300"
style={{ animationDelay: `${i * 0.2}s` }}
/>
</li>
))}
{pageResults
@@ -169,7 +172,7 @@ function WebSearchToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
.map((searchResult, i) => (
<motion.li
key={`search-result-${i}`}
className="text-muted-foreground flex max-w-40 gap-2 rounded-md bg-slate-100 px-2 py-1 text-sm"
className="text-muted-foreground bg-accent flex max-w-40 gap-2 rounded-md px-2 py-1 text-sm"
initial={{ opacity: 0, y: 10, scale: 0.66 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{
@@ -178,7 +181,11 @@ function WebSearchToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
ease: "easeOut",
}}
>
<FavIcon url={searchResult.url} title={searchResult.title} />
<FavIcon
className="mt-1"
url={searchResult.url}
title={searchResult.title}
/>
<a href={searchResult.url} target="_blank">
{searchResult.title}
</a>
@@ -203,7 +210,7 @@ function WebSearchToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
<Image
src={searchResult.image_url}
alt={searchResult.image_description}
className="h-40 w-40 max-w-full rounded-md bg-slate-100 bg-cover bg-center bg-no-repeat"
className="bg-accent h-40 w-40 max-w-full rounded-md bg-cover bg-center bg-no-repeat"
imageClassName="hover:scale-110"
imageTransition
/>
@@ -237,7 +244,7 @@ function CrawlToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
<div className="px-5">
<ul className="mt-2 flex flex-wrap gap-4">
<motion.li
className="text-muted-foreground flex h-40 w-40 gap-2 rounded-md bg-slate-100 px-2 py-1 text-sm"
className="text-muted-foreground bg-accent flex h-40 w-40 gap-2 rounded-md px-2 py-1 text-sm"
initial={{ opacity: 0, y: 10, scale: 0.66 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{
@@ -245,7 +252,7 @@ function CrawlToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
ease: "easeOut",
}}
>
<FavIcon url={url} title={title} />
<FavIcon className="mt-1" url={url} title={title} />
<a href={url} target="_blank">
{title}
</a>
@@ -269,7 +276,7 @@ function PythonToolCall({ toolCall }: { toolCall: ToolCallRuntime }) {
</RainbowText>
</div>
<div className="px-5">
<div className="mt-2 rounded-md bg-slate-50 p-2 text-sm">
<div className="bg-accent mt-2 rounded-md p-2 text-sm">
<SyntaxHighlighter language="python" style={docco}>
{code}
</SyntaxHighlighter>

View File

@@ -79,7 +79,10 @@ export function ResearchBlock({
</TabsList>
</div>
<TabsContent className="h-full min-h-0 flex-grow px-8" value="report">
<ScrollContainer className="px-5pb-20 h-full">
<ScrollContainer
className="px-5pb-20 h-full"
scrollShadowColor="var(--card)"
>
{reportId && researchId && (
<ResearchReportBlock
className="mt-4"
@@ -93,7 +96,7 @@ export function ResearchBlock({
className="h-full min-h-0 flex-grow px-8"
value="activities"
>
<ScrollContainer className="h-full">
<ScrollContainer className="h-full" scrollShadowColor="var(--card)">
{researchId && (
<ResearchActivitiesBlock
className="mt-4"

View File

@@ -10,7 +10,7 @@ export function ScrollContainer({
className,
children,
scrollShadow = true,
scrollShadowColor = "white",
scrollShadowColor = "var(--background)",
}: {
className?: string;
children: React.ReactNode;

View File

@@ -0,0 +1,36 @@
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "~/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
export function ThemeToggle() {
const { setTheme } = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@@ -6,6 +6,7 @@ import "~/styles/globals.css";
import { type Metadata } from "next";
import { Geist } from "next/font/google";
import { ThemeProvider } from "~/components/theme-provider";
import { TooltipProvider } from "~/components/ui/tooltip";
export const metadata: Metadata = {
@@ -24,9 +25,15 @@ export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={`${geist.variable}`}>
<body className="h-screen w-screen overflow-hidden overscroll-none bg-[#f7f5f3]">
<TooltipProvider>{children}</TooltipProvider>
<html lang="en" className={`${geist.variable}`} suppressHydrationWarning>
<body className="bg-app h-screen w-screen overflow-hidden overscroll-none">
<ThemeProvider
attribute="class"
defaultTheme="system"
disableTransitionOnChange
>
<TooltipProvider>{children}</TooltipProvider>
</ThemeProvider>
</body>
</html>
);

View File

@@ -14,6 +14,7 @@ import { cn } from "~/lib/utils";
import { Logo } from "./_components/logo";
import { MessagesBlock } from "./_components/messages-block";
import { ResearchBlock } from "./_components/research-block";
import { ThemeToggle } from "./_components/theme-toggle";
export default function HomePage() {
const openResearchId = useStore((state) => state.openResearchId);
@@ -25,16 +26,14 @@ export default function HomePage() {
<div className="flex h-full w-full justify-center">
<header className="fixed top-0 left-0 flex h-12 w-full w-screen items-center justify-between px-4">
<Logo />
<Button
className="opacity-70 transition-opacity duration-300 hover:opacity-100"
variant="ghost"
size="icon"
asChild
>
<Link href="https://github.com/bytedance/deer-flow" target="_blank">
<GithubOutlined />
</Link>
</Button>
<div className="flex items-center">
<Button variant="ghost" size="icon" asChild>
<Link href="https://github.com/bytedance/deer-flow" target="_blank">
<GithubOutlined />
</Link>
</Button>
<ThemeToggle />
</div>
</header>
<div
className={cn(

View File

@@ -0,0 +1,11 @@
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import * as React from "react";
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

View File

@@ -0,0 +1,257 @@
"use client"
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
import { cn } from "~/lib/utils"
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}
function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
)
}
function DropdownMenuTrigger({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
)
}
function DropdownMenuContent({
className,
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
)
}
function DropdownMenuGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
)
}
function DropdownMenuItem({
className,
inset,
variant = "default",
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
}) {
return (
<DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function DropdownMenuCheckboxItem({
className,
children,
checked,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return (
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
checked={checked}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
)
}
function DropdownMenuRadioGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
<DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
)
}
function DropdownMenuRadioItem({
className,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return (
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
)
}
function DropdownMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}
/>
)
}
function DropdownMenuSeparator({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
)
}
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
)
}
function DropdownMenuSubContent({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className
)}
{...props}
/>
)
}
export {
DropdownMenu,
DropdownMenuPortal,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
}

View File

@@ -0,0 +1,18 @@
import * as React from "react"
import { cn } from "~/lib/utils"
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
<textarea
data-slot="textarea"
className={cn(
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
{...props}
/>
)
}
export { Textarea }

View File

@@ -64,9 +64,9 @@ async function* chatStreamMock(
const [, event] = eventRaw.split("event: ", 2) as [string, string];
const [, data] = dataRaw.split("data: ", 2) as [string, string];
if (event === "message_chunk") {
await sleep(0);
await sleep(100);
} else if (event === "tool_call_result") {
await sleep(0);
await sleep(4000);
}
try {
yield {

View File

@@ -55,10 +55,13 @@
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-app: var(--app-background);
--color-brand: var(--brand);
}
:root {
--radius: 0.625rem;
--app-background: #f7f5f3;
--background: oklch(1 0 0);
--foreground: rgba(0, 0, 0, 0.72);
--card: oklch(1 0 0);
@@ -90,10 +93,12 @@
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--brand: #007aff;
}
.dark {
--background: oklch(0.145 0 0);
--app-background: var(--background);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
@@ -124,6 +129,7 @@
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--brand: #4087f4;
}
@layer base {
@@ -131,7 +137,7 @@
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
@apply text-foreground;
}
}
@@ -144,7 +150,10 @@ textarea {
line-height: 1.75;
a {
color: blue;
text-decoration: underline;
text-decoration-style: dotted;
text-underline-position: from-font;
text-decoration-color: var(--muted-foreground);
&:hover {
text-decoration: underline;