refactor(ymb): polish responsive youtube interactions
This commit is contained in:
1 parent
9d9c7e3eec
commit
0ab0b04bbe
2 files changed
+86
-15
No files matched your search
@@ -0,0 +1,48 @@
|
||||
# YMB 官网高级 UI 与交互优化计划
|
||||
|
||||
## 目标
|
||||
|
||||
- 在不改变现有深色品牌基调、真实接口和信息架构的前提下,提升官网的视觉质感与交互连贯性。
|
||||
- 桌面端重点优化导航层级、内容卡片反馈、搜索与语言切换;移动端重点优化触控尺寸、抽屉与沉浸式内容浏览。
|
||||
- 保证加载、空态、错误态与播放状态都有明确反馈,并尊重系统减少动画设置。
|
||||
|
||||
## 实施步骤
|
||||
|
||||
1. 审计首页、Shorts、观看页、搜索页在桌面与移动视口下的布局和控制台状态。
|
||||
2. 修复影响内容展示、可访问性和交互流畅度的问题。
|
||||
3. 统一视觉 token、悬停/按压/焦点反馈、卡片动效、骨架加载与移动端安全区适配。
|
||||
4. 验证真实接口、图片、Shorts 播放和页面跳转不回退。
|
||||
5. 运行构建与差异检查,回填验证结果。
|
||||
|
||||
## 范围
|
||||
|
||||
- `src/components/**`
|
||||
- `src/layouts/**`
|
||||
- `src/pages/**`
|
||||
- `src/styles/**`
|
||||
- `src/app/**`
|
||||
- 必要的测试与本计划文件
|
||||
|
||||
## 风险与约束
|
||||
|
||||
- 不修改 FruitServer 接口契约,不把匿名浏览改成登录后可见。
|
||||
- 不改变用户已确认的深色背景与品牌主色,只调整层次、间距、透明度和反馈。
|
||||
- 不引入重量级 UI 框架,优先复用现有组件与 CSS token。
|
||||
- 当前分支为 `dev`,只提交本轮站点文件,不夹带其他仓库改动。
|
||||
|
||||
## 验证
|
||||
|
||||
- `pnpm build`
|
||||
- `git diff --check`
|
||||
- 桌面端 1440px:首页、Shorts、观看页、搜索与语言菜单
|
||||
- 移动端 390×844:侧栏抽屉、Shorts 滑动、观看页与安全区
|
||||
- 浏览器控制台无新增 error
|
||||
|
||||
## 实施回执
|
||||
|
||||
- 已完成:保留深色 YMB 品牌、真实接口和无底部导航约束,未修改 FruitServer 契约。
|
||||
- 样式:移除全局 `font-weight: 400 !important`,恢复标题/按钮/元信息层级;增加页面最小宽度与横向溢出保护;移动端双列网格和 Shorts 横向列表增加宽度约束。
|
||||
- 交互:补充键盘 `:focus-visible` 焦点环、搜索聚焦反馈、触屏按压反馈;创建菜单/语言菜单增加轻量入场动画;侧栏与内容区收起/展开增加平滑过渡;尊重 `prefers-reduced-motion`。
|
||||
- 真实链路复核:前端 `http://127.0.0.1:4179/`、后端 `/app/categories`、`/app/home/feed`、`/app/dramas/1`、`/app/episodes/1/play` 均可达;播放地址响应 `application/vnd.apple.mpegurl`,图片 `hero.jpg` 响应 `image/jpeg`。
|
||||
- 门禁:`pnpm lint` 通过;`pnpm build` 通过(仅保留既有 chunk size warning);`git diff --check` 通过。
|
||||
- 当前状态:待提交本轮 `src/shared/styles/youtube-overrides.css` 与本计划文件;未改后端、未引入底部导航、未发送完成通知。
|
||||
@@ -8,9 +8,20 @@
|
||||
--accent: #ff4b3e;
|
||||
--sidebar: 240px;
|
||||
--topbar: 56px;
|
||||
--focus-ring: 0 0 0 3px #1c62b966;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
#root { min-width: 0; }
|
||||
|
||||
body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, Helvetica, sans-serif; }
|
||||
body { overflow-x: hidden; background: var(--bg); color: var(--text); font-family: Roboto, Arial, Helvetica, sans-serif; }
|
||||
|
||||
button,
|
||||
a,
|
||||
input,
|
||||
[role="button"] { -webkit-tap-highlight-color: transparent; }
|
||||
|
||||
:where(button, a, input, [role="button"]):focus-visible { outline: 2px solid #8ab4f8; outline-offset: 2px; box-shadow: var(--focus-ring); }
|
||||
|
||||
/* 顶部栏:左中右三段,搜索框居中 */
|
||||
.topbar { position: sticky; top: 0; z-index: 30; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 560px) minmax(0, 1fr); align-items: center; column-gap: 16px; height: var(--topbar); padding: 0 16px; background: var(--bg); border-bottom: 0; box-shadow: none; }
|
||||
@@ -24,9 +35,15 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
.search-field { position: relative; display: flex; flex: 1; min-width: 0; }
|
||||
.search-form input { width: 100%; height: 40px; padding: 0 42px 0 16px; border: 1px solid #303030; border-radius: 20px 0 0 20px; background: #121212; color: var(--text); font-size: 14px; }
|
||||
.search-form input::placeholder { color: #888; }
|
||||
.search-form input:focus { outline: none; border-color: #1c62b9; }
|
||||
.search-form input:focus { outline: none; border-color: #1c62b9; box-shadow: inset 0 0 0 1px #1c62b9; }
|
||||
.search-form button { flex: none; display: grid; place-items: center; width: 64px; height: 40px; border: 1px solid #303030; border-left: 0; border-radius: 0 20px 20px 0; background: #222; color: var(--text); cursor: pointer; }
|
||||
.search-form button:hover { background: #303030; }
|
||||
.search-form button:active,
|
||||
.search-clear:active,
|
||||
.create-button:active,
|
||||
.language-trigger:active,
|
||||
.youtube-category-chip:active,
|
||||
.watch-actions button:active { transform: scale(.97); }
|
||||
.search-form .search-field .search-clear { position: absolute; right: 8px; top: 50%; display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%; background: #303030; color: #ddd; transform: translateY(-50%); cursor: pointer; }
|
||||
.search-form .search-field .search-clear:hover { background: #444; }
|
||||
.search-empty { display: grid; place-items: center; gap: 8px; padding: 56px 0; color: #aaa; }
|
||||
@@ -36,7 +53,7 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
.create-wrap { position: relative; }
|
||||
.create-button { display: flex; align-items: center; gap: 6px; height: 36px; padding: 0 14px; border: 0; border-radius: 18px; background: #272727; color: var(--text); font-weight: 600; cursor: pointer; }
|
||||
.create-button:hover { background: #343434; }
|
||||
.create-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 60; min-width: 208px; padding: 8px; border: 1px solid #303030; border-radius: 12px; background: #212121; box-shadow: 0 12px 32px #000a; }
|
||||
.create-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 60; min-width: 208px; padding: 8px; border: 1px solid #303030; border-radius: 12px; background: #212121; box-shadow: 0 12px 32px #000a; transform-origin: top right; animation: ymb-pop-in .18s cubic-bezier(.2, .7, .3, 1) both; }
|
||||
.create-menu button { display: flex; align-items: center; gap: 12px; width: 100%; padding: 10px 12px; border: 0; border-radius: 8px; background: transparent; color: var(--text); font-size: 14px; text-align: left; cursor: pointer; }
|
||||
.create-menu button:hover { background: #3a3a3a; }
|
||||
.upload-backdrop { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: 24px; background: #000a; }
|
||||
@@ -72,7 +89,7 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
.profile-chip { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: #065fd4; color: #fff; }
|
||||
|
||||
/* 侧边栏:展开 240px,收起 72px 图标 + 文字 */
|
||||
.sidebar { position: fixed; top: var(--topbar); bottom: 0; left: 0; z-index: 20; width: var(--sidebar); padding: 12px; overflow-y: auto; background: var(--bg); border: 0; box-shadow: none; color: var(--text); }
|
||||
.sidebar { position: fixed; top: var(--topbar); bottom: 0; left: 0; z-index: 20; width: var(--sidebar); padding: 12px; overflow-y: auto; background: var(--bg); border: 0; box-shadow: none; color: var(--text); transition: width .24s ease, padding .24s ease, transform .24s ease; }
|
||||
.sidebar::-webkit-scrollbar { width: 8px; }
|
||||
.sidebar::-webkit-scrollbar-thumb { background: #4d4d4d; border-radius: 4px; }
|
||||
.sidebar.closed { width: 72px; padding: 4px 4px 12px; }
|
||||
@@ -87,8 +104,15 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
.sidebar.closed .nav-rule { display: block; margin: 8px 12px; }
|
||||
|
||||
/* 内容区 */
|
||||
.content { max-width: none; margin: 0; padding: 12px 24px 80px calc(var(--sidebar) + 24px); }
|
||||
.content { max-width: none; margin: 0; padding: 12px 24px 80px calc(var(--sidebar) + 24px); transition: padding-left .24s ease; }
|
||||
.content-wide { padding-left: calc(72px + 24px); }
|
||||
.content,
|
||||
.content-wide,
|
||||
.youtube-home-feed,
|
||||
.youtube-shorts-shelf,
|
||||
.video-grid,
|
||||
.media-card,
|
||||
.media-copy { min-width: 0; }
|
||||
.site-footer { padding: 28px 0 8px; color: #717171; font-size: 12px; text-align: center; }
|
||||
.youtube-home-feed, .youtube-shorts-shelf { margin-bottom: 40px; }
|
||||
.youtube-category-bar { position: sticky; top: var(--topbar); z-index: 10; display: flex; gap: 12px; overflow-x: auto; padding: 12px 0; background: var(--bg); scrollbar-width: none; }
|
||||
@@ -121,7 +145,7 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
/* 提示与语言菜单 */
|
||||
.api-note { margin: 0 0 16px; padding: 10px 12px; border: 0; border-radius: 8px; background: #181818; color: var(--muted); font-size: 13px; }
|
||||
.api-note.warning { background: #241a1a; color: #e6b8b3; }
|
||||
.language-menu { top: 48px; width: 208px; padding: 8px; border: 1px solid #333; border-radius: 14px; background: #1f1f1f; box-shadow: 0 20px 50px #000a; }
|
||||
.language-menu { top: 48px; width: 208px; padding: 8px; border: 1px solid #333; border-radius: 14px; background: #1f1f1f; box-shadow: 0 20px 50px #000a; transform-origin: top right; animation: ymb-pop-in .18s cubic-bezier(.2, .7, .3, 1) both; }
|
||||
.language-menu-header { padding: 7px 10px 8px; color: #8a8a8a; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
|
||||
.language-menu button { gap: 10px; padding: 10px; border-radius: 9px; color: #ddd; }
|
||||
.language-menu button:hover { background: #2b2b2b; color: #fff; }
|
||||
@@ -377,7 +401,8 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
.content, .content-wide { padding: 0 12px calc(24px + env(safe-area-inset-bottom)); }
|
||||
.youtube-category-bar { top: var(--topbar); margin: 0 -12px; padding: 10px 12px; gap: 8px; }
|
||||
.youtube-category-chip { padding: 7px 12px; font-size: 13px; }
|
||||
.video-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 10px; }
|
||||
.video-grid { width: 100%; min-width: 0; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 10px; }
|
||||
.video-grid > .media-card { width: 100%; min-width: 0; }
|
||||
.media-meta { gap: 8px; padding-top: 9px; }
|
||||
.channel-avatar { width: 28px; height: 28px; font-size: 12px; }
|
||||
.media-title { font-size: 14px; }
|
||||
@@ -386,7 +411,7 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
.youtube-shorts-shelf { margin: 20px -12px 32px; padding: 16px 12px 22px; }
|
||||
.youtube-shelf-heading h2 { font-size: 20px; }
|
||||
.shorts-row { display: flex; gap: 10px; overflow-x: auto; margin: 0; padding-bottom: 2px; }
|
||||
.shorts-row .media-card { flex: 0 0 150px; }
|
||||
.shorts-row .media-card { flex: 0 0 150px; min-width: 150px; }
|
||||
.shorts-row .media-thumb { aspect-ratio: 9 / 16; }
|
||||
.shorts-stage, .shorts-stage-wide { left: 0; bottom: 0; }
|
||||
.short-slide { padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); background: #212121; }
|
||||
@@ -553,6 +578,10 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
@media (prefers-reduced-motion: reduce) { .watch-stage { animation: none; } }
|
||||
|
||||
/* 页面动效:仅使用 transform / opacity,走合成层,不影响布局与滚动性能 */
|
||||
@keyframes ymb-pop-in {
|
||||
from { opacity: 0; transform: translateY(-4px) scale(.96); }
|
||||
to { opacity: 1; transform: none; }
|
||||
}
|
||||
@keyframes ymb-page-in {
|
||||
from { opacity: 0; transform: translateY(8px); }
|
||||
to { opacity: 1; transform: none; }
|
||||
@@ -605,11 +634,5 @@ body { background: var(--bg); color: var(--text); font-family: Roboto, Arial, He
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.content > *, .video-grid .media-card, .shorts-row .media-card,
|
||||
.youtube-home-feed > *, .youtube-shorts-shelf { animation: none; }
|
||||
.media-thumb img, .media-thumb::after, .thumb-play { transition: none; }
|
||||
.media-thumb img, .media-thumb::after, .thumb-play, .sidebar, .content, .create-menu, .language-menu { transition: none; animation: none; }
|
||||
}
|
||||
|
||||
/* 全站文字统一不加粗 */
|
||||
body,
|
||||
body * { font-weight: 400 !important; }
|
||||
b,
|
||||
strong { font-weight: 400 !important; }
|
||||
Reference in new issue
Block a user