fix: compact mobile bottom navigation
ci / gitleaks (push) Successful in 8s
ci / docker (push) Successful in 3m40s

This commit is contained in:
2026-09-18 08:58:41 +08:00
parent 7bfc5d2a44
commit 4d5d1f46e0
5 changed files with 11 additions and 11 deletions
+3 -3
View File
@@ -38,7 +38,7 @@ test('bottom navigation uses the paper baseline geometry', async ({ page }) => {
}
})
expect(metrics.documentOverflow).toBe(0)
expect(metrics.nav).toEqual({ left: 0, right: metrics.viewportWidth, bottom: page.viewportSize()!.height, width: metrics.viewportWidth, height: 64 })
expect(metrics.nav).toEqual({ left: 0, right: metrics.viewportWidth, bottom: page.viewportSize()!.height, width: metrics.viewportWidth, height: 56 })
expect(Math.max(...metrics.buttonWidths) - Math.min(...metrics.buttonWidths)).toBeLessThanOrEqual(1)
expect(metrics.buttonHeights.every(height => height >= 44)).toBeTruthy()
expect(metrics.background).toBe('rgb(255, 253, 248)')
@@ -70,11 +70,11 @@ test('bottom navigation keeps its safe-area gap after dragging', async ({ page }
const nav = page.locator('.bottom')
await expect(nav).toBeVisible()
await page.locator('html').evaluate(element => element.style.setProperty('--safe-area-bottom', '34px', 'important'))
await expect.poll(() => nav.evaluate(element => getComputedStyle(element).height)).toBe('98px')
await expect.poll(() => nav.evaluate(element => getComputedStyle(element).height)).toBe('90px')
const navBox = await nav.boundingBox()
expect(navBox).not.toBeNull()
expect(navBox!.height).toBe(98)
expect(navBox!.height).toBe(90)
const fab = page.locator('.unified-fab')
const fabBox = await fab.boundingBox()
+4 -4
View File
@@ -202,10 +202,10 @@ describe('MVP view utilities', () => {
expect(clampFabPosition(120, 300, 390, 844)).toEqual({ x: 120, y: 300 })
})
it('reserves the paper navigation, safe area, and gap below a dragged add button', () => {
expect(fabBottomReserved(0)).toBe(76)
expect(fabBottomReserved(34)).toBe(110)
expect(clampFabPosition(500, 900, 390, 844, 56, 14, fabBottomReserved(34))).toEqual({ x: 320, y: 678 })
it('reserves the compact paper navigation, safe area, and gap below a dragged add button', () => {
expect(fabBottomReserved(0)).toBe(68)
expect(fabBottomReserved(34)).toBe(102)
expect(clampFabPosition(500, 900, 390, 844, 56, 14, fabBottomReserved(34))).toEqual({ x: 320, y: 686 })
})
it('snaps the add button to the nearest horizontal edge while preserving its safe vertical position', () => {
+1 -1
View File
@@ -190,7 +190,7 @@ export async function performTrashMutation(
return { mutated: true, refreshed: await refresh() }
}
export function fabBottomReserved(safeAreaBottom = 0, navigationHeight = 64, gap = 12) {
export function fabBottomReserved(safeAreaBottom = 0, navigationHeight = 56, gap = 12) {
return navigationHeight + Math.max(0, safeAreaBottom) + gap
}
+1 -1
View File
@@ -82,7 +82,7 @@ input,select,textarea,.search{background:var(--surface-raised);border-color:var(
.empty,.center{color:var(--muted)}.loader{background:var(--surface-raised)}
.toast{background:#3b342c;color:#fff;border:1px solid #574d42;border-radius:var(--radius-control);box-shadow:var(--shadow-raised)}.error-toast{background:var(--danger);color:#fff;border:1px solid #9f2f22;border-radius:var(--radius-control);box-shadow:var(--shadow-raised)}
:focus-visible{outline:3px solid var(--focus-ring);outline-offset:2px}.task-check:focus-visible,.archived-lists-toggle:focus-visible,.archived-row-menu-trigger:focus-visible,.archived-row-actions button:focus-visible{outline:3px solid var(--focus-ring);outline-offset:2px}
@media(max-width:930px){.bottom{left:0;right:0;bottom:0;height:calc(64px + var(--safe-area-bottom));display:grid;grid-template-columns:repeat(4,minmax(0,1fr));background:var(--surface-raised);border:0;border-top:1px solid var(--border-cream);border-radius:0;padding:4px 10px var(--safe-area-bottom);box-shadow:none}.bottom button{position:relative;min-width:0;min-height:44px;border-radius:0;padding:2px 4px;line-height:1.1}.bottom button svg{width:19px;height:19px}.bottom button.active{background:transparent;color:var(--accent)}.bottom button.active:before{content:"";position:absolute;left:23%;right:23%;top:-5px;height:3px;border-radius:0 0 3px 3px;background:var(--accent)}.sidebar{border-radius:0 var(--radius-panel) var(--radius-panel) 0}.detail,.app-sheet,.task-compose-sheet,.habit-detail-sheet,.countdown-detail-sheet{border-radius:var(--sheet-radius) var(--sheet-radius) 0 0!important}.task-list,.habit-list,.countdown-timeline{display:grid;gap:0}.task-row,.habit-row,.countdown-row{min-height:62px;background:var(--surface-raised);border:0;border-radius:0;box-shadow:none}.task-row+.task-row,.habit-row+.habit-row,.countdown-row+.countdown-row{border-top:1px solid var(--border-cream)}.countdown-row:first-of-type{border-top:0}.unified-fab{bottom:calc(76px + var(--safe-area-bottom))}}
@media(max-width:930px){.bottom{left:0;right:0;bottom:0;height:calc(56px + var(--safe-area-bottom));display:grid;grid-template-columns:repeat(4,minmax(0,1fr));background:var(--surface-raised);border:0;border-top:1px solid var(--border-cream);border-radius:0;padding:4px 10px var(--safe-area-bottom);box-shadow:none}.bottom button{position:relative;min-width:0;min-height:44px;border-radius:0;padding:2px 4px;line-height:1.1}.bottom button svg{width:19px;height:19px}.bottom button.active{background:transparent;color:var(--accent)}.bottom button.active:before{content:"";position:absolute;left:23%;right:23%;top:-5px;height:3px;border-radius:0 0 3px 3px;background:var(--accent)}.sidebar{border-radius:0 var(--radius-panel) var(--radius-panel) 0}.detail,.app-sheet,.task-compose-sheet,.habit-detail-sheet,.countdown-detail-sheet{border-radius:var(--sheet-radius) var(--sheet-radius) 0 0!important}.task-list,.habit-list,.countdown-timeline{display:grid;gap:0}.task-row,.habit-row,.countdown-row{min-height:62px;background:var(--surface-raised);border:0;border-radius:0;box-shadow:none}.task-row+.task-row,.habit-row+.habit-row,.countdown-row+.countdown-row{border-top:1px solid var(--border-cream)}.countdown-row:first-of-type{border-top:0}.unified-fab{bottom:calc(68px + var(--safe-area-bottom))}}
@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:.01ms!important;transition-duration:.01ms!important}.completed-filter-pill,.completed-filter-pill__track,.completed-filter-pill__thumb{transition:none!important}.topbar-refresh.spinning svg{animation:none!important}.completed-filter-pill:active:not(:disabled){transform:none}.task-row.just-completed,.habit-row.just-completed,.task-row.just-completed .task-check-mark,.habit-row.just-completed .task-check-mark{animation:none}}
/* Approved five-detail list/search polish. */
@media(min-width:931px){.task-search-toggle{display:none}.search-reveal{min-width:320px}}
+2 -2
View File
@@ -204,7 +204,7 @@ describe('solid cream material system', () => {
it('keeps controls solid, focus visible, mobile targets safe, and motion reducible', () => {
expect(css).toContain('input,select,textarea,.search{background:var(--surface-raised)')
expect(css).toContain(':focus-visible{outline:3px solid var(--focus-ring)')
expect(css).toContain('@media(max-width:930px){.bottom{left:0;right:0;bottom:0;height:calc(64px + var(--safe-area-bottom));')
expect(css).toContain('@media(max-width:930px){.bottom{left:0;right:0;bottom:0;height:calc(56px + var(--safe-area-bottom));')
expect(css).toContain('border:0;border-top:1px solid var(--border-cream);border-radius:0;padding:4px 10px var(--safe-area-bottom)')
expect(css).toMatch(/\.bottom button\{[^}]*min-height:44px/)
expect(css).toMatch(/\.bottom button\.active\{[^}]*background:transparent/)
@@ -1196,7 +1196,7 @@ describe('unified floating add interaction', () => {
expect(css).toContain('.unified-fab.snapping')
expect(countdownPanel).toContain('<AppSheet :open="open" variant="create"')
expect(countdownPanel).toContain('panel-class="countdown-modal"')
expect(css).toMatch(/@media\(max-width:930px\)\{[^}]*\.bottom\{[^}]*height:calc\(64px \+ var\(--safe-area-bottom\)\)[\s\S]*?\.unified-fab\{bottom:calc\(76px \+ var\(--safe-area-bottom\)\)\}/)
expect(css).toMatch(/@media\(max-width:930px\)\{[^}]*\.bottom\{[^}]*height:calc\(56px \+ var\(--safe-area-bottom\)\)[\s\S]*?\.unified-fab\{bottom:calc\(68px \+ var\(--safe-area-bottom\)\)\}/)
})
it('renders the original orange circular plus button', () => {