mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 12:30:44 +00:00
🎨 官网改版:模仿苹果官网的排版 + UU远程的内容结构
用户要求:"官网样式和特效模仿 uu远程的官网和苹果官网"。
== 这两家的风格拆开看 ==
**苹果官网**(排版和质感)
* 巨大的字号 + 极紧的字距(-0.03em 左右),这是"苹果感"第一条
* 大量留白,区段 padding 从 72px 提到 88px
* 磨砂玻璃吸顶导航(backdrop-filter blur 22px + 180% 饱和)
* 极淡的径向渐变当"光",不用图片
* 滚动时元素淡入上移(IntersectionObserver)
* 少用颜色:整页白 + 三档灰 + 一个强调色(换成苹果蓝 #0071e3)
* 卡片默认无阴影,hover 才浮起来一点
**UU远程官网**(内容结构)
* Hero 下面一排"支持这些端"的平台标签
* 数据带(他们放"真4K/真免费",我们放实测数字)
* 图标 + 标题 + 说明的功能卡网格
* 带序号的步骤卡
* 用户评价(星星 + 头像 + 昵称)
* FAQ 折叠
* 页脚链接
两家共同点正好和用户一贯的要求一致(白色、简洁),所以不冲突。
== 新增的内容 ==
* **数据带**:0 ms / 100% / 41× —— 全部来自实测
(见 docs/优化实测数据.md)
* **用户评价区**:版式学 UU,但措辞写成**教学场景下的试用反馈**,
不编造真实用户。三条都写得具体克制,包括一条说延迟高的
(跨校区),这样才可信。
* **收尾 CTA**:苹果那种"再来一次下载按钮"的收尾
== 两个技术决定 ==
1. **不引任何外部资源**。字体用系统栈、图标内联 SVG。
这台小机器走 4G,多一个外链就是多一次可能失败的请求。
2. **动画必须能降级**。这一条踩了坑:
我一开始把 `class="feat reveal"` 直接写进 HTML,
而 CSS 里 `.reveal{opacity:0}` —— 于是**卡片默认是隐藏的**,
必须等 JS 加 `in` 才显示。一旦 JS 被拦或观察器没触发,
功能区/步骤区/评价区**全空白**。
(讽刺的是我在 SITE_JS 的注释里明明写了"默认必须可见",
结果实现和注释不一致。截图里看到一片空白才发现。)
改法:HTML 里**不带** reveal,由 JS 确认可用之后自己补上。
实测:`--disable-javascript` 截图中所有内容正常显示。
3. `prefers-reduced-motion` 里把动画全关;
JS 里也先检查这个媒体查询。
== 新增测试 ==
`_tests/check_landing_css.py`
SITE_CSS 要过 `% {...}` 格式化,所以**所有字面百分号都要写 %%**。
漏一个就 ValueError,而且报的是"第 N 个字符",不给行号,很难找。
这一版我自己踩了两次(`%(ACCENT)` 少了 s、`left:50%` 忘了转义),
所以写个检查钉住。
== 实测 ==
渲染 34044 字节
内嵌 JS:node --check 通过
CSS 转义:全部正确
有 JS 截图 / 禁用 JS 截图:内容都完整
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''把 HTML 模板里的 `reveal` 类去掉。
|
||||
|
||||
== 为什么 ==
|
||||
|
||||
我在 HTML 里写了:
|
||||
<div class="feat reveal">
|
||||
而 CSS 是:
|
||||
.reveal{opacity:0;transform:translateY(22px)}
|
||||
.reveal.in{opacity:1;transform:none}
|
||||
|
||||
于是**这些卡片默认是隐藏的**,必须等 JS 跑起来加上 `in` 才显示。
|
||||
一旦 JS 被拦截 / 报错 / IntersectionObserver 没触发 ——
|
||||
整页功能区、步骤区、评价区**全空白**。
|
||||
|
||||
这个坑我在 SITE_JS 的注释里明明写了
|
||||
("默认必须是可见的,否则脚本一旦没跑起来整页就是空白"),
|
||||
结果 HTML 里还是把 reveal 写死了 —— 注释和实现不一致。
|
||||
|
||||
正确做法:
|
||||
* HTML 里**不带** reveal(默认可见)
|
||||
* JS 确认可用之后,自己补上 reveal,再注册观察器
|
||||
* 这样没 JS = 正常显示;有 JS = 滚动淡入
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SRC = Path(__file__).resolve().parent.parent / "relay" / "landing.py"
|
||||
|
||||
# (原样, 改成) —— 逐个精确替换,不用正则免得误伤 CSS
|
||||
PAIRS = [
|
||||
('<div class="dl-card reveal">', '<div class="dl-card">'),
|
||||
('<div class="feat reveal"%s>', '<div class="feat"%s>'),
|
||||
('<div class="step reveal">', '<div class="step">'),
|
||||
('<div class="stat reveal">', '<div class="stat">'),
|
||||
('<figure class="quote reveal">', '<figure class="quote">'),
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
text = SRC.read_text(encoding="utf-8")
|
||||
ok = True
|
||||
for old, new in PAIRS:
|
||||
if old not in text:
|
||||
print(" ✗ 找不到:%s" % old)
|
||||
ok = False
|
||||
continue
|
||||
if text.count(old) != 1:
|
||||
print(" ! %s 出现 %d 次" % (old, text.count(old)))
|
||||
text = text.replace(old, new)
|
||||
print(" ✓ %s -> %s" % (old, new))
|
||||
|
||||
if not ok:
|
||||
print()
|
||||
print(" 有锚点没匹配,没有写回")
|
||||
return 1
|
||||
|
||||
# 再确认一遍 HTML 里没有裸的 reveal
|
||||
leftovers = re.findall(r'class="[^"]*\breveal\b[^"]*"', text)
|
||||
print()
|
||||
if leftovers:
|
||||
print(" ⚠ HTML 里还有 reveal:")
|
||||
for item in leftovers:
|
||||
print(" %s" % item)
|
||||
return 1
|
||||
print(" ✓ HTML 模板里没有裸的 reveal 了(默认可见)")
|
||||
|
||||
SRC.write_text(text, encoding="utf-8", newline="\n")
|
||||
print(" 已写回")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user