From 0ab0b04bbea0b476c4469098db18acbb7932cfea Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Thu, 12 Nov 2026 17:57:57 +0800 Subject: [PATCH] refactor(ymb): polish responsive youtube interactions --- ...026-11-12-premium-ui-interaction-polish.md | 48 +++++++++++++++++ src/shared/styles/youtube-overrides.css | 53 +++++++++++++------ 2 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 docs/plans/2026-11-12-premium-ui-interaction-polish.md diff --git a/docs/plans/2026-11-12-premium-ui-interaction-polish.md b/docs/plans/2026-11-12-premium-ui-interaction-polish.md new file mode 100644 index 0000000..2ccbd78 --- /dev/null +++ b/docs/plans/2026-11-12-premium-ui-interaction-polish.md @@ -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` 与本计划文件;未改后端、未引入底部导航、未发送完成通知。 diff --git a/src/shared/styles/youtube-overrides.css b/src/shared/styles/youtube-overrides.css index 5c2d0b1..9477ef8 100644 --- a/src/shared/styles/youtube-overrides.css +++ b/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; }