From a96222cc287bfc085c021be9bf0cf80390cc0772 Mon Sep 17 00:00:00 2001 From: zhenyi <434836402@qq.com> Date: Sun, 31 May 2026 13:11:19 +0800 Subject: [PATCH] fix(ui): add asChild support to Button and update theme styles --- src/App.css | 186 ----------------------------------- src/App.tsx | 27 ++++- src/components/ui/button.tsx | 25 ++++- src/index.css | 40 +++++++- src/lib/theme.ts | 5 + 5 files changed, 88 insertions(+), 195 deletions(-) delete mode 100644 src/App.css diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 49b42df..0000000 --- a/src/App.css +++ /dev/null @@ -1,186 +0,0 @@ -.counter { - font-size: 16px; - padding: 5px 10px; - border-radius: 5px; - color: var(--accent); - background: var(--accent-bg); - border: 2px solid transparent; - transition: border-color 0.3s; - margin-bottom: 24px; - - &:hover { - border-color: var(--accent-border); - } - &:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } -} - - - -.hero { - position: relative; - - .base, - .framework, - .vite { - inset-inline: 0; - margin: 0 auto; - } - - .base { - width: 170px; - position: relative; - z-index: 0; - } - - .framework, - .vite { - position: absolute; - } - - .framework { - z-index: 1; - top: 34px; - height: 28px; - transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) - scale(1.4); - } - - .vite { - z-index: 0; - top: 107px; - height: 26px; - width: auto; - transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) - scale(0.8); - } -} - -#center { - display: flex; - flex-direction: column; - gap: 25px; - place-content: center; - place-items: center; - flex-grow: 1; - - @media (max-width: 1024px) { - padding: 32px 20px 24px; - gap: 18px; - } -} - -#next-steps { - display: flex; - border-top: 1px solid var(--border); - text-align: left; - - & > div { - flex: 1 1 0; - padding: 32px; - @media (max-width: 1024px) { - padding: 24px 20px; - } - } - - .icon { - margin-bottom: 16px; - width: 22px; - height: 22px; - } - - @media (max-width: 1024px) { - flex-direction: column; - text-align: center; - } -} - -#docs { - border-right: 1px solid var(--border); - - @media (max-width: 1024px) { - border-right: none; - border-bottom: 1px solid var(--border); - } -} - -#next-steps ul { - list-style: none; - padding: 0; - display: flex; - gap: 8px; - margin: 32px 0 0; - - .logo { - height: 18px; - } - - a { - color: var(--text-h); - font-size: 16px; - border-radius: 6px; - background: var(--social-bg); - display: flex; - padding: 6px 12px; - align-items: center; - gap: 8px; - text-decoration: none; - transition: box-shadow 0.3s; - - &:hover { - box-shadow: var(--shadow); - } - .button-icon { - height: 18px; - width: 18px; - } - } - - @media (max-width: 1024px) { - margin-top: 20px; - flex-wrap: wrap; - justify-content: center; - - li { - flex: 1 1 calc(50% - 8px); - } - - a { - width: 100%; - justify-content: center; - box-sizing: border-box; - } - } -} - -#spacer { - height: 88px; - border-top: 1px solid var(--border); - @media (max-width: 1024px) { - height: 48px; - } -} - -.ticks { - position: relative; - width: 100%; - - &::before, - &::after { - content: ''; - position: absolute; - top: -4.5px; - border: 5px solid transparent; - } - - &::before { - left: 0; - border-left-color: var(--border); - } - &::after { - right: 0; - border-right-color: var(--border); - } -} diff --git a/src/App.tsx b/src/App.tsx index e21f1e0..8079d24 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,3 @@ -import './App.css' import { useEffect } from "react"; import { Navigate, createBrowserRouter, useParams } from "react-router"; import { RouterProvider } from "react-router/dom"; @@ -46,6 +45,11 @@ import SettingsAccessibilityPage from "@/page/settings/accessibility"; import SettingsSecurityPage from "@/page/settings/security"; import SettingsTokensPage from "@/page/settings/tokens"; import SettingsSshKeysPage from "@/page/settings/ssh-keys"; +import LandingLayout from "@/page/landing/layout"; +import LandingHome from "@/page/landing/home"; +import FeaturesPage from "@/page/landing/features"; +import WorkflowPage from "@/page/landing/workflow"; +import PricingPage from "@/page/landing/pricing"; function WorkspaceCompatRedirect() { const { projectName = "" } = useParams(); @@ -65,8 +69,25 @@ function App() { element: , children: [ { - path: "/", - element: , + element: , + children: [ + { + index: true, + element: , + }, + { + path: "features", + element: , + }, + { + path: "workflow", + element: , + }, + { + path: "pricing", + element: , + }, + ], }, { path: "/workspace/:projectName/*", diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 8cf4c32..6aca069 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,10 +1,11 @@ +import React from "react" import { Button as ButtonPrimitive } from "@base-ui/react/button" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( - "group/button inline-flex shrink-0 items-center justify-center rounded-xl border border-transparent bg-clip-padding text-[13px] font-semibold whitespace-nowrap transition-all outline-none select-none focus-visible:ring-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:scale-[0.97] disabled:pointer-events-none disabled:opacity-40 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-sm", + "group/button inline-flex shrink-0 items-center justify-center rounded-xl border border-transparent bg-clip-padding text-[13px] font-semibold whitespace-nowrap transition-[background-color,border-color,color,box-shadow,transform] outline-none select-none focus-visible:ring-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:scale-[0.97] disabled:pointer-events-none disabled:opacity-40 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-sm", { variants: { variant: { @@ -42,14 +43,30 @@ function Button({ className, variant = "default", size = "default", + asChild, + children, + render, ...props -}: ButtonPrimitive.Props & VariantProps) { +}: ButtonPrimitive.Props & VariantProps & { asChild?: boolean }) { + const classes = cn(buttonVariants({ variant, size, className })) + + let finalRender = render + let finalChildren = children + + if (asChild && !finalRender) { + finalRender = React.Children.only(children) as React.ReactElement + finalChildren = undefined + } + return ( + > + {finalChildren} + ) } diff --git a/src/index.css b/src/index.css index d4da028..fd11757 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@plugin "@tailwindcss/typography"; @import "tw-animate-css"; @import "shadcn/tailwind.css"; @import "@fontsource-variable/space-grotesk"; @@ -29,6 +30,12 @@ --color-input: var(--input); --color-border: var(--border); --color-destructive: var(--destructive); + --color-success: var(--success); + --color-success-foreground: var(--success-foreground); + --color-warning: var(--warning); + --color-warning-foreground: var(--warning-foreground); + --color-info: var(--info); + --color-info-foreground: var(--info-foreground); --color-accent-foreground: var(--accent-foreground); --color-accent: var(--accent); --color-muted-foreground: var(--muted-foreground); @@ -68,6 +75,12 @@ --accent: #f5f5f5; --accent-foreground: #111111; --destructive: #ef4444; + --success: #16a34a; + --success-foreground: #ffffff; + --warning: #d97706; + --warning-foreground: #ffffff; + --info: #2563eb; + --info-foreground: #ffffff; --border: #e8e8e8; --input: #e8e8e8; --ring: #111111; @@ -103,6 +116,12 @@ --accent: #1f2128; --accent-foreground: #ececf1; --destructive: #EF4444; + --success: #22c55e; + --success-foreground: #052e16; + --warning: #f59e0b; + --warning-foreground: #451a03; + --info: #60a5fa; + --info-foreground: #082f49; --border: rgba(255, 255, 255, 0.08); --input: rgba(255, 255, 255, 0.08); --ring: #5E6AD2; @@ -120,12 +139,29 @@ * { @apply border-border outline-ring/50; } + html { + @apply font-sans; + -webkit-tap-highlight-color: transparent; + } body { @apply bg-background text-foreground antialiased; font-feature-settings: "cv02", "cv03", "cv04", "cv11"; } - html { - @apply font-sans; + button, + a, + input, + textarea, + select { + touch-action: manipulation; + } + h1, + h2, + h3 { + text-wrap: balance; + } + p, + li { + text-wrap: pretty; } } diff --git a/src/lib/theme.ts b/src/lib/theme.ts index abc85b3..ab686b4 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -348,6 +348,11 @@ export function applyTheme(preset: ThemePreset) { root.style.setProperty("--chart-4", preset.isDark ? "#6e7681" : "#374151"); root.style.setProperty("--chart-5", preset.isDark ? "#4b5563" : "#1f2937"); + root.style.setProperty("color-scheme", preset.isDark ? "dark" : "light"); + + const themeColorMeta = document.querySelector('meta[name="theme-color"]'); + themeColorMeta?.setAttribute("content", c.background); + // Toggle dark class for any tailwind dark: variants still in use if (preset.isDark) { root.classList.add("dark");