style: redesign today board as paper note
ci / gitleaks (push) Successful in 10s
ci / docker (push) Successful in 8m12s

This commit is contained in:
2026-09-17 14:55:13 +08:00
parent cb7d18e192
commit ea2dc12ec1
4 changed files with 41 additions and 22 deletions
+12 -9
View File
@@ -1537,16 +1537,19 @@ onUnmounted(() => {
<MemoPanel v-else-if="activeView==='memos'" ref="memoPanel" :request="api" @scope="memoTrash=$event==='trash'" @detail="memoDetailOpen=$event" @notice="toast" /> <MemoPanel v-else-if="activeView==='memos'" ref="memoPanel" :request="api" @scope="memoTrash=$event==='trash'" @detail="memoDetailOpen=$event" @notice="toast" />
<CountdownPanel ref="countdownComposer" v-else-if="activeView==='countdowns'" @notice="toast" /> <CountdownPanel ref="countdownComposer" v-else-if="activeView==='countdowns'" @notice="toast" />
<template v-else> <template v-else>
<section v-if="activeView==='today'" class="today-board" aria-label="今日进度"> <section v-if="activeView==='today'" class="today-board" aria-labelledby="today-board-title">
<h2 id="today-board-title" class="sr-only">今日纸笺</h2>
<TodayEnvironmentStrip :environment="todayEnvironment" :loading="todayEnvironmentLoading" :failed="todayEnvironmentError" /> <TodayEnvironmentStrip :environment="todayEnvironment" :loading="todayEnvironmentLoading" :failed="todayEnvironmentError" />
<button class="today-track today-task-track" type="button" aria-controls="today-tasks" @click="navigateTodaySection('tasks')"> <div class="today-board__progress" aria-label="今日任务与习惯进度">
<span class="today-track-head"><strong>任务 {{ todayTaskCompleted }} / {{ todayTaskTotal }}</strong></span> <button class="today-track today-task-track" type="button" aria-controls="today-tasks" @click="navigateTodaySection('tasks')">
<span class="today-track-rail" role="progressbar" aria-label="今日任务进度" aria-valuemin="0" :aria-valuemax="todayTaskTotal" :aria-valuenow="todayTaskCompleted"><i class="today-track-fill" :style="{ width: todayTaskProgressPercent }" /></span> <span class="today-track-head"><strong>任务 {{ todayTaskCompleted }} / {{ todayTaskTotal }}</strong></span>
</button> <span class="today-track-rail" role="progressbar" aria-label="今日任务进度" aria-valuemin="0" :aria-valuemax="todayTaskTotal" :aria-valuenow="todayTaskCompleted"><i class="today-track-fill" :style="{ width: todayTaskProgressPercent }" /></span>
<button class="today-track today-habit-track" type="button" aria-controls="today-habits" @click="navigateTodaySection('habits')"> </button>
<span class="today-track-head"><strong>习惯 {{ todayHabitProgress }}</strong></span> <button class="today-track today-habit-track" type="button" aria-controls="today-habits" @click="navigateTodaySection('habits')">
<span class="today-track-rail" role="progressbar" aria-label="今日习惯进度" aria-valuemin="0" :aria-valuemax="todayHabitTotal" :aria-valuenow="todayHabitCompleted"><i class="today-track-fill" :style="{ width: todayHabitProgressPercent }" /></span> <span class="today-track-head"><strong>习惯 {{ todayHabitProgress }}</strong></span>
</button> <span class="today-track-rail" role="progressbar" aria-label="今日习惯进度" aria-valuemin="0" :aria-valuemax="todayHabitTotal" :aria-valuenow="todayHabitCompleted"><i class="today-track-fill" :style="{ width: todayHabitProgressPercent }" /></span>
</button>
</div>
</section> </section>
<template v-if="activeView==='today'"> <template v-if="activeView==='today'">
<section v-if="overdueTaskTree.length" class="overdue-section today-collapsible-section"> <section v-if="overdueTaskTree.length" class="overdue-section today-collapsible-section">
+21 -5
View File
@@ -39,17 +39,33 @@ describe('Today environment integration', () => {
}) })
it('keeps mobile environment copy to two rows including refresh states', () => { it('keeps mobile environment copy to two rows including refresh states', () => {
expect(css).toMatch(/@container\(max-width:559px\)\{\.today-environment\{[^}]*grid-template-rows:auto auto/) expect(css).toMatch(/@container\(max-width:559px\)\{\.today-environment\{[^}]*grid-template-rows:40px 66px/)
expect(css).toMatch(/\.today-environment__status\{[^}]*grid-column:auto;grid-row:auto/) expect(css).toMatch(/\.today-environment__status\{[^}]*grid-column:auto;grid-row:auto/)
expect(css).not.toContain('.today-environment__refreshing') expect(css).not.toContain('.today-environment__refreshing')
}) })
it('places one full-width information row inside the existing cream board', () => { it('renders方案 A as one labelled paper surface with a grouped progress region', () => {
expect(app.match(/class="today-board"/g)).toHaveLength(1) expect(app.match(/class="today-board"/g)).toHaveLength(1)
expect(app).toContain('<section v-if="activeView===\'today\'" class="today-board" aria-labelledby="today-board-title">')
expect(app).toContain('<h2 id="today-board-title" class="sr-only">今日纸笺</h2>')
expect(app).toContain('<div class="today-board__progress" aria-label="今日任务与习惯进度">')
expect(app).toMatch(/today-board__progress[\s\S]*today-task-track[\s\S]*today-habit-track[\s\S]*<\/div>/)
expect(css).toMatch(/\.today-board\{[^}]*grid-template-rows:76px 62px[^}]*gap:1px/)
expect(css).toMatch(/\.today-board__progress\{[^}]*min-height:62px[^}]*display:grid[^}]*grid-template-columns:repeat\(2,minmax\(0,1fr\)\)/)
expect(css).toContain('@media(max-width:559px){.today-board{grid-template-rows:106px 70px;padding:0 12px}.today-board__progress{min-height:70px}')
expect(css).not.toMatch(/\.today-(?:board|track)[^{]*\{[^}]*(?:gradient|backdrop-filter)/)
})
it('keeps the editorial environment layout readable at desktop and narrow widths', () => {
expect(css).toMatch(/\.today-environment\{[^}]*grid-column:1\/-1/) expect(css).toMatch(/\.today-environment\{[^}]*grid-column:1\/-1/)
expect(css).toMatch(/@container\(min-width:560px\)\{\.today-environment\{[^}]*flex-wrap:nowrap/) expect(css).toMatch(/@container\(min-width:560px\)\{\.today-environment\{[^}]*grid-template-columns:minmax\(0,1\.45fr\) minmax\(0,1fr\) minmax\(0,1fr\)/)
expect(css).toMatch(/@container\(max-width:559px\)\{\.today-environment\{[^}]*grid-template-columns:/) expect(css).toMatch(/\.today-environment__calendar strong\{[^}]*font-size:25px/)
expect(css).toMatch(/\.today-environment__item\{[^}]*min-width:0/) expect(css).toMatch(/@container\(max-width:559px\)\{\.today-environment\{[^}]*grid-template-columns:minmax\(0,1fr\) minmax\(0,1fr\)[^}]*grid-template-rows:40px 66px/)
expect(css).toMatch(/\.today-environment__calendar\{[^}]*grid-column:1\/-1[^}]*grid-row:1/)
expect(css).toMatch(/\.today-environment__weather\{[^}]*grid-column:1[^}]*grid-row:2/)
expect(css).toMatch(/\.today-environment__gold\{[^}]*grid-column:2[^}]*grid-row:2/)
expect(css).toMatch(/\.today-environment__weather,\.today-environment__gold\{[^}]*flex-direction:column/)
expect(css).not.toMatch(/\.today-environment[^}]*overflow-x:auto/) expect(css).not.toMatch(/\.today-environment[^}]*overflow-x:auto/)
expect(css).not.toMatch(/@container\(max-width:559px\)[\s\S]*?\.today-environment[^}]*text-overflow:ellipsis/)
}) })
}) })
@@ -126,23 +126,23 @@ describe('TodayEnvironmentStrip', () => {
it('uses the approved cream edit-line typography and separators', () => { it('uses the approved cream edit-line typography and separators', () => {
const css = readFileSync(resolve(process.cwd(), 'src/style.css'), 'utf8') const css = readFileSync(resolve(process.cwd(), 'src/style.css'), 'utf8')
expect(css).toContain('.today-environment{grid-column:1/-1;position:relative;min-width:0;min-height:40px;') expect(css).toContain('.today-environment{grid-column:1/-1;position:relative;min-width:0;min-height:76px;')
expect(css).toContain('color:#302a24;font-size:14px;font-weight:650') expect(css).toContain('color:#302a24;font-size:25px;line-height:1;font-weight:680')
expect(css).toContain('color:#655b50;font-size:12px;font-weight:450') expect(css).toContain('color:#655b50;font-size:12px;font-weight:450')
expect(css).toContain('color:#8b8275;font-size:11px;font-weight:400') expect(css).toContain('color:#8b8275;font-size:11px;font-weight:400')
expect(css).toContain('.today-environment:after{content:"";position:absolute;left:10px;right:10px;bottom:0;height:1px;background:#e4d5c0}') expect(css).toContain('.today-environment:after{content:"";position:absolute;left:10px;right:10px;bottom:0;height:1px;background:#e4d5c0}')
expect(css).toContain('.today-environment.has-attention:before{content:"";width:2px;height:14px;background:#f15a29') expect(css).toContain('.today-environment.has-attention:before{content:"";position:absolute;left:0;top:50%;width:2px;height:14px;background:#f15a29')
expect(css).not.toContain('border-left:1px solid var(--border-cream)') expect(css).not.toContain('border-left:1px solid var(--border-cream)')
expect(css).not.toMatch(/\.today-environment[^}]*gradient|\.today-environment[^}]*box-shadow|\.today-environment[^}]*backdrop-filter/) expect(css).not.toMatch(/\.today-environment[^}]*gradient|\.today-environment[^}]*box-shadow|\.today-environment[^}]*backdrop-filter/)
}) })
it('keeps mobile content to exactly two parent rows with stacked source columns', () => { it('keeps mobile content to exactly two parent rows with stacked source columns', () => {
const css = readFileSync(resolve(process.cwd(), 'src/style.css'), 'utf8') const css = readFileSync(resolve(process.cwd(), 'src/style.css'), 'utf8')
expect(css).toContain('@container(max-width:559px){.today-environment{min-height:68px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:auto auto;column-gap:14px;') expect(css).toContain('@container(max-width:559px){.today-environment{min-height:106px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:40px 66px;column-gap:14px;')
expect(css).toContain('.today-environment__calendar{grid-column:1/-1;grid-row:1;display:flex;flex-direction:row;') expect(css).toContain('.today-environment__calendar{grid-column:1/-1;grid-row:1;display:flex;flex-direction:row;')
expect(css).toContain('.today-environment__weather,.today-environment__gold{display:flex;flex-direction:column;align-items:flex-start;gap:2px;overflow:visible}') expect(css).toContain('.today-environment__weather,.today-environment__gold{display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:4px;overflow:visible;white-space:normal}')
expect(css).toContain('.today-environment__gold{grid-column:2;grid-row:2;padding-left:14px}') expect(css).toContain('.today-environment__gold{grid-column:2;grid-row:2;padding-left:14px}')
expect(css).toContain('.today-environment__gold:before{content:"";position:absolute;left:0;top:4px;width:1px;height:14px;background:#e4d5c0}') expect(css).toContain('.today-environment__gold:before{content:"";position:absolute;left:0;top:8px;width:5px;height:1px;background:#cbbda9}')
expect(css).toContain('.today-environment__calendar-main--desktop,.today-environment__gold-date--desktop{display:none}') expect(css).toContain('.today-environment__calendar-main--desktop,.today-environment__gold-date--desktop{display:none}')
expect(css).toContain('.today-environment__calendar-main--mobile,.today-environment__gold-date--mobile{display:inline}') expect(css).toContain('.today-environment__calendar-main--mobile,.today-environment__gold-date--mobile{display:inline}')
const mobileBlock = css.slice(css.indexOf('@container(max-width:559px){.today-environment'), css.indexOf('/* Solid cream material system')) const mobileBlock = css.slice(css.indexOf('@container(max-width:559px){.today-environment'), css.indexOf('/* Solid cream material system'))
File diff suppressed because one or more lines are too long