design: 效果稿改为清淡简洁风格,T-200 完成

- play.html / list.html:从深色重金属改为浅暖纸白 + 墨色 + 单一柔和赭石强调,
  去掉颗粒/暗角/发光,UI 改用 Public Sans / Noto Sans SC;补齐焦点态、
  cursor、44px 触控、对比度、prefers-reduced-motion、aria-label
- README:设计语言更新为「清淡简洁 · Swiss/Minimal」
- 06-tasks:T-200 标记 DONE

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ila
2026-06-21 17:21:14 +08:00
co-authored by Claude Opus 4.8
parent d37b83011a
commit 39713eb8ac
4 changed files with 119 additions and 134 deletions
+5 -4
View File
@@ -7,11 +7,12 @@
| `play.html` | 播放页 | **逐句字幕同步**:当前句放大+琥珀高亮+左侧光条,已读句变暗;**点任意句跳转**;底部播放器(播/停、上/下一句、进度)。点 ▶ 可看自动逐句推进的效果 | | `play.html` | 播放页 | **逐句字幕同步**:当前句放大+琥珀高亮+左侧光条,已读句变暗;**点任意句跳转**;底部播放器(播/停、上/下一句、进度)。点 ▶ 可看自动逐句推进的效果 |
| `list.html` | 课程列表 | 26 集卡片:英文/中文标题、Act 幕状态(有音频=琥珀点 / 无音频=虚线)、进度环;顶部"继续学习"卡;ep18+ 显示"暂无音频"降级态 | | `list.html` | 课程列表 | 26 集卡片:英文/中文标题、Act 幕状态(有音频=琥珀点 / 无音频=虚线)、进度环;顶部"继续学习"卡;ep18+ 显示"暂无音频"降级态 |
## 设计语言(深夜电影感精听) ## 设计语言(清淡简洁 · Swiss/Minimal)
- **配色**:暖墨黑底 `#15120d` + 纸感米色 `#ece1cd` + **琥珀金单点强调** `#e6ad5c`;细微胶片颗粒 + 顶部暖光。 - **配色**:浅暖纸白底 `#f8f6f1` + 墨色文字 `#2a2724` + **单一柔和赭石强调** `#a4662b`;细发丝线分隔,无颗粒/暗角/发光。
- **字体**:英文句子用文学衬线 **Newsreader**(精听的主角);UI 用 **Hanken Grotesk**;中文用 **Noto Serif SC**。 - **字体**:英文句子用文学衬线 **Newsreader**(精听的主角,唯一的表现型元素);UI 用干净的 **Public Sans**;中文用 **Noto Sans SC**。
- **原则**:单一强调色、移动优先、内容(英文句子)为视觉主体;克制、不镀金。 - **原则**:大量留白、高对比、单一强调色、移动优先;当前句靠"墨色加深 + 字号 + 细赭石标记"区分,不靠重特效。
- **可访问性**:焦点可见态、`cursor-pointer`、44px 触控区、文字对比 ≥4.5:1、`prefers-reduced-motion` 降级、图标按钮带 `aria-label`。
> 颜色 / 字体 / 间距都用 CSS 变量(`:root`)集中定义,迁移进 go-app 时整段 `:root` + 组件样式可直接复用。 > 颜色 / 字体 / 间距都用 CSS 变量(`:root`)集中定义,迁移进 go-app 时整段 `:root` + 组件样式可直接复用。
+48 -55
View File
@@ -6,88 +6,81 @@
<title>课程列表 · 走遍美国精听</title> <title>课程列表 · 走遍美国精听</title>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600&family=Hanken+Grotesk:wght@400;500;600;700&family=Noto+Serif+SC:wght@400;500;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600&family=Public+Sans:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500&display=swap" rel="stylesheet">
<style> <style>
:root{ :root{
--ink:#15120d; --ink-2:#1d1812; --card:#221c14; --card-2:#271f16; --paper:#f8f6f1; --surface:#ffffff;
--paper:#ece1cd; --muted:#9b8d77; --faint:#5c513f; --ink:#2a2724; --ink-soft:#56514a; --muted:#938b7e; --faint:#c2bbae;
--amber:#e6ad5c; --amber-2:#f0c074; --amber-soft:rgba(230,173,92,.13); --hair:#e9e3d8; --hair-soft:#f0ebe1;
--line:rgba(236,225,205,.08); --accent:#a4662b; --accent-2:#c0833f; --accent-soft:#f1e8d9;
--serif:"Newsreader",Georgia,serif; --serif:"Newsreader",Georgia,serif;
--sans:"Hanken Grotesk",system-ui,sans-serif; --sans:"Public Sans",system-ui,sans-serif;
--han:"Noto Serif SC",serif; --han:"Noto Sans SC",sans-serif;
} }
*{margin:0;padding:0;box-sizing:border-box} *{margin:0;padding:0;box-sizing:border-box}
body{ body{
font-family:var(--sans); color:var(--paper); font-family:var(--sans); color:var(--ink); background:var(--paper);
background: display:flex; justify-content:center; min-height:100vh; -webkit-font-smoothing:antialiased;
radial-gradient(120% 70% at 50% -5%, rgba(230,173,92,.10), transparent 50%),
var(--ink);
display:flex; justify-content:center; min-height:100vh;
-webkit-font-smoothing:antialiased;
} }
body::after{ .phone{width:100%;max-width:440px;min-height:100vh;border-left:1px solid var(--hair);border-right:1px solid var(--hair);padding-bottom:30px;background:var(--paper)}
content:""; position:fixed; inset:0; pointer-events:none; z-index:50; opacity:.5; mix-blend-mode:overlay; :focus-visible{outline:2px solid var(--accent); outline-offset:3px; border-radius:8px}
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.4'/%3E%3C/svg%3E");
}
.phone{width:100%;max-width:440px;min-height:100vh;border-left:1px solid var(--line);border-right:1px solid var(--line);padding-bottom:30px}
/* header */ /* header */
.head{padding:30px 24px 18px} .head{padding:32px 24px 16px}
.brand{display:flex;align-items:baseline;justify-content:space-between} .brand{display:flex;align-items:baseline;justify-content:space-between}
.brand h1{font-family:var(--han);font-size:25px;font-weight:600;letter-spacing:.04em} .brand h1{font-family:var(--han);font-size:24px;font-weight:500;letter-spacing:.03em}
.brand .en{font-family:var(--serif);font-style:italic;font-size:13px;color:var(--amber);letter-spacing:.02em} .brand .en{font-family:var(--serif);font-style:italic;font-size:13px;color:var(--accent);letter-spacing:.01em}
.sub{font-size:11.5px;color:var(--muted);letter-spacing:.16em;text-transform:uppercase;margin-top:6px} .sub{font-size:11px;color:var(--muted);letter-spacing:.14em;text-transform:uppercase;margin-top:6px}
/* continue hero */ /* continue hero */
.hero{ .hero{
margin:18px 20px 6px;padding:18px 20px;border-radius:18px;position:relative;overflow:hidden; margin:18px 20px 4px;padding:20px;border-radius:16px;position:relative;
background:linear-gradient(135deg,var(--card-2),var(--card));border:1px solid var(--line); background:var(--surface);border:1px solid var(--hair);
} }
.hero::before{content:"";position:absolute;right:-40px;top:-40px;width:160px;height:160px;border-radius:50%; .hero .lab{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:600}
background:radial-gradient(closest-side,rgba(230,173,92,.18),transparent)} .hero .ti{font-family:var(--serif);font-size:22px;font-weight:600;margin-top:7px;color:var(--ink)}
.hero .lab{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--amber);font-weight:600}
.hero .ti{font-family:var(--serif);font-size:22px;font-weight:600;margin-top:7px}
.hero .cn{font-family:var(--han);font-size:12.5px;color:var(--muted);margin-top:2px} .hero .cn{font-family:var(--han);font-size:12.5px;color:var(--muted);margin-top:2px}
.hero .row{display:flex;align-items:center;gap:14px;margin-top:16px} .hero .row{display:flex;align-items:center;gap:14px;margin-top:18px}
.progress{flex:1;height:5px;border-radius:5px;background:rgba(0,0,0,.35);overflow:hidden} .progress{flex:1;height:6px;border-radius:6px;background:var(--hair);overflow:hidden}
.progress i{display:block;height:100%;width:38%;border-radius:5px;background:linear-gradient(90deg,var(--amber),var(--amber-2))} .progress i{display:block;height:100%;width:38%;border-radius:6px;background:var(--accent)}
.resume{flex:none;display:flex;align-items:center;gap:7px;background:var(--amber);color:#1a1206; .resume{flex:none;display:flex;align-items:center;gap:6px;background:var(--accent);color:#fff;
font-weight:700;font-size:13px;border:none;border-radius:30px;padding:9px 16px;cursor:pointer} font-weight:600;font-size:13px;border:none;border-radius:30px;padding:10px 18px;cursor:pointer;
.resume:hover{background:var(--amber-2)} min-height:44px;transition:.2s}
.resume:hover{background:var(--accent-2)}
/* section label */ /* section label */
.seclab{display:flex;align-items:center;gap:10px;padding:22px 24px 8px;color:var(--muted)} .seclab{display:flex;align-items:center;gap:12px;padding:24px 24px 6px;color:var(--muted)}
.seclab span{font-size:11px;letter-spacing:.18em;text-transform:uppercase} .seclab span{font-size:11px;letter-spacing:.14em;text-transform:uppercase}
.seclab .ln{flex:1;height:1px;background:var(--line)} .seclab .ln{flex:1;height:1px;background:var(--hair)}
/* episode list */ /* episode list */
.list{padding:4px 14px} .list{padding:4px 14px}
.ep{display:flex;gap:14px;align-items:center;padding:14px 12px;border-radius:14px;cursor:pointer; .ep{display:flex;gap:14px;align-items:center;padding:15px 12px;border-radius:12px;cursor:pointer;
transition:background .2s;position:relative} transition:background .2s;position:relative}
.ep:hover{background:var(--ink-2)} .ep:hover{background:var(--surface)}
.ep + .ep{border-top:1px solid var(--line)} .ep + .ep{border-top:1px solid var(--hair-soft)}
.num{font-family:var(--serif);font-size:20px;font-style:italic;color:var(--faint);width:30px;flex:none;text-align:center} .num{font-family:var(--serif);font-size:19px;font-style:italic;color:var(--faint);width:30px;flex:none;text-align:center}
.ep.cur .num{color:var(--amber)} .ep.cur .num{color:var(--accent)}
.body{flex:1;min-width:0} .body{flex:1;min-width:0}
.body .ti{font-family:var(--serif);font-size:18px;font-weight:500;line-height:1.2} .body .ti{font-family:var(--serif);font-size:18px;font-weight:500;line-height:1.25;color:var(--ink)}
.body .cn{font-family:var(--han);font-size:11.5px;color:var(--muted);margin-top:2px} .body .cn{font-family:var(--han);font-size:11.5px;color:var(--muted);margin-top:2px}
.acts{display:flex;gap:6px;margin-top:9px} .acts{display:flex;gap:6px;margin-top:10px}
.chip{font-size:10px;letter-spacing:.06em;color:var(--muted);border:1px solid var(--line); .chip{font-size:10px;letter-spacing:.04em;color:var(--ink-soft);border:1px solid var(--hair);
border-radius:6px;padding:3px 8px;display:flex;align-items:center;gap:5px} border-radius:6px;padding:3px 8px;display:flex;align-items:center;gap:5px;background:var(--surface)}
.chip.done{color:var(--amber);border-color:rgba(230,173,92,.4)} .chip.done{color:var(--accent);border-color:rgba(164,102,43,.35);background:var(--accent-soft)}
.chip.done::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--amber)} .chip.done::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent)}
.chip.noaudio{color:var(--faint);border-style:dashed} .chip.noaudio{color:var(--faint);border-style:dashed;background:transparent}
.ring{flex:none;width:34px;height:34px} .ring{flex:none;width:34px;height:34px}
.ring svg{transform:rotate(-90deg)} .ring svg{transform:rotate(-90deg)}
.ring .bg{stroke:var(--line)} .ring .fg{stroke:var(--amber);stroke-linecap:round} .ring .bg{stroke:var(--hair)} .ring .fg{stroke:var(--accent);stroke-linecap:round}
.ep.locked{opacity:.5} .ep.locked{opacity:.55}
.ep.locked .body .ti{font-style:normal} .ep.locked .body .ti{font-style:normal}
.badge{font-size:9.5px;letter-spacing:.1em;color:var(--faint);border:1px dashed var(--line); .badge{font-size:9.5px;letter-spacing:.08em;color:var(--muted);border:1px dashed var(--hair);
border-radius:20px;padding:4px 9px;flex:none} border-radius:20px;padding:5px 10px;flex:none}
.list .ep{animation:rise .5s both} .list .ep{animation:rise .45s both}
@keyframes rise{from{opacity:0;transform:translateY(8px)}} @keyframes rise{from{opacity:0;transform:translateY(6px)}}
@media (prefers-reduced-motion: reduce){*{animation:none!important;transition:none!important}}
</style> </style>
</head> </head>
<body> <body>
+65 -74
View File
@@ -6,126 +6,117 @@
<title>播放页 · 走遍美国精听</title> <title>播放页 · 走遍美国精听</title>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400;1,6..72,500&family=Hanken+Grotesk:wght@400;500;600;700&family=Noto+Serif+SC:wght@400;500;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400&family=Public+Sans:wght@400;500;600&family=Noto+Sans+SC:wght@400;500&display=swap" rel="stylesheet">
<style> <style>
:root{ :root{
--ink:#15120d; --ink-2:#1d1812; --card:#221c14; --paper:#f8f6f1; --surface:#ffffff;
--paper:#ece1cd; --muted:#9b8d77; --faint:#5c513f; --ink:#2a2724; --ink-soft:#56514a; --muted:#938b7e; --faint:#c2bbae;
--amber:#e6ad5c; --amber-2:#f0c074; --amber-soft:rgba(230,173,92,.13); --hair:#e9e3d8; --hair-soft:#f0ebe1;
--line:rgba(236,225,205,.08); --accent:#a4662b; --accent-2:#c0833f; --accent-soft:#f1e8d9;
--serif:"Newsreader",Georgia,serif; --serif:"Newsreader",Georgia,serif;
--sans:"Hanken Grotesk",system-ui,sans-serif; --sans:"Public Sans",system-ui,sans-serif;
--han:"Noto Serif SC",serif; --han:"Noto Sans SC",sans-serif;
} }
*{margin:0;padding:0;box-sizing:border-box} *{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%} html,body{height:100%}
body{ body{
font-family:var(--sans); color:var(--paper); font-family:var(--sans); color:var(--ink); background:var(--paper);
background:
radial-gradient(120% 80% at 50% -10%, rgba(230,173,92,.10), transparent 55%),
radial-gradient(140% 120% at 50% 120%, rgba(0,0,0,.5), transparent 60%),
var(--ink);
display:flex; justify-content:center; min-height:100%; display:flex; justify-content:center; min-height:100%;
-webkit-font-smoothing:antialiased; -webkit-font-smoothing:antialiased;
} }
/* film grain */
body::after{
content:""; position:fixed; inset:0; pointer-events:none; z-index:50; opacity:.5; mix-blend-mode:overlay;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.4'/%3E%3C/svg%3E");
}
.phone{ .phone{
width:100%; max-width:440px; min-height:100vh; position:relative; width:100%; max-width:440px; min-height:100vh; position:relative;
display:flex; flex-direction:column; display:flex; flex-direction:column; background:var(--paper);
background:linear-gradient(180deg, rgba(255,255,255,.015), transparent 30%); border-left:1px solid var(--hair); border-right:1px solid var(--hair);
border-left:1px solid var(--line); border-right:1px solid var(--line);
} }
:focus-visible{outline:2px solid var(--accent); outline-offset:3px; border-radius:6px}
/* ---------- top bar ---------- */ /* ---------- top bar ---------- */
.top{ .top{
position:sticky; top:0; z-index:20; padding:18px 20px 14px; position:sticky; top:0; z-index:20; padding:16px 20px;
display:flex; align-items:center; gap:14px; display:flex; align-items:center; gap:14px;
background:linear-gradient(180deg, var(--ink) 60%, transparent); background:rgba(248,246,241,.9); backdrop-filter:blur(8px);
border-bottom:1px solid var(--hair);
} }
.icon-btn{ .icon-btn{
width:38px;height:38px;border-radius:50%;border:1px solid var(--line); width:40px;height:40px;border-radius:11px;border:1px solid var(--hair);
background:var(--ink-2); color:var(--paper); display:grid;place-items:center; background:var(--surface); color:var(--ink-soft); display:grid;place-items:center;
cursor:pointer; transition:.2s; flex:none; cursor:pointer; transition:.2s; flex:none; font-size:18px;
} }
.icon-btn:hover{border-color:var(--amber); color:var(--amber)} .icon-btn:hover{border-color:var(--accent); color:var(--accent)}
.top-meta{flex:1; text-align:center; line-height:1.25} .top-meta{flex:1; text-align:center; line-height:1.3}
.top-meta .ep{font-size:11px; letter-spacing:.22em; text-transform:uppercase; color:var(--amber); font-weight:600} .top-meta .ep{font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--accent); font-weight:600}
.top-meta .ti{font-family:var(--serif); font-size:18px; font-weight:500} .top-meta .ti{font-family:var(--serif); font-size:18px; font-weight:500; color:var(--ink)}
.top-meta .cn{font-family:var(--han); font-size:11.5px; color:var(--muted); margin-top:1px} .top-meta .cn{font-family:var(--han); font-size:11.5px; color:var(--muted); margin-top:1px}
.tag{font-size:10px;letter-spacing:.14em;color:var(--muted);border:1px solid var(--line); .tag{font-size:10px;letter-spacing:.1em;color:var(--ink-soft);background:var(--accent-soft);
padding:5px 8px;border-radius:20px;flex:none} padding:5px 9px;border-radius:20px;flex:none;font-weight:500}
/* ---------- subtitle stream ---------- */ /* ---------- subtitle stream ---------- */
.stream{ .stream{
flex:1; overflow:hidden; position:relative; padding:8vh 26px 6vh; flex:1; overflow:hidden; position:relative; padding:9vh 28px 6vh;
-webkit-mask-image:linear-gradient(180deg,transparent,#000 16%,#000 82%,transparent); -webkit-mask-image:linear-gradient(180deg,transparent,#000 15%,#000 84%,transparent);
mask-image:linear-gradient(180deg,transparent,#000 16%,#000 82%,transparent); mask-image:linear-gradient(180deg,transparent,#000 15%,#000 84%,transparent);
} }
.line{ .line{
font-family:var(--serif); font-size:21px; line-height:1.45; letter-spacing:.005em; font-family:var(--serif); font-size:20px; line-height:1.5; letter-spacing:.005em;
color:var(--faint); padding:13px 0 13px 18px; position:relative; cursor:pointer; color:var(--faint); padding:13px 0 13px 18px; position:relative; cursor:pointer;
transition:color .35s ease, opacity .35s, transform .35s; opacity:.8; transition:color .25s ease, font-size .25s ease;
} }
.line:hover{color:var(--muted)} .line:hover{color:var(--muted)}
.line .t{ .line .t{
font-family:var(--sans); font-size:10.5px; color:var(--faint); letter-spacing:.08em; font-family:var(--sans); font-size:10.5px; color:var(--faint); letter-spacing:.06em;
opacity:0; transition:.25s; position:absolute; left:-2px; top:18px; opacity:0; transition:.2s; position:absolute; left:-1px; top:17px;
} }
.line:hover .t{opacity:.7} .line:hover .t{opacity:1}
/* active line */ /* active line */
.line.active{ .line.active{
color:var(--paper); font-size:25px; opacity:1; font-weight:500; color:var(--ink); font-size:24px; font-weight:500;
text-shadow:0 0 30px rgba(230,173,92,.18);
} }
.line.active::before{ .line.active::before{
content:""; position:absolute; left:0; top:50%; transform:translateY(-50%); content:""; position:absolute; left:0; top:50%; transform:translateY(-50%);
width:3px; height:62%; border-radius:3px; width:3px; height:58%; border-radius:3px; background:var(--accent);
background:linear-gradient(var(--amber),var(--amber-2));
box-shadow:0 0 14px rgba(230,173,92,.6);
} }
.line.done{color:var(--muted); opacity:.55} .line.done{color:var(--muted)}
/* ---------- player bar ---------- */ /* ---------- player bar ---------- */
.player{ .player{
position:sticky; bottom:0; z-index:20; padding:16px 22px 26px; position:sticky; bottom:0; z-index:20; padding:16px 22px 24px;
background:linear-gradient(0deg, var(--ink) 62%, transparent); background:var(--surface); border-top:1px solid var(--hair);
} }
.seek{display:flex;align-items:center;gap:10px;margin-bottom:16px} .seek{display:flex;align-items:center;gap:10px;margin-bottom:16px}
.seek .time{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums;letter-spacing:.04em} .seek .time{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums;letter-spacing:.03em}
.bar{flex:1;height:4px;border-radius:4px;background:var(--line);position:relative;cursor:pointer} .bar{flex:1;height:4px;border-radius:4px;background:var(--hair);position:relative;cursor:pointer}
.bar .fill{position:absolute;inset:0 60% 0 0;border-radius:4px; .bar .fill{position:absolute;inset:0 60% 0 0;border-radius:4px;background:var(--accent)}
background:linear-gradient(90deg,var(--amber),var(--amber-2))} .bar .knob{position:absolute;left:40%;top:50%;width:12px;height:12px;border-radius:50%;
.bar .knob{position:absolute;left:40%;top:50%;width:11px;height:11px;border-radius:50%; transform:translate(-50%,-50%);background:var(--accent);border:2px solid var(--surface);box-shadow:0 1px 3px rgba(0,0,0,.15)}
transform:translate(-50%,-50%);background:var(--amber-2);box-shadow:0 0 10px rgba(230,173,92,.7)}
.controls{display:flex;align-items:center;justify-content:center;gap:30px} .controls{display:flex;align-items:center;justify-content:center;gap:30px}
.ctl{background:none;border:none;color:var(--paper);cursor:pointer;display:grid;place-items:center; .ctl{background:none;border:none;color:var(--ink-soft);cursor:pointer;display:grid;place-items:center;
opacity:.85;transition:.2s} min-width:44px;min-height:44px;transition:.2s;font-size:18px}
.ctl:hover{opacity:1;color:var(--amber-2)} .ctl:hover{color:var(--accent)}
.ctl small{font-family:var(--sans);font-size:9px;letter-spacing:.12em;color:var(--muted); .ctl small{font-family:var(--sans);font-size:9px;letter-spacing:.08em;color:var(--muted);
display:block;text-align:center;margin-top:3px} display:block;text-align:center;margin-top:2px}
.play{ .play{
width:64px;height:64px;border-radius:50%;flex:none; width:62px;height:62px;border-radius:50%;flex:none;
background:radial-gradient(120% 120% at 30% 20%,var(--amber-2),var(--amber)); background:var(--accent); color:#fff;
color:#1a1206;display:grid;place-items:center;cursor:pointer;border:none; display:grid;place-items:center;cursor:pointer;border:none;
box-shadow:0 8px 26px rgba(230,173,92,.34), inset 0 1px 1px rgba(255,255,255,.4); box-shadow:0 4px 14px rgba(164,102,43,.28); transition:.15s;
transition:transform .15s;
} }
.play:active{transform:scale(.94)} .play:hover{background:var(--accent-2)}
.hint{text-align:center;font-size:10.5px;color:var(--faint);letter-spacing:.08em;margin-top:16px} .play:active{transform:scale(.95)}
.hint{text-align:center;font-size:10.5px;color:var(--muted);letter-spacing:.05em;margin-top:15px}
/* load-in */ /* load-in */
.line{animation:rise .6s both} .line{animation:rise .5s both}
@keyframes rise{from{opacity:0;transform:translateY(8px)}} @keyframes rise{from{opacity:0;transform:translateY(6px)}}
@media (prefers-reduced-motion: reduce){
*{animation:none!important; transition:none!important; scroll-behavior:auto!important}
}
</style> </style>
</head> </head>
<body> <body>
<div class="phone"> <div class="phone">
<header class="top"> <header class="top">
<button class="icon-btn" aria-label="返回">‹</button> <button class="icon-btn" aria-label="返回课程列表">‹</button>
<div class="top-meta"> <div class="top-meta">
<div class="ep">EP 1 · Act I</div> <div class="ep">EP 1 · Act I</div>
<div class="ti">46 Linden Street</div> <div class="ti">46 Linden Street</div>
@@ -139,15 +130,15 @@
<footer class="player"> <footer class="player">
<div class="seek"> <div class="seek">
<span class="time" id="cur">0:41</span> <span class="time" id="cur">0:41</span>
<div class="bar" id="bar"><div class="fill" id="fill"></div><div class="knob" id="knob"></div></div> <div class="bar" id="bar" role="slider" aria-label="播放进度"><div class="fill" id="fill"></div><div class="knob" id="knob"></div></div>
<span class="time">3:42</span> <span class="time">3:42</span>
</div> </div>
<div class="controls"> <div class="controls">
<button class="ctl" id="prev" title="上一句">⏮<small>上一句</small></button> <button class="ctl" id="prev" aria-label="上一句">⏮<small>上一句</small></button>
<button class="play" id="play" aria-label="播放/暂停"> <button class="play" id="play" aria-label="播放或暂停">
<svg id="ico" width="22" height="22" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg> <svg id="ico" width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>
</button> </button>
<button class="ctl" id="next" title="下一句">⏭<small>下一句</small></button> <button class="ctl" id="next" aria-label="下一句">⏭<small>下一句</small></button>
</div> </div>
<div class="hint">点击任意一句 · 音频跳到该句重听</div> <div class="hint">点击任意一句 · 音频跳到该句重听</div>
</footer> </footer>
@@ -175,7 +166,7 @@ function render(){
LINES.forEach((l,i)=>{ LINES.forEach((l,i)=>{
const d = document.createElement('div'); const d = document.createElement('div');
d.className = 'line' + (i===active?' active':'') + (i<active?' done':''); d.className = 'line' + (i===active?' active':'') + (i<active?' done':'');
d.style.animationDelay = (i*0.05)+'s'; d.style.animationDelay = (i*0.04)+'s';
d.innerHTML = `<span class="t">${l.t.toFixed(1)}s</span>${l.en}`; d.innerHTML = `<span class="t">${l.t.toFixed(1)}s</span>${l.en}`;
d.onclick = ()=>{ active=i; sync(); }; // 点句跳转 d.onclick = ()=>{ active=i; sync(); }; // 点句跳转
stream.appendChild(d); stream.appendChild(d);
+1 -1
View File
@@ -47,7 +47,7 @@
| ID | 任务 | 依赖 | 验收要点 | 状态 | | ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| T-200 | UI 效果稿:播放页 + 课程列表(HTML/CSS) | — | 中保真稿覆盖逐句高亮/点句跳转/进度/无音频降级;用真实数据;CSS 可迁移 go-app。见 `design_mockups/` | DOING | | T-200 | UI 效果稿:播放页 + 课程列表(HTML/CSS) | — | 中保真稿覆盖逐句高亮/点句跳转/进度/无音频降级;用真实数据;CSS 可迁移 go-app。见 `design_mockups/` | DONE |
> 设计 spike,不是生产代码。产出在 `design_mockups/play.html`、`list.html`,作为 T-201/T-203 的视觉依据。 > 设计 spike,不是生产代码。产出在 `design_mockups/play.html`、`list.html`,作为 T-201/T-203 的视觉依据。