Skip to content
Open
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
28 changes: 14 additions & 14 deletions frontend/src/app/layout/app-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export function AppHeader() {
<header
data-layout="unified"
data-surface="frosted-bar"
className="fixed inset-x-0 top-0 z-50 border-b border-[#1c231e]/10 bg-transparent text-[#1c231e] shadow-[inset_0_1px_0_rgba(255,255,255,0.55)] backdrop-blur-xl"
className="fixed inset-x-0 top-0 z-50 border-b border-app-ink/10 bg-transparent text-app-ink shadow-app-header backdrop-blur-xl"
>
<div className="relative mx-auto grid min-h-14 w-full max-w-[90rem] grid-cols-[auto_1fr_auto] items-center gap-4 px-4 sm:px-6 lg:px-8">
<div className="flex min-w-0 items-center gap-1.5">
Expand All @@ -121,7 +121,7 @@ export function AppHeader() {
aria-label="返回 Windup 工作台"
data-motion="text-wave"
onClick={() => playTextWave('brand')}
className={`flex min-h-11 shrink-0 items-center gap-2.5 pr-1 text-[#1c231e] focus-visible:rounded-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#284331] max-[360px]:hidden ${
className={`flex min-h-11 shrink-0 items-center gap-2.5 pr-1 text-app-ink focus-visible:rounded-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-app-accent max-[360px]:hidden ${
wave.entry === 'brand' ? 'app-header-text-wave' : ''
}`}
>
Expand All @@ -147,10 +147,10 @@ export function AppHeader() {
aria-current={active ? 'page' : undefined}
data-motion="text-wave"
onClick={() => playTextWave(item.motionKey)}
className={`relative inline-flex min-h-11 items-center px-1.5 text-[12px] font-medium whitespace-nowrap transition-colors after:absolute after:inset-x-1.5 after:bottom-0 after:h-[2px] after:origin-center after:bg-[#284331] after:transition-transform focus-visible:rounded-md focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#284331] sm:px-3 sm:text-[13px] sm:after:inset-x-3 ${
className={`relative inline-flex min-h-11 items-center px-1.5 text-[12px] font-medium whitespace-nowrap transition-colors after:absolute after:inset-x-1.5 after:bottom-0 after:h-[2px] after:origin-center after:bg-app-accent after:transition-transform focus-visible:rounded-md focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-app-accent sm:px-3 sm:text-[13px] sm:after:inset-x-3 ${
active
? 'text-[#243c2c] after:scale-x-100'
: 'text-[#667068] after:scale-x-0 hover:text-[#26372c]'
? 'text-app-accent after:scale-x-100'
: 'text-app-muted after:scale-x-0 hover:text-app-accent'
} ${wave.entry === item.motionKey ? 'app-header-text-wave' : ''}`}
>
{item.compactLabel ? (
Expand Down Expand Up @@ -183,15 +183,15 @@ export function AppHeader() {
{session.state.status === 'booting' ? (
<span
aria-label="正在恢复登录状态"
className="inline-grid min-h-11 min-w-11 place-items-center text-sm text-[#778078]"
className="inline-grid min-h-11 min-w-11 place-items-center text-sm text-app-faint"
>
</span>
) : session.state.status === 'guest' ? (
<Link
to={accountEntry}
aria-label="登录 / 注册"
className="inline-flex min-h-10 items-center rounded-lg border border-[#2d3b31]/14 bg-white/45 px-3 text-[13px] font-medium whitespace-nowrap text-[#34483a] transition-colors hover:bg-white/75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#284331]"
className="inline-flex min-h-10 items-center rounded-lg border border-app-ink/14 bg-app-surface-raised/45 px-3 text-[13px] font-medium whitespace-nowrap text-app-ink-soft transition-colors hover:bg-app-surface-raised/75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-app-accent"
>
<span className="hidden sm:inline">登录 / 注册</span>
<span className="sm:hidden">登录</span>
Expand All @@ -204,12 +204,12 @@ export function AppHeader() {
aria-expanded={accountMenuOpen}
title={session.state.user.email}
onClick={toggleAccountMenu}
className={`inline-flex min-h-10 max-w-24 items-center gap-2 rounded-lg px-2.5 text-xs font-medium text-[#3e4941] transition-[color,background-color,transform] duration-150 ease-out hover:bg-[#e5e8e3] hover:text-[#26372c] active:translate-y-px active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#284331] motion-reduce:transform-none sm:max-w-36 ${
accountMenuOpen ? 'bg-[#e5e8e3] text-[#26372c]' : ''
className={`inline-flex min-h-10 max-w-24 items-center gap-2 rounded-lg px-2.5 text-xs font-medium text-app-ink-soft transition-[color,background-color,transform] duration-150 ease-out hover:bg-app-accent-muted hover:text-app-accent active:translate-y-px active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-app-accent motion-reduce:transform-none sm:max-w-36 ${
accountMenuOpen ? 'bg-app-accent-muted text-app-accent' : ''
}`}
>
<span
className={`grid h-7 w-7 shrink-0 place-items-center rounded-full bg-[#dfe5df] font-serif text-[11px] text-[#284331] transition-transform duration-200 ease-[cubic-bezier(0.16,1,0.3,1)] motion-reduce:transition-none ${
className={`grid h-7 w-7 shrink-0 place-items-center rounded-full bg-app-accent-muted font-serif text-[11px] text-app-accent transition-transform duration-200 ease-[cubic-bezier(0.16,1,0.3,1)] motion-reduce:transition-none ${
accountMenuOpen ? 'scale-110' : 'scale-100'
}`}
>
Expand All @@ -220,7 +220,7 @@ export function AppHeader() {
</span>
<span
aria-hidden="true"
className={`text-[10px] text-[#7b847d] transition-transform duration-200 ease-[cubic-bezier(0.16,1,0.3,1)] motion-reduce:transition-none ${
className={`text-[10px] text-app-faint transition-transform duration-200 ease-[cubic-bezier(0.16,1,0.3,1)] motion-reduce:transition-none ${
accountMenuOpen ? 'rotate-180' : 'rotate-0'
}`}
>
Expand All @@ -235,7 +235,7 @@ export function AppHeader() {
aria-hidden={accountMenuOpen ? undefined : true}
inert={!accountMenuOpen}
onAnimationEnd={finishAccountMenuMotion}
className={`absolute top-[calc(100%+0.5rem)] right-0 grid min-w-44 origin-top-right overflow-hidden rounded-lg border border-[#1c231e]/12 bg-[#f8f7f2] p-1.5 shadow-[0_8px_24px_rgba(28,35,30,0.10)] ${
className={`absolute top-[calc(100%+0.5rem)] right-0 grid min-w-44 origin-top-right overflow-hidden rounded-lg border border-app-ink/12 bg-app-surface-raised p-1.5 shadow-app-menu ${
accountMenuState === 'open'
? 'visible app-header-account-menu-in'
: accountMenuState === 'closing'
Expand All @@ -248,15 +248,15 @@ export function AppHeader() {
aria-label="打开账号中心"
aria-current={pathname.startsWith('/account') ? 'page' : undefined}
onClick={() => setAccountMenuState('closing')}
className="flex min-h-10 items-center rounded-md px-3 text-[13px] text-[#3f4942] transition-colors hover:bg-[#e8ebe7] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[#284331]"
className="flex min-h-10 items-center rounded-md px-3 text-[13px] text-app-ink-soft transition-colors hover:bg-app-accent-muted focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-app-accent"
>
账号中心
</Link>
<button
type="button"
onClick={signOut}
aria-label="退出登录"
className="flex min-h-10 items-center rounded-md px-3 text-left text-[13px] text-[#707871] transition-colors hover:bg-[#e8ebe7] hover:text-[#26372c] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[#284331]"
className="flex min-h-10 items-center rounded-md px-3 text-left text-[13px] text-app-muted transition-colors hover:bg-app-accent-muted hover:text-app-accent focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-app-accent"
>
退出登录
</button>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface AppShellProps {
/** 登录产品外壳;只服务工作台与受保护业务页。 */
export function AppShell({ children }: AppShellProps) {
return (
<div className="min-h-screen bg-white text-slate-900">
<div className="min-h-screen bg-app-canvas text-app-ink">
<AppHeader />
{/*
外壳只管顶栏。页面自己决定宽度与留白,不在这里统一夹到屏幕中间,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/layout/page-back-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function PageBackButton() {
aria-label="返回上一页"
title="返回上一页"
onClick={goBack}
className="inline-grid h-9 w-9 shrink-0 place-items-center rounded-md border border-[#25342a]/12 bg-white/35 text-[#34483a] transition-[background-color,color,transform] duration-150 hover:bg-white/75 hover:text-[#1f3326] active:translate-y-px active:scale-[0.96] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#284331] motion-reduce:transform-none"
className="inline-grid h-9 w-9 shrink-0 place-items-center rounded-md border border-app-accent/12 bg-app-surface-raised/35 text-app-ink-soft transition-[background-color,color,transform] duration-150 hover:bg-app-surface-raised/75 hover:text-app-accent-hover active:translate-y-px active:scale-[0.96] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-app-accent motion-reduce:transform-none"
>
<span aria-hidden="true" className="text-[1.2rem] leading-none">
Expand Down
52 changes: 46 additions & 6 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,46 @@ body,
--color-accent-deep: #1b2f22;
--color-spark: #555b54;

/*
* 登录后产品界面的共享色彩语义。页面只选择角色,不选择具体色值:这样工作台、
* 项目资产、创作与预览台共享同一层级,错误/警告/信息仍保留各自含义。
*/
--color-app-canvas: #f3f2ec;
--color-app-surface: #f7f8f4;
--color-app-surface-raised: #ffffff;
--color-app-surface-muted: #e7eae5;
--color-app-surface-strong: #dfe3df;
--color-app-ink: #1d251f;
--color-app-ink-soft: #303a32;
--color-app-muted: #687169;
--color-app-faint: #778078;
--color-app-line: #cbd1ca;
--color-app-line-strong: #8fa092;
--color-app-accent: #284331;
--color-app-accent-hover: #1f3828;
--color-app-accent-soft: #dce9df;
--color-app-accent-muted: #edf2ec;
--color-app-on-accent: #f7f8f4;
--color-app-danger: #6f3928;
--color-app-danger-hover: #593022;
--color-app-danger-muted: #7a5548;
--color-app-danger-line: #d6c1b6;
--color-app-danger-soft: #fff8f3;
--color-app-warning: #67552e;
--color-app-warning-line: #c7a967;
--color-app-warning-soft: #f4eddc;
--color-app-info: #2a5284;
--color-app-info-hover: #3668a0;

--shadow-app-card: 0 18px 45px rgb(29 37 31 / 9%);
--shadow-app-panel: 0 22px 60px rgb(29 37 31 / 12%);
--shadow-app-page: 0 26px 80px rgb(29 37 31 / 10%);
--shadow-app-header: inset 0 1px 0 rgb(255 255 255 / 55%);
--shadow-app-menu: 0 8px 24px rgb(29 37 31 / 10%);
--shadow-app-stage-panel: 0 12px 32px rgb(29 37 31 / 18%);
--shadow-app-float: 0 16px 40px rgb(29 37 31 / 24%);
--shadow-app-pulse: 0 0 0 16px rgb(40 67 49 / 5%);

/*
* 字阶按约 1.3 递进,display 与 body 之间留了 title / subtitle / lead 三档,
* 补上原先 100px 直接跳到 14px 的断层。
Expand Down Expand Up @@ -336,9 +376,9 @@ body,
width: 5px;
height: 5px;
border-radius: 1px;
background: #bfc6bf;
background: var(--color-app-line);
opacity: 0.62;
box-shadow: 0 0 0 1px rgb(255 255 255 / 18%);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-app-surface-raised) 18%, transparent);
animation: project-pixel-field 4.6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}

Expand Down Expand Up @@ -375,7 +415,7 @@ body,
}

.project-pixel-mark i.is-active {
background: #202120;
background: var(--color-app-ink-soft);
opacity: 0.9;
box-shadow: none;
animation: project-pixel-frame 3.4s ease-in-out infinite;
Expand All @@ -394,14 +434,14 @@ body,
}

.project-pixel-mark i.is-handle {
background: #050505;
background: var(--color-app-ink);
opacity: 1;
box-shadow: 0 0 0 4px rgb(17 18 17 / 9%);
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-app-ink) 9%, transparent);
animation: project-pixel-handle 3.8s ease-in-out infinite;
}

.project-pixel-mark i.is-cursor {
background: #090a09;
background: var(--color-app-accent);
opacity: 1;
animation: project-pixel-cursor 2.8s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
30 changes: 15 additions & 15 deletions frontend/src/pages/account/account.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
width: 100%;
min-height: 3rem;
padding-inline: 0.875rem;
border: 1px solid #c7cbc5;
border: 1px solid var(--color-app-line);
border-radius: 10px;
background: #fbfbf8;
color: #202720;
background: var(--color-app-surface);
color: var(--color-app-ink);
font-size: 1rem;
outline: none;
transition:
Expand All @@ -15,23 +15,23 @@
}

.account-field::placeholder {
color: #8a8f88;
color: var(--color-app-faint);
}

.account-field:hover {
border-color: #adb4ac;
border-color: var(--color-app-line-strong);
}

.account-field:focus {
border-color: #526b59;
background: white;
box-shadow: 0 0 0 3px rgb(82 107 89 / 10%);
border-color: var(--color-app-accent);
background: var(--color-app-surface-raised);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-app-accent) 10%, transparent);
}

.account-field:disabled {
cursor: not-allowed;
background: #eff0ec;
color: #7a8079;
background: var(--color-app-surface-muted);
color: var(--color-app-faint);
}

.account-primary-button {
Expand All @@ -41,25 +41,25 @@
justify-content: center;
padding-inline: 1rem;
border-radius: 0.5rem;
background: #284331;
color: #f7f6f0;
background: var(--color-app-accent);
color: var(--color-app-on-accent);
font-size: 0.875rem;
font-weight: 600;
transition: background-color 150ms;
}

.account-primary-button:hover {
background: #1f3627;
background: var(--color-app-accent-hover);
}

.account-primary-button:focus-visible {
outline: 2px solid #284331;
outline: 2px solid var(--color-app-accent);
outline-offset: 2px;
}

.account-primary-button:disabled {
cursor: not-allowed;
background: #8a8f89;
background: var(--color-app-line-strong);
}

.account-badge-button {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/account/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ describe('AccountPage', () => {

expect(await screen.findByRole('heading', { name: '账号中心' })).toBeTruthy()
const page = container.querySelector('[data-account-page]')
expect(page?.className).toContain('bg-[#f3f2ec]')
expect(page?.className).toContain('bg-app-canvas')
const shell = container.querySelector('[data-account-shell]')
expect(shell?.className).toContain('max-w-[1560px]')
expect(screen.getByRole('heading', { name: '账号中心' }).className).toContain(
Expand Down
Loading