fix(ui): add asChild support to Button and update theme styles

This commit is contained in:
zhenyi 2026-05-31 13:11:19 +08:00
parent 6a0fcf5343
commit a96222cc28
5 changed files with 88 additions and 195 deletions

View File

@ -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);
}
}

View File

@ -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: <RootLayout />,
children: [
{
path: "/",
element: <Navigate replace to="/me" />,
element: <LandingLayout />,
children: [
{
index: true,
element: <LandingHome />,
},
{
path: "features",
element: <FeaturesPage />,
},
{
path: "workflow",
element: <WorkflowPage />,
},
{
path: "pricing",
element: <PricingPage />,
},
],
},
{
path: "/workspace/:projectName/*",

View File

@ -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<typeof buttonVariants>) {
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants> & { 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 (
<ButtonPrimitive
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
render={finalRender}
className={classes}
{...props}
/>
>
{finalChildren}
</ButtonPrimitive>
)
}

View File

@ -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;
}
}

View File

@ -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<HTMLMetaElement>('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");