diff --git a/workout-tracker/e2e/rest-timer-visibility.spec.ts b/workout-tracker/e2e/rest-timer-visibility.spec.ts new file mode 100644 index 0000000..f483934 --- /dev/null +++ b/workout-tracker/e2e/rest-timer-visibility.spec.ts @@ -0,0 +1,89 @@ +import { test, expect } from '@playwright/test'; + +/** + * TDD: Rest timer should be the most prominent element during rest. + * It should overlay the header so it cannot be occluded. + */ +test.describe('Rest Timer Visibility', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/'); + await page.waitForSelector('#app'); + await page.click('#start-workout-btn'); + await page.waitForSelector('.workout-screen'); + }); + + test('rest timer covers the header area when active', async ({ page }) => { + // Complete first set to trigger rest timer + await page.click('[data-testid="done-set-btn"]'); + + const timer = page.locator('#rest-timer'); + await expect(timer).toBeVisible(); + + // Timer should be at the very top of the viewport (covering the header) + const timerBox = await timer.boundingBox(); + expect(timerBox).not.toBeNull(); + expect(timerBox!.y).toBeLessThanOrEqual(10); // At or near top of viewport + }); + + test('rest timer has a large, prominent countdown display', async ({ page }) => { + await page.click('[data-testid="done-set-btn"]'); + + const timerValue = page.locator('#timer-value'); + await expect(timerValue).toBeVisible(); + + // Timer value font size should be large (at least 2.5rem = 40px) + const fontSize = await timerValue.evaluate((el) => { + return parseFloat(getComputedStyle(el).fontSize); + }); + expect(fontSize).toBeGreaterThanOrEqual(40); + }); + + test('rest timer has higher z-index than the header', async ({ page }) => { + await page.click('[data-testid="done-set-btn"]'); + + const timerZ = await page.locator('#rest-timer').evaluate((el) => { + return parseInt(getComputedStyle(el).zIndex || '0', 10); + }); + const headerZ = await page.locator('.app-header').evaluate((el) => { + return parseInt(getComputedStyle(el).zIndex || '0', 10); + }); + + expect(timerZ).toBeGreaterThan(headerZ); + }); + + test('rest timer spans the full width of the app', async ({ page }) => { + await page.click('[data-testid="done-set-btn"]'); + + const timer = page.locator('#rest-timer'); + await expect(timer).toBeVisible(); + + const timerBox = await timer.boundingBox(); + const appBox = await page.locator('#app').boundingBox(); + + expect(timerBox).not.toBeNull(); + expect(appBox).not.toBeNull(); + // Timer should be nearly full-width (within a few pixels for border/padding) + expect(timerBox!.width).toBeGreaterThanOrEqual(appBox!.width - 2); + }); + + test('skip button remains accessible on the rest timer overlay', async ({ page }) => { + await page.click('[data-testid="done-set-btn"]'); + + const skipBtn = page.locator('#skip-timer-btn'); + await expect(skipBtn).toBeVisible(); + await expect(skipBtn).toBeInViewport(); + + // Clicking skip still works + await skipBtn.click(); + await expect(page.locator('#rest-timer')).toBeHidden(); + }); + + test('rest timer visual snapshot', async ({ page }) => { + await page.click('[data-testid="done-set-btn"]'); + await expect(page.locator('#rest-timer')).toBeVisible(); + + await expect(page).toHaveScreenshot('rest-timer-overlay.png', { + maxDiffPixelRatio: 0.05, + }); + }); +}); diff --git a/workout-tracker/e2e/rest-timer-visibility.spec.ts-snapshots/rest-timer-overlay-chromium-linux.png b/workout-tracker/e2e/rest-timer-visibility.spec.ts-snapshots/rest-timer-overlay-chromium-linux.png new file mode 100644 index 0000000..b2a8a5c Binary files /dev/null and b/workout-tracker/e2e/rest-timer-visibility.spec.ts-snapshots/rest-timer-overlay-chromium-linux.png differ diff --git a/workout-tracker/e2e/rest-timer-visibility.spec.ts-snapshots/rest-timer-overlay-mobile-chrome-linux.png b/workout-tracker/e2e/rest-timer-visibility.spec.ts-snapshots/rest-timer-overlay-mobile-chrome-linux.png new file mode 100644 index 0000000..61a0008 Binary files /dev/null and b/workout-tracker/e2e/rest-timer-visibility.spec.ts-snapshots/rest-timer-overlay-mobile-chrome-linux.png differ diff --git a/workout-tracker/src/style.css b/workout-tracker/src/style.css index 09514fb..6832968 100644 --- a/workout-tracker/src/style.css +++ b/workout-tracker/src/style.css @@ -266,15 +266,18 @@ body { .rest-timer { background: var(--color-accent); color: #fff; - border-radius: var(--radius); - padding: 16px; + padding: 20px 16px; display: flex; align-items: center; gap: 12px; - margin-bottom: 16px; - position: sticky; - top: 60px; - z-index: 9; + position: fixed; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 100%; + max-width: 600px; + z-index: 15; + border-radius: 0 0 var(--radius) var(--radius); } .rest-timer.hidden { @@ -282,13 +285,14 @@ body { } .timer-label { - font-size: 0.85rem; - font-weight: 600; + font-size: 0.9rem; + font-weight: 700; text-transform: uppercase; + letter-spacing: 0.05em; } .timer-value { - font-size: 1.8rem; + font-size: 3rem; font-weight: 700; font-variant-numeric: tabular-nums; flex: 1;