feat(ui): align shared console chrome with calm compact workspace system

This commit is contained in:
Haitao Pan 2026-03-20 21:07:57 +08:00
parent 986985a63d
commit 08646c0760
11 changed files with 248 additions and 234 deletions

View File

@ -6,82 +6,92 @@
@tailwind utilities;
:root {
--font-geist-sans: "Geist", sans-serif;
--font-geist-mono: "Geist Mono", monospace;
--font-geist-sans:
-apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
"Segoe UI", sans-serif;
--font-geist-mono:
"SFMono-Regular", "SF Mono", ui-monospace, "Cascadia Code", Consolas,
monospace;
--font-editorial-display:
"Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
--app-shell-nav-offset: 5.5rem;
--app-shell-nav-offset: 4.75rem;
/* Light theme defaults */
--color-background: #f5f5f7;
--color-background-muted: #ececef;
--color-background: #f8f9fa;
--color-background-muted: #f2f5f8;
--color-surface: #ffffff;
--color-surface-elevated: rgba(255, 255, 255, 0.96);
--color-surface-translucent: rgba(255, 255, 255, 0.92);
--color-surface-muted: #f3f4f6;
--color-surface-hover: #f7f8fb;
--color-surface-border: #e5e7eb;
--color-surface-border-strong: #d1d5db;
--color-text: #0d0d0d;
--color-heading: #111827;
--color-text-muted: #4b5563;
--color-text-subtle: #6b7280;
--color-surface-elevated: rgba(255, 255, 255, 0.94);
--color-surface-translucent: rgba(255, 255, 255, 0.88);
--color-surface-muted: #f2f5f8;
--color-surface-hover: #edf2f7;
--color-surface-border: rgba(166, 180, 200, 0.18);
--color-surface-border-strong: rgba(166, 180, 200, 0.28);
--color-text: #1c1b1f;
--color-heading: #1c1b1f;
--color-text-muted: #667085;
--color-text-subtle: #98a1b2;
--color-text-inverse: #f8fbff;
--color-primary: #4c8bf5;
--color-primary-hover: #5d97f6;
--color-primary-muted: #edf3ff;
--color-primary-border: #d7e5ff;
--color-primary: #0058bd;
--color-primary-hover: #0b4f9a;
--color-primary-muted: #e8f0fb;
--color-primary-border: #d7e4f7;
--color-primary-foreground: #ffffff;
--color-accent: #335fd4;
--color-accent-muted: #e7edff;
--color-accent-foreground: #1b3477;
--color-accent: #5b8def;
--color-accent-muted: #e8f0fb;
--color-accent-foreground: #23446c;
--color-success: #16a34a;
--color-success-muted: #dcfce7;
--color-success-foreground: #166534;
--color-warning: #f59e0b;
--color-warning-muted: #fef3c7;
--color-warning-foreground: #92400e;
--color-danger: #ef4444;
--color-warning: #8f4a00;
--color-warning-muted: #fff3cd;
--color-warning-foreground: #664d03;
--color-danger: #c3655c;
--color-danger-muted: #fee2e2;
--color-danger-foreground: #7f1d1d;
--color-info: #3366ff;
--color-info-muted: #f0f4ff;
--color-info-foreground: #254edb;
--color-overlay: rgba(17, 24, 39, 0.42);
--color-ring: #d6e0ff;
--color-focus: rgba(51, 102, 255, 0.35);
--color-divider: rgba(17, 24, 39, 0.08);
--color-badge-surface: #e5e7eb;
--color-badge-muted: #f3f4f6;
--color-badge-foreground: #1f2937;
--color-info: #5b8def;
--color-info-muted: #e8f0fb;
--color-info-foreground: #23446c;
--color-overlay: rgba(28, 27, 31, 0.28);
--color-ring: #d7e4f7;
--color-focus: rgba(0, 88, 189, 0.18);
--color-divider: rgba(28, 27, 31, 0.08);
--color-badge-surface: #e9eef4;
--color-badge-muted: #f2f5f8;
--color-badge-foreground: #49454f;
--gradient-app-from: #fafafa;
--gradient-app-via: #f4f5f7;
--gradient-app-to: #f7f7f8;
--gradient-primary-from: #4c8bf5;
--gradient-primary-to: #335fd4;
--gradient-app-from: #f8f9fa;
--gradient-app-via: #f3f6f9;
--gradient-app-to: #eef2f6;
--gradient-primary-from: #0058bd;
--gradient-primary-to: #5b8def;
--shadow-sm:
0 1px 2px rgba(17, 24, 39, 0.05), 0 3px 10px rgba(17, 24, 39, 0.04);
--shadow-md: 0 8px 24px rgba(17, 24, 39, 0.08), 0 2px 6px rgba(17, 24, 39, 0.06);
0 1px 2px rgba(90, 108, 132, 0.06),
0 6px 18px rgba(90, 108, 132, 0.05);
--shadow-md:
0 12px 30px rgba(90, 108, 132, 0.08),
0 2px 6px rgba(90, 108, 132, 0.05);
--shadow-soft:
0 1px 2px rgba(17, 24, 39, 0.04), 0 8px 20px rgba(17, 24, 39, 0.05);
--shadow-lg: 0 12px 32px rgba(17, 24, 39, 0.10), 0 4px 12px rgba(17, 24, 39, 0.08);
0 1px 2px rgba(90, 108, 132, 0.04),
0 10px 26px rgba(90, 108, 132, 0.05);
--shadow-lg:
0 18px 44px rgba(90, 108, 132, 0.1),
0 4px 12px rgba(90, 108, 132, 0.06);
--radius-lg: 0.75rem;
--radius-xl: 1rem;
--radius-lg: 0.375rem;
--radius-xl: 0.5rem;
--radius-pill: 999px;
--type-body-size: 1rem;
--type-body-line-height: 1.5;
--type-sidebar-size: 0.875rem;
--type-sidebar-line-height: 1.5;
--type-body-size: 0.8125rem;
--type-body-line-height: 1.1538;
--type-sidebar-size: 0.8125rem;
--type-sidebar-line-height: 1.1538;
--type-meta-size: 0.75rem;
--type-meta-line-height: 1.4;
--type-heading-1-size: 1.75rem;
--type-heading-2-size: 1.5rem;
--type-heading-3-size: 1.25rem;
--type-heading-line-height: 1.25;
--type-meta-line-height: 1.3334;
--type-heading-1-size: 1.5rem;
--type-heading-2-size: 1.25rem;
--type-heading-3-size: 0.8125rem;
--type-heading-line-height: 1.1667;
}
html {
@ -97,10 +107,10 @@ body {
background-image:
radial-gradient(
circle at top left,
rgba(76, 139, 245, 0.08),
transparent 26%
rgba(91, 141, 239, 0.08),
transparent 24%
),
linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0));
linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0));
color: var(--color-text);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
@ -136,7 +146,7 @@ button {
color: var(--color-heading);
font-weight: 600;
line-height: var(--type-heading-line-height);
letter-spacing: -0.02em;
letter-spacing: -0.03em;
}
h1 {
@ -164,14 +174,14 @@ button {
@layer components {
.tactile-button {
display: inline-flex;
min-height: 40px;
min-height: 32px;
align-items: center;
justify-content: center;
gap: 0.5rem;
border-radius: 12px;
border-radius: 8px;
border: 1px solid transparent;
padding: 0.625rem 1rem;
font-size: 0.875rem;
padding: 0.45rem 0.8rem;
font-size: 0.8125rem;
font-weight: 600;
line-height: 1;
transition:
@ -197,13 +207,13 @@ button {
.tactile-button-soft {
border-color: var(--color-surface-border);
background: #f1f1f3;
background: rgba(255, 255, 255, 0.88);
color: var(--color-text);
box-shadow: var(--shadow-soft);
}
.tactile-button-soft:hover {
background: #e5e5ea;
background: var(--color-surface-hover);
}
.tactile-button-primary {
@ -218,12 +228,12 @@ button {
.tactile-button-subtle {
border-color: var(--color-surface-border);
background: rgba(255, 255, 255, 0.84);
background: rgba(255, 255, 255, 0.72);
color: var(--color-text-subtle);
}
.tactile-control {
border-radius: 12px;
border-radius: 8px;
border: 1px solid var(--color-surface-border);
background: rgba(255, 255, 255, 0.88);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);

View File

@ -155,10 +155,10 @@ export default function Header({
};
return (
<header className="sticky top-0 z-30 overflow-hidden border-b border-[color:var(--color-surface-border)] bg-white/80 text-[var(--color-text)] shadow-[var(--shadow-soft)] backdrop-blur-xl transition-colors">
<header className="sticky top-0 z-30 overflow-hidden border-b border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] text-[var(--color-text)] shadow-[var(--shadow-soft)] backdrop-blur-xl transition-colors">
{assumeStatus.isAssuming ? (
<div className="flex items-center justify-between gap-3 px-4 py-2 text-xs md:px-6">
<div className="rounded-[12px] border border-[color:var(--color-warning-muted)] bg-[var(--color-warning-muted)] px-3 py-1.5 text-[var(--color-warning-foreground)]">
<div className="flex items-center justify-between gap-3 px-4 py-2 text-xs md:px-5">
<div className="rounded-[8px] border border-[color:var(--color-warning-muted)] bg-[var(--color-warning-muted)] px-3 py-1.5 text-[var(--color-warning-foreground)]">
{language === "zh"
? `当前处于 Assume: ${assumeStatus.target || "sandbox@svc.plus"}(只读视角)`
: `Assuming: ${assumeStatus.target || "sandbox@svc.plus"} (read-only view)`}
@ -167,7 +167,7 @@ export default function Header({
type="button"
onClick={() => void handleRevertAssume()}
disabled={assumeBusy}
className="tactile-button tactile-button-subtle min-h-9 px-3 text-[var(--color-warning-foreground)] disabled:opacity-60"
className="tactile-button tactile-button-subtle min-h-8 px-3 text-[var(--color-warning-foreground)] disabled:opacity-60"
>
{assumeBusy
? language === "zh"
@ -179,13 +179,13 @@ export default function Header({
</button>
</div>
) : (
<div className="flex items-center justify-end gap-2 px-4 py-2 text-xs md:px-6">
<div className="flex items-center justify-end gap-2 px-4 py-2 text-xs md:px-5">
{isRoot ? (
<button
type="button"
onClick={() => void handleAssumeSandbox()}
disabled={assumeBusy || isLoading}
className="tactile-button tactile-button-soft min-h-9 border border-[color:var(--color-primary-border)] px-3 text-[var(--color-primary)] disabled:opacity-60"
className="tactile-button tactile-button-soft min-h-8 border border-[color:var(--color-primary-border)] px-3 text-[var(--color-primary)] disabled:opacity-60"
>
{assumeBusy
? language === "zh"
@ -199,11 +199,11 @@ export default function Header({
</div>
)}
<div className="flex items-center justify-between px-4 py-3 md:px-6">
<div className="flex items-center justify-between px-4 py-2.5 md:px-5">
<div className="flex items-center gap-4">
<button
type="button"
className="tactile-button tactile-button-soft gap-2 px-3 text-sm font-medium text-[var(--color-text-subtle)] md:hidden"
className="tactile-button tactile-button-soft gap-2 px-3 text-[13px] font-medium text-[var(--color-text-subtle)] md:hidden"
onClick={onMenu}
aria-label="Toggle navigation menu"
>
@ -214,7 +214,7 @@ export default function Header({
{onCollapse && (
<button
type="button"
className="tactile-button tactile-button-soft hidden h-10 w-10 items-center justify-center p-0 text-[var(--color-text-subtle)] md:flex"
className="tactile-button tactile-button-soft hidden h-8 w-8 items-center justify-center p-0 text-[var(--color-text-subtle)] md:flex"
onClick={onCollapse}
aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
>
@ -231,16 +231,16 @@ export default function Header({
<div className="flex items-center gap-3">
<Link
href="/"
className="tactile-button tactile-button-soft gap-2 px-3 text-sm font-medium text-[var(--color-text-subtle)]"
className="tactile-button tactile-button-soft gap-2 px-3 text-[13px] font-medium text-[var(--color-text-subtle)]"
>
</Link>
<span
className={`rounded-[12px] px-3 py-1.5 text-xs font-semibold ${badgeClasses}`}
className={`rounded-[999px] px-3 py-1.5 text-xs font-semibold ${badgeClasses}`}
>
{statusBadge}
</span>
<div className="flex h-10 w-10 items-center justify-center rounded-[12px] bg-gradient-to-br from-[var(--gradient-primary-from)] to-[var(--gradient-primary-to)] text-sm font-semibold text-[var(--color-primary-foreground)] shadow-[var(--shadow-soft)] transition-colors">
<div className="flex h-8 w-8 items-center justify-center rounded-[8px] bg-gradient-to-br from-[var(--gradient-primary-from)] to-[var(--gradient-primary-to)] text-[13px] font-semibold text-[var(--color-primary-foreground)] shadow-[var(--shadow-soft)] transition-colors">
{isLoading ? (
<span className="animate-pulse"></span>
) : (
@ -248,7 +248,7 @@ export default function Header({
)}
</div>
<div className="hidden flex-col text-right text-xs text-[var(--color-text-subtle)] transition-colors sm:flex">
<span className="text-sm font-semibold text-[var(--color-text)]">
<span className="text-[13px] font-semibold text-[var(--color-text)]">
{accountLabel}
</span>
<span>

View File

@ -18,7 +18,7 @@ export default function Sidebar({
}: SidebarProps) {
return (
<SidebarRoot
className={`transition-all duration-300 ${collapsed ? "w-20" : "w-64"} border-r border-[color:var(--color-surface-border)] bg-white/82 p-4 text-[var(--color-text)] shadow-[var(--shadow-soft)] backdrop-blur ${className}`}
className={`transition-all duration-300 ${collapsed ? "w-20" : "w-64"} border-r border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] p-3 text-[var(--color-text)] shadow-[var(--shadow-soft)] backdrop-blur ${className}`}
>
<PanelSidebarContent onNavigate={onNavigate} collapsed={collapsed} />
</SidebarRoot>

View File

@ -116,15 +116,15 @@ export default function PanelLayout({
onCollapse={() => setIsCollapsed((prev) => !prev)}
isCollapsed={isCollapsed}
/>
<main className="flex flex-1 flex-col space-y-4 bg-transparent px-3 py-4 text-[var(--color-text)] transition-colors sm:px-4 md:px-5 lg:px-6">
<main className="flex flex-1 flex-col space-y-3 bg-transparent px-2 py-3 text-[var(--color-text)] transition-colors sm:px-3 md:px-4 lg:px-5">
{requiresSetup ? (
<div className="rounded-[14px] border border-[color:var(--color-warning-muted)] bg-[var(--color-warning-muted)]/90 p-4 text-sm text-[var(--color-warning-foreground)] shadow-[var(--shadow-soft)] transition-colors">
<p className="text-sm">{copy.lockedMessage}</p>
<div className="mt-3 flex flex-wrap gap-2 text-xs">
<div className="rounded-[6px] border border-[color:var(--color-warning-muted)] bg-[var(--color-warning-muted)]/92 p-3 text-[13px] text-[var(--color-warning-foreground)] shadow-[var(--shadow-soft)] transition-colors">
<p className="text-[13px]">{copy.lockedMessage}</p>
<div className="mt-2 flex flex-wrap gap-2 text-xs">
<button
type="button"
onClick={() => router.replace("/panel/account?setupMfa=1")}
className="tactile-button tactile-button-primary px-3 text-sm"
className="tactile-button tactile-button-primary px-3 text-[13px]"
>
{copy.actions.setup}
</button>
@ -132,26 +132,26 @@ export default function PanelLayout({
href={copy.actions.docsUrl}
target="_blank"
rel="noreferrer"
className="tactile-button tactile-button-soft border border-[color:var(--color-primary-border)] px-3 text-sm text-[var(--color-primary)]"
className="tactile-button tactile-button-soft border border-[color:var(--color-primary-border)] px-3 text-[13px] text-[var(--color-primary)]"
>
{copy.actions.docs}
</a>
<button
type="button"
onClick={handleLogout}
className="tactile-button tactile-button-subtle px-3 text-sm text-[var(--color-warning-foreground)]"
className="tactile-button tactile-button-subtle px-3 text-[13px] text-[var(--color-warning-foreground)]"
>
{copy.actions.logout}
</button>
{isLoading ? (
<span className="inline-flex min-h-10 items-center rounded-[12px] border border-[color:var(--color-warning-muted)] bg-[var(--color-warning-muted)] px-3 py-1.5 text-xs text-[var(--color-warning-foreground)]">
<span className="inline-flex min-h-8 items-center rounded-[8px] border border-[color:var(--color-warning-muted)] bg-[var(--color-warning-muted)] px-3 py-1.5 text-xs text-[var(--color-warning-foreground)]">
</span>
) : null}
</div>
</div>
) : null}
<div className="flex w-full flex-1 flex-col gap-4 rounded-[16px] border border-[color:var(--color-surface-border)] bg-white/72 p-3 text-[var(--color-text)] shadow-[var(--shadow-soft)] backdrop-blur md:gap-5 md:p-4">
<div className="flex w-full flex-1 flex-col gap-3 rounded-[6px] border border-[color:var(--color-surface-border)] bg-[var(--color-surface-elevated)] p-3 text-[var(--color-text)] shadow-[var(--shadow-soft)] backdrop-blur md:gap-4 md:p-4">
{children}
</div>
</main>

View File

@ -36,8 +36,8 @@ export default function Footer() {
};
const footerClassName = isDark
? "border-white/10 bg-[linear-gradient(135deg,rgba(15,23,42,0.95),rgba(30,41,59,0.92))] text-slate-300 shadow-[0_24px_60px_rgba(15,23,42,0.16)]"
: "border-slate-200/90 bg-[linear-gradient(135deg,rgba(255,255,255,0.96),rgba(244,247,252,0.95))] text-slate-600 shadow-[0_24px_60px_rgba(148,163,184,0.18)]";
? "border-[color:var(--color-surface-border)] bg-[linear-gradient(135deg,rgba(23,28,40,0.96),rgba(30,36,51,0.94))] text-[var(--color-text-muted)] shadow-[var(--shadow-md)]"
: "border-[color:var(--color-surface-border)] bg-[linear-gradient(135deg,rgba(255,255,255,0.96),rgba(242,245,248,0.95))] text-[var(--color-text-muted)] shadow-[var(--shadow-md)]";
const linkClassName = isDark
? "transition-colors hover:text-white"
: "transition-colors hover:text-slate-950";
@ -54,7 +54,7 @@ export default function Footer() {
return (
<footer
className={`mt-12 flex flex-col items-center justify-center gap-4 rounded-[2rem] border px-6 py-5 text-sm ${footerClassName}`}
className={`mt-8 flex flex-col items-center justify-center gap-3 rounded-[8px] border px-5 py-4 text-[13px] ${footerClassName}`}
>
<div className="flex w-full flex-col items-center gap-4 sm:flex-row sm:justify-between">
<div className="flex gap-4 order-2 sm:order-1">
@ -74,7 +74,7 @@ export default function Footer() {
<a
key={label}
href={href}
className={`flex h-9 w-9 items-center justify-center rounded-full border transition ${iconButtonClassName}`}
className={`flex h-8 w-8 items-center justify-center rounded-[8px] border transition ${iconButtonClassName}`}
>
<Icon className="h-4 w-4" aria-hidden />
<span className="sr-only">{label}</span>
@ -88,7 +88,7 @@ export default function Footer() {
onClick={handleViewToggle}
aria-label={viewToggleLabel}
title={viewToggleLabel}
className={`group flex h-10 w-10 items-center justify-center rounded-full border transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${controlButtonClassName}`}
className={`group flex h-8 w-8 items-center justify-center rounded-[8px] border transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${controlButtonClassName}`}
>
<span
className={`material-symbols-outlined text-xl ${themeIconClassName}`}
@ -102,7 +102,7 @@ export default function Footer() {
aria-pressed={isDark}
aria-label={toggleLabel}
title={toggleLabel}
className={`group relative flex h-10 w-20 items-center rounded-full border px-2 transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${controlButtonClassName}`}
className={`group relative flex h-8 w-[4.5rem] items-center rounded-[999px] border px-1.5 transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${controlButtonClassName}`}
>
<span
className={`relative z-10 flex w-full items-center justify-between ${themeIconClassName}`}
@ -118,7 +118,7 @@ export default function Footer() {
</span>
<span
aria-hidden
className={`absolute inset-y-1 left-1 h-8 w-8 rounded-full shadow-sm transition-transform duration-300 ease-out ${thumbClassName} ${isDark ? "translate-x-0" : "translate-x-10"}`}
className={`absolute inset-y-1 left-1 h-6 w-6 rounded-full shadow-sm transition-transform duration-300 ease-out ${thumbClassName} ${isDark ? "translate-x-0" : "translate-x-10"}`}
/>
</button>
</div>

View File

@ -223,9 +223,9 @@ export default function UnifiedNavigation() {
width: "calc(100% + var(--assistant-reserve-offset, 0px))",
marginRight: "calc(var(--assistant-reserve-offset, 0px) * -1)",
}}
className="sticky top-0 z-50 w-full border-b border-surface-border/80 bg-background/92 text-text backdrop-blur-xl transition-colors duration-150"
className="sticky top-0 z-50 w-full border-b border-surface-border bg-background/88 text-text backdrop-blur-xl transition-colors duration-150"
>
<div className="flex items-center justify-between border-b border-surface-border/70 bg-background px-4 pb-3 pt-[max(0.875rem,env(safe-area-inset-top))] lg:hidden">
<div className="flex items-center justify-between border-b border-surface-border bg-background/92 px-4 pb-2 pt-[max(0.75rem,env(safe-area-inset-top))] lg:hidden">
<Link
href="/"
className="flex items-center gap-2"
@ -239,13 +239,13 @@ export default function UnifiedNavigation() {
className="h-6 w-6"
unoptimized
/>
<span className="text-[1.05rem] font-semibold tracking-tight text-text">
<span className="text-[13px] font-semibold tracking-[-0.02em] text-text">
Cloud-Neutral
</span>
</Link>
<button
onClick={() => setMenuOpen(!menuOpen)}
className="tactile-button tactile-button-soft h-10 w-10 rounded-[12px] p-0"
className="tactile-button tactile-button-soft h-8 w-8 rounded-[8px] p-0"
aria-label="Toggle menu"
>
{menuOpen ? (
@ -256,8 +256,8 @@ export default function UnifiedNavigation() {
</button>
</div>
<div className="hidden lg:block mx-auto w-full max-w-7xl px-6 sm:px-8">
<div className="flex items-center gap-5 py-3">
<div className="mx-auto hidden w-full max-w-7xl px-6 sm:px-8 lg:block">
<div className="flex items-center gap-4 py-2.5">
<div className="flex flex-1 items-center">
<nav className="hidden items-center gap-1 text-sm font-medium text-text-muted lg:flex whitespace-nowrap -ml-2">
{filteredMainNav.map((item) => {

View File

@ -27,22 +27,22 @@ export default function DownloadSummary({
];
return (
<section className="rounded-[2.4rem] border border-slate-900/10 bg-[linear-gradient(180deg,#ffffff,#faf7f2)] p-6 shadow-[0_22px_50px_rgba(15,23,42,0.05)] sm:p-8 lg:p-10">
<div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_22rem] lg:items-end">
<div className="space-y-4">
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.26em] text-text-subtle">
<section className="rounded-[8px] border border-[color:var(--color-surface-border)] bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(242,245,248,0.92))] p-5 shadow-[var(--shadow-md)] sm:p-6 lg:p-7">
<div className="grid gap-5 lg:grid-cols-[minmax(0,1fr)_18rem] lg:items-end">
<div className="space-y-3">
<p className="text-[0.75rem] font-semibold uppercase tracking-[0.22em] text-text-subtle">
{isChinese ? "下载中心" : "Download library"}
</p>
<h1
className={
isChinese
? "text-[2.7rem] font-semibold leading-[0.9] tracking-[-0.08em] text-heading sm:text-[3.4rem]"
: "editorial-display text-[2.9rem] leading-[0.9] tracking-[-0.06em] text-heading sm:text-[3.6rem]"
? "text-[1.8rem] font-semibold leading-[1.02] tracking-[-0.05em] text-heading sm:text-[2.2rem]"
: "text-[1.9rem] font-semibold leading-[1.02] tracking-[-0.04em] text-heading sm:text-[2.3rem]"
}
>
{t.title}
</h1>
<p className="max-w-2xl text-[1rem] leading-8 text-text-muted sm:text-[1.05rem]">
<p className="max-w-2xl text-[0.95rem] leading-7 text-text-muted sm:text-[1rem]">
{t.description}
</p>
</div>
@ -53,13 +53,13 @@ export default function DownloadSummary({
return (
<div
key={item.label}
className="rounded-[1.5rem] border border-slate-900/10 bg-white/85 p-4"
className="rounded-[8px] border border-[color:var(--color-surface-border)] bg-white/88 p-4"
>
<div className="flex items-center gap-2 text-slate-600">
<div className="flex items-center gap-2 text-[var(--color-text-muted)]">
<Icon className="h-4 w-4 text-primary" aria-hidden />
<dt className="text-sm font-medium">{item.label}</dt>
<dt className="text-[13px] font-medium">{item.label}</dt>
</div>
<dd className="mt-3 text-[2rem] font-semibold leading-none tracking-[-0.05em] text-slate-900">
<dd className="mt-2.5 text-[1.7rem] font-semibold leading-none tracking-[-0.04em] text-[var(--color-heading)]">
{item.value.toLocaleString(locale)}
</dd>
</div>

View File

@ -25,19 +25,19 @@ export function PublicPageShell({
<div className="relative min-h-screen bg-background text-text transition-colors duration-150">
<div
aria-hidden
className="pointer-events-none absolute inset-0 bg-[linear-gradient(180deg,rgba(255,255,255,0.58),rgba(255,255,255,0))]"
className="pointer-events-none absolute inset-0 bg-[linear-gradient(180deg,rgba(255,255,255,0.72),rgba(255,255,255,0))]"
/>
<div className="relative">
<UnifiedNavigation />
<div
className={cn(
"mx-auto w-full max-w-6xl px-4 pb-16 sm:px-6 sm:pb-20",
"mx-auto w-full max-w-6xl px-4 pb-12 sm:px-6 sm:pb-16",
containerClassName,
)}
>
<main
className={cn(
"space-y-8 pt-6 sm:space-y-10 sm:pt-10",
"space-y-6 pt-4 sm:space-y-8 sm:pt-6",
mainClassName,
)}
>
@ -58,22 +58,22 @@ export function PublicPageIntro({
className,
}: PublicPageIntroProps) {
return (
<header className={cn("space-y-4", className)}>
<header className={cn("space-y-3", className)}>
{eyebrow ? (
<p className="text-[0.68rem] font-semibold uppercase tracking-[0.26em] text-text-subtle">
<p className="text-[0.75rem] font-semibold uppercase tracking-[0.22em] text-text-subtle">
{eyebrow}
</p>
) : null}
<h1
className={cn(
"max-w-4xl text-[2.5rem] font-semibold leading-[0.92] tracking-[-0.06em] text-heading sm:text-[3.2rem]",
"max-w-4xl text-[1.75rem] font-semibold leading-[1.05] tracking-[-0.035em] text-heading sm:text-[2.2rem]",
titleClassName,
)}
>
{title}
</h1>
{subtitle ? (
<p className="max-w-3xl text-[1rem] leading-8 text-text-muted sm:text-[1.05rem]">
<p className="max-w-3xl text-[0.95rem] leading-7 text-text-muted sm:text-[1rem]">
{subtitle}
</p>
) : null}

View File

@ -5,62 +5,62 @@ export const darkTheme: ThemeDefinition = {
colorScheme: 'dark',
tokens: {
colors: {
background: '#0f172a',
'background-muted': '#1e293b',
surface: '#111827',
'surface-elevated': 'rgba(30, 41, 59, 0.85)',
'surface-translucent': 'rgba(17, 24, 39, 0.75)',
'surface-muted': '#1f2937',
'surface-hover': '#1f2937',
'surface-border': '#1f2937',
'surface-border-strong': '#334155',
text: '#e2e8f0',
heading: '#f8fafc',
'text-muted': '#cbd5f5',
'text-subtle': '#94a3b8',
'text-inverse': '#0f172a',
primary: '#3366ff',
'primary-hover': '#4d7aff',
'primary-muted': '#1e293b',
'primary-border': '#2563eb',
background: '#141422',
'background-muted': '#171c28',
surface: '#171c28',
'surface-elevated': 'rgba(23, 28, 40, 0.9)',
'surface-translucent': 'rgba(23, 28, 40, 0.8)',
'surface-muted': '#1e2433',
'surface-hover': '#262d3f',
'surface-border': 'rgba(202, 196, 208, 0.18)',
'surface-border-strong': 'rgba(202, 196, 208, 0.28)',
text: '#e6e1e5',
heading: '#e6e1e5',
'text-muted': '#b0b8c8',
'text-subtle': '#8b95a8',
'text-inverse': '#141422',
primary: '#4b8fe8',
'primary-hover': '#6aa2ee',
'primary-muted': '#1c3355',
'primary-border': '#31527f',
'primary-foreground': '#ffffff',
accent: '#60a5fa',
'accent-muted': '#1d4ed8',
'accent-foreground': '#e0f2fe',
success: '#22c55e',
'success-muted': '#14532d',
accent: '#5b8def',
'accent-muted': '#1c3355',
'accent-foreground': '#d7e6ff',
success: '#5cb978',
'success-muted': '#183e28',
'success-foreground': '#bbf7d0',
warning: '#fbbf24',
'warning-muted': '#92400e',
'warning-foreground': '#fde68a',
danger: '#f87171',
'danger-muted': '#7f1d1d',
warning: '#e0ae5a',
'warning-muted': '#3d3520',
'warning-foreground': '#ffe082',
danger: '#ef9a9a',
'danger-muted': '#442526',
'danger-foreground': '#fee2e2',
info: '#38bdf8',
'info-muted': '#1d4ed8',
'info-foreground': '#bae6fd',
overlay: 'rgba(2, 6, 23, 0.55)',
ring: 'rgba(168, 85, 247, 0.55)',
focus: 'rgba(129, 140, 248, 0.45)',
divider: 'rgba(148, 163, 184, 0.24)',
'badge-surface': '#1f2937',
'badge-muted': '#334155',
'badge-foreground': '#cbd5f5',
info: '#82aaff',
'info-muted': '#1c3355',
'info-foreground': '#d7e6ff',
overlay: 'rgba(2, 6, 23, 0.48)',
ring: 'rgba(75, 143, 232, 0.35)',
focus: 'rgba(75, 143, 232, 0.28)',
divider: 'rgba(202, 196, 208, 0.18)',
'badge-surface': '#262d3f',
'badge-muted': '#1e2433',
'badge-foreground': '#cac4d0',
},
gradients: {
'app-from': '#1e293b',
'app-via': '#0f172a',
'app-to': '#1d4ed8',
'primary-from': '#3366ff',
'primary-to': '#254edb',
'app-from': '#141422',
'app-via': '#171c28',
'app-to': '#1e2433',
'primary-from': '#4b8fe8',
'primary-to': '#1c3355',
},
shadows: {
sm: '0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(15, 23, 42, 0.6)',
md: '0 20px 45px rgba(2, 6, 23, 0.6)',
sm: '0 1px 2px rgba(0, 0, 0, 0.24), 0 8px 24px rgba(2, 6, 23, 0.28)',
md: '0 18px 40px rgba(2, 6, 23, 0.34), 0 3px 8px rgba(2, 6, 23, 0.18)',
},
radii: {
lg: '1rem',
xl: '1.5rem',
lg: '0.375rem',
xl: '0.5rem',
pill: '999px',
},
},

View File

@ -4,17 +4,17 @@ type ButtonVariant = 'primary' | 'secondary'
export const designTokens = {
colors: {
primary: 'text-white bg-[#3467e9] hover:bg-[#2957cf] focus-visible:outline-[#2957cf]',
accent: 'text-[#3467e9]',
accentHover: 'text-[#2957cf]',
textPrimary: 'text-slate-900',
textSecondary: 'text-slate-600',
textMuted: 'text-slate-500',
border: 'border-black/10',
primary: 'text-white bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] focus-visible:outline-[var(--color-primary)]',
accent: 'text-[var(--color-primary)]',
accentHover: 'text-[var(--color-primary-hover)]',
textPrimary: 'text-[var(--color-text)]',
textSecondary: 'text-[var(--color-text-muted)]',
textMuted: 'text-[var(--color-text-subtle)]',
border: 'border-[color:var(--color-surface-border)]',
surface: 'bg-white',
surfaceAlt: 'bg-[#f6f7f9]',
background: 'bg-[#f6f7f9]',
gradient: 'bg-white',
surfaceAlt: 'bg-[var(--color-surface-muted)]',
background: 'bg-[var(--color-background)]',
gradient: 'bg-[linear-gradient(180deg,rgba(255,255,255,0.94),rgba(242,245,248,0.86))]',
},
layout: {
container: 'max-w-7xl mx-auto px-6 md:px-10',
@ -31,18 +31,20 @@ export const designTokens = {
product: 'min-h-[60vh]',
} satisfies Record<PageVariant, string>,
background: {
homepage: 'bg-gradient-to-br from-brand-surface/60 via-white to-white',
product: 'bg-gradient-to-b from-brand-surface/70 to-white',
homepage:
'bg-[linear-gradient(160deg,rgba(242,245,248,0.94),rgba(255,255,255,0.92),rgba(232,240,251,0.72))]',
product:
'bg-[linear-gradient(180deg,rgba(242,245,248,0.92),rgba(255,255,255,0.96))]',
} satisfies Record<PageVariant, string>,
},
effects: {
radii: {
sm: 'rounded-md',
md: 'rounded-lg',
xl: 'rounded-xl',
sm: 'rounded-[6px]',
md: 'rounded-[8px]',
xl: 'rounded-[8px]',
},
shadows: {
soft: 'shadow-none',
soft: 'shadow-[var(--shadow-soft)]',
},
},
transitions: {
@ -50,17 +52,19 @@ export const designTokens = {
product: 'transition duration-300',
} satisfies Record<PageVariant, string>,
cards: {
base: 'border border-black/10 rounded-md md:rounded-lg transition duration-200 bg-white',
base: 'border border-[color:var(--color-surface-border)] rounded-[6px] md:rounded-[8px] transition duration-200 bg-white/94 shadow-[var(--shadow-soft)]',
},
buttons: {
base: 'inline-flex items-center justify-center font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#2957cf]/60',
base: 'inline-flex items-center justify-center font-semibold focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-focus)]',
palette: {
primary: 'bg-[#3467e9] text-white hover:bg-[#2957cf]',
secondary: 'border border-black/10 text-[#2957cf] hover:bg-[#f6f7f9]',
primary:
'bg-[var(--color-primary)] text-white hover:bg-[var(--color-primary-hover)]',
secondary:
'border border-[color:var(--color-surface-border)] bg-white/88 text-[var(--color-primary)] hover:bg-[var(--color-surface-muted)]',
} satisfies Record<ButtonVariant, string>,
shape: {
homepage: 'rounded-md px-6 py-3 text-base',
product: 'rounded-md px-5 py-3 text-sm',
homepage: 'rounded-[8px] px-5 py-2.5 text-[13px]',
product: 'rounded-[8px] px-4 py-2 text-[13px]',
} satisfies Record<PageVariant, string>,
},
}

View File

@ -5,62 +5,62 @@ export const lightTheme: ThemeDefinition = {
colorScheme: 'light',
tokens: {
colors: {
background: '#f8fafc',
'background-muted': '#f1f5f9',
background: '#f8f9fa',
'background-muted': '#f2f5f8',
surface: '#ffffff',
'surface-elevated': 'rgba(255, 255, 255, 0.92)',
'surface-translucent': 'rgba(255, 255, 255, 0.85)',
'surface-muted': '#f1f5f9',
'surface-hover': '#f3f4f6',
'surface-border': '#e2e8f0',
'surface-border-strong': '#cbd5e1',
text: '#111827',
heading: '#0f172a',
'text-muted': '#4b5563',
'text-subtle': '#6b7280',
'surface-elevated': 'rgba(255, 255, 255, 0.94)',
'surface-translucent': 'rgba(255, 255, 255, 0.88)',
'surface-muted': '#f2f5f8',
'surface-hover': '#edf2f7',
'surface-border': 'rgba(166, 180, 200, 0.18)',
'surface-border-strong': 'rgba(166, 180, 200, 0.28)',
text: '#1c1b1f',
heading: '#1c1b1f',
'text-muted': '#667085',
'text-subtle': '#98a1b2',
'text-inverse': '#f8fafc',
primary: '#3366ff',
'primary-hover': '#254edb',
'primary-muted': '#f0f4ff',
'primary-border': '#d6e0ff',
primary: '#0058bd',
'primary-hover': '#0b4f9a',
'primary-muted': '#e8f0fb',
'primary-border': '#d7e4f7',
'primary-foreground': '#ffffff',
accent: '#6366f1',
'accent-muted': '#e0e7ff',
'accent-foreground': '#312e81',
success: '#16a34a',
accent: '#5b8def',
'accent-muted': '#e8f0fb',
'accent-foreground': '#23446c',
success: '#34a853',
'success-muted': '#dcfce7',
'success-foreground': '#166534',
warning: '#f59e0b',
'warning-muted': '#fef3c7',
'warning-foreground': '#92400e',
danger: '#ef4444',
warning: '#8f4a00',
'warning-muted': '#fff3cd',
'warning-foreground': '#664d03',
danger: '#c3655c',
'danger-muted': '#fee2e2',
'danger-foreground': '#7f1d1d',
info: '#2563eb',
'info-muted': '#dbeafe',
'info-foreground': '#1d4ed8',
overlay: 'rgba(15, 23, 42, 0.45)',
ring: '#c4b5fd',
focus: 'rgba(124, 58, 237, 0.35)',
divider: 'rgba(15, 23, 42, 0.08)',
'badge-surface': '#e5e7eb',
'badge-muted': '#f3f4f6',
'badge-foreground': '#1f2937',
info: '#5b8def',
'info-muted': '#e8f0fb',
'info-foreground': '#23446c',
overlay: 'rgba(28, 27, 31, 0.28)',
ring: '#d7e4f7',
focus: 'rgba(0, 88, 189, 0.18)',
divider: 'rgba(28, 27, 31, 0.08)',
'badge-surface': '#e9eef4',
'badge-muted': '#f2f5f8',
'badge-foreground': '#49454f',
},
gradients: {
'app-from': '#ede9fe',
'app-via': '#f5f3ff',
'app-to': '#e0f2fe',
'primary-from': '#3366ff',
'primary-to': '#254edb',
'app-from': '#f8f9fa',
'app-via': '#f3f6f9',
'app-to': '#eef2f6',
'primary-from': '#0058bd',
'primary-to': '#5b8def',
},
shadows: {
sm: '0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.04)',
md: '0 12px 32px rgba(15, 23, 42, 0.12)',
sm: '0 1px 2px rgba(90, 108, 132, 0.06), 0 6px 18px rgba(90, 108, 132, 0.05)',
md: '0 12px 30px rgba(90, 108, 132, 0.08), 0 2px 6px rgba(90, 108, 132, 0.05)',
},
radii: {
lg: '1rem',
xl: '1.5rem',
lg: '0.375rem',
xl: '0.5rem',
pill: '999px',
},
},