Skip to content

Commit a1b0c2d

Browse files
committed
fix(layout): 修复 GitHub Pages 布局溢出和样式一致性问题
- 移除全局 margin/padding 重置,避免与组件样式冲突 - 在 html/body/#root 添加 overflow-x: clip 防止绝对定位元素撑宽文档 - 新增 .landing-shell 和 .landing-center 工具类统一容器宽度和内边距 - 重构所有 Landing 组件使用新工具类替代重复的 max-w-7xl + px-* 样式 - 修复 HeroSection 粒子动画使用预计算位置,防止 hydration 抖动 - 修复文档链接指向完
1 parent b760c3e commit a1b0c2d

12 files changed

Lines changed: 109 additions & 32 deletions

index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,6 @@
151151
*::before,
152152
*::after {
153153
box-sizing: border-box;
154-
margin: 0;
155-
padding: 0;
156154
}
157155

158156
html {
@@ -169,12 +167,14 @@
169167
-moz-osx-font-smoothing: grayscale;
170168
text-rendering: optimizeLegibility;
171169
scroll-behavior: smooth;
170+
overflow-x: hidden;
172171
}
173172

174173
body {
175174
min-height: 100vh;
176175
min-height: 100dvh; /* 动态视口高度 */
177176
width: 100%;
177+
max-width: 100vw;
178178
background: #0a0a0a;
179179
color: #fafafa;
180180
overflow-x: hidden;

src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HashRouter as Router, Routes, Route } from 'react-router-dom';
1+
import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
22
import { Suspense, lazy } from 'react';
33
import ErrorBoundary from '@/components/ui/ErrorBoundary';
44
import LoadingSpinner from '@/components/ui/LoadingSpinner';
@@ -30,6 +30,9 @@ export default function App() {
3030
<Route path="/app" element={<AdvancedDigitalHumanPage />} />
3131
<Route path="/advanced" element={<AdvancedDigitalHumanPage />} />
3232
<Route path="/digital-human" element={<DigitalHumanPage />} />
33+
34+
{/* Fallback - 防止未知 hash 路径导致空白页 */}
35+
<Route path="*" element={<Navigate to="/" replace />} />
3336
</Routes>
3437
</Suspense>
3538
</Router>

src/components/landing/CTASection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ export default function CTASection() {
1010
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[min(600px,80vw)] h-[min(600px,80vh)] bg-blue-600/10 rounded-full blur-[100px]" />
1111
</div>
1212

13-
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
14-
<div className="grid gap-10 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)] lg:items-start">
13+
<div className="landing-shell relative z-10">
14+
<div className="landing-center max-w-5xl grid gap-10 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)] lg:items-start">
1515
{/* Left: CTA Content */}
1616
<div>
1717
<h2 className="text-3xl sm:text-4xl font-bold text-white mb-4">

src/components/landing/FeaturesSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default function FeaturesSection() {
7474
}}
7575
/>
7676

77-
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
77+
<div className="landing-shell relative z-10">
7878
{/* Section Header */}
7979
<div className="text-center max-w-3xl mx-auto mb-16">
8080
<h2 className="text-3xl sm:text-4xl font-bold text-white mb-4">
@@ -92,7 +92,7 @@ export default function FeaturesSection() {
9292
{features.map((feature, index) => (
9393
<div
9494
key={feature.title}
95-
className={`group relative flex h-full flex-col rounded-2xl border p-6 backdrop-blur-sm transition-all duration-300 hover:scale-[1.02] hover:shadow-xl ${feature.borderColor} ${feature.bgColor}`}
95+
className={`group relative flex flex-col rounded-2xl border p-6 backdrop-blur-sm transition-all duration-300 hover:scale-[1.02] hover:shadow-xl ${feature.borderColor} ${feature.bgColor}`}
9696
style={{
9797
animationDelay: `${index * 100}ms`,
9898
}}
@@ -130,7 +130,7 @@ export default function FeaturesSection() {
130130
{ value: '60+', label: 'FPS 流畅渲染' },
131131
{ value: '468', label: '面部关键点' },
132132
{ value: '<100ms', label: '首字响应延迟' },
133-
{ value: '0', label: '配置即开即用' },
133+
{ value: '0 配置', label: '开箱即用' },
134134
].map((stat) => (
135135
<div key={stat.label} className="text-center">
136136
<div className="text-3xl sm:text-4xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">

src/components/landing/Footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default function Footer() {
5454

5555
return (
5656
<footer className="relative bg-[#050508] border-t border-white/5">
57-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
57+
<div className="landing-shell py-12">
5858
<div className="mb-12 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
5959
{/* Brand */}
6060
<div className="sm:col-span-2 lg:col-span-1">

src/components/landing/HeroSection.tsx

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,30 @@ const highlights = [
77
{ icon: Shield, label: '生产级品质' },
88
];
99

10+
// 预计算粒子位置,避免 Math.random() 导致每次渲染不一致(hydration 抖动)
11+
const PARTICLES = [
12+
{ left: '8%', top: '12%', delay: '0s', duration: '2.8s' },
13+
{ left: '23%', top: '5%', delay: '0.4s', duration: '3.5s' },
14+
{ left: '41%', top: '18%', delay: '1.1s', duration: '2.2s' },
15+
{ left: '67%', top: '8%', delay: '0.7s', duration: '4.0s' },
16+
{ left: '85%', top: '22%', delay: '1.8s', duration: '3.1s' },
17+
{ left: '92%', top: '45%', delay: '0.3s', duration: '2.5s' },
18+
{ left: '78%', top: '62%', delay: '2.2s', duration: '3.8s' },
19+
{ left: '55%', top: '78%', delay: '0.9s', duration: '2.9s' },
20+
{ left: '31%', top: '85%', delay: '1.5s', duration: '4.2s' },
21+
{ left: '12%', top: '68%', delay: '0.6s', duration: '3.3s' },
22+
{ left: '4%', top: '40%', delay: '2.0s', duration: '2.7s' },
23+
{ left: '19%', top: '55%', delay: '1.3s', duration: '3.6s' },
24+
{ left: '48%', top: '35%', delay: '0.2s', duration: '2.4s' },
25+
{ left: '73%', top: '28%', delay: '1.7s', duration: '4.1s' },
26+
{ left: '88%', top: '72%', delay: '0.8s', duration: '3.0s' },
27+
{ left: '62%', top: '90%', delay: '2.5s', duration: '2.6s' },
28+
{ left: '35%', top: '48%', delay: '1.0s', duration: '3.9s' },
29+
{ left: '16%', top: '32%', delay: '0.5s', duration: '2.3s' },
30+
{ left: '94%', top: '15%', delay: '1.9s', duration: '4.3s' },
31+
{ left: '50%', top: '60%', delay: '0.1s', duration: '3.2s' },
32+
];
33+
1034
export default function HeroSection() {
1135
return (
1236
<section className="relative min-h-screen flex items-center justify-center overflow-hidden">
@@ -27,24 +51,24 @@ export default function HeroSection() {
2751
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[min(800px,100vw)] h-[min(800px,100vh)] bg-blue-600/10 rounded-full blur-[120px]" />
2852
<div className="absolute top-1/4 right-1/4 w-[min(400px,50vw)] h-[min(400px,50vh)] bg-purple-600/10 rounded-full blur-[100px]" />
2953

30-
{/* Floating Particles */}
31-
<div className="absolute inset-0 overflow-hidden">
32-
{[...Array(20)].map((_, i) => (
54+
{/* Floating Particles - 固定预计算位置,防止 hydration 抖动 */}
55+
<div className="absolute inset-0 overflow-hidden" aria-hidden="true">
56+
{PARTICLES.map((p, i) => (
3357
<div
3458
key={i}
3559
className="absolute w-1 h-1 bg-blue-400/30 rounded-full animate-pulse"
3660
style={{
37-
left: `${Math.random() * 100}%`,
38-
top: `${Math.random() * 100}%`,
39-
animationDelay: `${Math.random() * 3}s`,
40-
animationDuration: `${2 + Math.random() * 3}s`,
61+
left: p.left,
62+
top: p.top,
63+
animationDelay: p.delay,
64+
animationDuration: p.duration,
4165
}}
4266
/>
4367
))}
4468
</div>
4569

4670
{/* Content */}
47-
<div className="relative z-10 mx-auto w-full max-w-7xl px-4 pt-28 pb-20 sm:px-6 lg:px-8 lg:pt-24 lg:pb-16">
71+
<div className="landing-shell relative z-10 pt-28 pb-20 lg:pt-24 lg:pb-16">
4872
<div className="grid items-center gap-10 lg:grid-cols-2 lg:gap-14">
4973
{/* Left: Text Content */}
5074
<div className="mx-auto max-w-2xl text-center lg:mx-0 lg:max-w-none lg:text-left">
@@ -80,7 +104,7 @@ export default function HeroSection() {
80104
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
81105
</Link>
82106
<a
83-
href="docs/"
107+
href="https://lessup.github.io/meta-human/docs/"
84108
className="group inline-flex items-center justify-center gap-2 px-8 py-4 bg-white/5 hover:bg-white/10 text-white font-semibold rounded-xl border border-white/10 transition-all hover:border-white/20"
85109
>
86110
<BookOpen className="w-5 h-5" />

src/components/landing/Navbar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default function Navbar() {
3939
: 'bg-transparent'
4040
}`}
4141
>
42-
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
42+
<div className="landing-shell">
4343
<div className="flex items-center justify-between h-16">
4444
{/* Logo */}
4545
<Link to="/" className="flex items-center gap-2 group">
@@ -116,7 +116,7 @@ export default function Navbar() {
116116
isMobileMenuOpen ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
117117
}`}
118118
>
119-
<div className="mx-auto max-w-7xl space-y-3 border-t border-white/10 bg-black/95 px-4 py-4 backdrop-blur-lg">
119+
<div className="landing-shell space-y-3 border-t border-white/10 bg-black/95 py-4 backdrop-blur-lg">
120120
{navLinks.map((link) =>
121121
link.external ? (
122122
<a

src/components/landing/TechStackSection.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export default function TechStackSection() {
5757
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[min(1000px,100vw)] h-[500px] bg-indigo-600/5 rounded-full blur-[100px]" />
5858
</div>
5959

60-
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
60+
<div className="landing-shell relative z-10">
6161
{/* Section Header */}
6262
<div className="text-center max-w-3xl mx-auto mb-16">
6363
<h2 className="text-3xl sm:text-4xl font-bold text-white mb-4">
@@ -69,30 +69,30 @@ export default function TechStackSection() {
6969
</div>
7070

7171
{/* Architecture Diagram */}
72-
<div className="mb-20">
72+
<div className="landing-center mb-20 max-w-5xl">
7373
<div className="relative overflow-hidden rounded-3xl border border-white/10 bg-gradient-to-b from-white/[0.02] to-transparent p-6 sm:p-8">
7474
{/* Layers */}
75-
<div className="grid gap-6">
75+
<div className="grid gap-5">
7676
{techLayers.map((layer, index) => (
7777
<div key={layer.title} className="relative">
7878
{/* Connection Line */}
7979
{index < techLayers.length - 1 && (
80-
<div className="absolute left-8 top-full w-px h-6 bg-gradient-to-b from-white/20 to-transparent" />
80+
<div className="absolute left-8 top-full w-px h-5 bg-gradient-to-b from-white/20 to-transparent" />
8181
)}
8282

83-
<div className="flex flex-col gap-4 rounded-xl border border-white/5 bg-white/[0.02] p-4 transition-colors hover:border-white/10 sm:flex-row sm:items-center sm:gap-6">
83+
<div className="flex flex-col gap-4 rounded-xl border border-white/5 bg-white/[0.02] p-4 transition-colors hover:border-white/10 sm:flex-row sm:items-center sm:gap-8">
8484
{/* Layer Title */}
85-
<div className="sm:w-32 flex-shrink-0">
85+
<div className="sm:w-56 flex-shrink-0">
8686
<h3 className="text-white font-semibold">{layer.title}</h3>
87-
<p className="text-xs text-gray-500">{layer.description}</p>
87+
<p className="text-xs text-gray-500 mt-0.5">{layer.description}</p>
8888
</div>
8989

9090
{/* Tech Items */}
91-
<div className="flex flex-wrap gap-3">
91+
<div className="flex flex-1 flex-wrap gap-3">
9292
{layer.items.map((item) => (
9393
<div
9494
key={item.name}
95-
className="flex items-center gap-2 px-3 py-2 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10 transition-colors"
95+
className="flex items-center gap-2 px-4 py-2.5 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10 transition-colors"
9696
>
9797
<item.icon className="w-5 h-5" style={{ color: item.color }} />
9898
<span className="text-sm text-gray-300">{item.name}</span>
@@ -114,7 +114,7 @@ export default function TechStackSection() {
114114
</div>
115115

116116
{/* Browser APIs */}
117-
<div className="grid gap-8 lg:grid-cols-2 lg:items-start [&>*]:min-w-0">
117+
<div className="landing-center max-w-5xl grid gap-8 lg:grid-cols-2 lg:items-start [&>*]:min-w-0">
118118
<div>
119119
<h3 className="text-xl font-semibold text-white mb-6">
120120
浏览器原生 API

src/index.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,24 @@
5252
scroll-behavior: smooth;
5353
scroll-padding-top: 5rem;
5454
background-color: #000;
55+
overflow-x: hidden;
56+
overflow-x: clip;
5557
}
5658

5759
html,
5860
body,
5961
#root {
6062
width: 100%;
63+
max-width: 100%;
6164
}
6265

6366
body {
6467
margin: 0;
6568
min-height: 100vh;
6669
width: 100%;
70+
max-width: 100vw;
6771
overflow-x: hidden;
72+
overflow-x: clip;
6873
background-color: #000;
6974
color: #fff;
7075
}
@@ -74,6 +79,7 @@
7479
min-height: 100dvh;
7580
max-width: 100vw;
7681
overflow-x: hidden;
82+
overflow-x: clip;
7783
isolation: isolate;
7884
}
7985

@@ -112,6 +118,30 @@
112118
}
113119

114120
@layer components {
121+
.landing-shell {
122+
width: 100%;
123+
max-width: 80rem;
124+
margin-inline: auto;
125+
padding-inline: 1rem;
126+
}
127+
128+
@media (min-width: 640px) {
129+
.landing-shell {
130+
padding-inline: 1.5rem;
131+
}
132+
}
133+
134+
@media (min-width: 1024px) {
135+
.landing-shell {
136+
padding-inline: 2rem;
137+
}
138+
}
139+
140+
.landing-center {
141+
width: 100%;
142+
margin-inline: auto;
143+
}
144+
115145
.custom-scrollbar::-webkit-scrollbar {
116146
width: 4px;
117147
}

src/main.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ if (import.meta.env.DEV) {
2222
// HashRouter 使用 hash 来管理路由
2323
// 构建目标 hash 路径
2424
const targetHash = path || '/';
25+
const allowedRoutes = new Set(['/', '/app', '/advanced', '/digital-human']);
2526

2627
// 设置 hash 路由
27-
if (targetHash !== '/') {
28+
if (targetHash !== '/' && allowedRoutes.has(targetHash)) {
2829
window.location.hash = targetHash;
2930
}
3031

0 commit comments

Comments
 (0)