diff --git a/web/e2e/smoke.spec.js b/web/e2e/smoke.spec.js index 5a5e47d..2bab4f7 100644 --- a/web/e2e/smoke.spec.js +++ b/web/e2e/smoke.spec.js @@ -804,6 +804,46 @@ test('en rolle kan rettes uden at fjerne personen', async ({ browser, page }) => expect(trouble).toEqual([]); }); +/** + * The project page on a phone. + * + * Every other test here runs at desktop width, and this is what that misses: the + * header is a flex row of a title and four controls, and 390px does not have room + * for them. Without wrapping, the heading was squeezed to its min-content — which + * with `overflow-wrap: anywhere` is one character — so a project called + * "Skovvænget" rendered as a vertical column of letters while the buttons ran off + * the right edge. It looked like a broken font. + */ +test('projektsiden er brugbar på en telefon', async ({ page }) => { + const trouble = watchForTrouble(page); + await page.setViewportSize({ width: 390, height: 844 }); + await page.goto('/'); + + // The sidebar is a drawer at this width, so the project is reached through it. + await page.getByRole('button', { name: 'Vis menu' }).click(); + const sidebar = page.getByRole('navigation', { name: 'Hovedmenu' }); + await sidebar.getByRole('button', { name: 'Nyt projekt' }).click(); + await sidebar.getByLabel('Projektnavn').fill('Skovvænget'); + await sidebar.getByLabel('Projektnavn').press('Enter'); + + const heading = page.getByRole('heading', { name: 'Skovvænget' }); + await expect(heading).toBeVisible(); + + const box = await heading.boundingBox(); + // Wide enough to hold the word, and short enough not to be a column of + // letters. One line of "Skovvænget" at --text-2xl is nowhere near 200px tall. + expect(box.width, 'overskriften er klemt sammen').toBeGreaterThan(120); + expect(box.height, 'overskriften er brudt op i én bogstavkolonne').toBeLessThan(120); + + // And nothing sticks out past the right edge of the page. + const overflow = await page.evaluate( + () => document.documentElement.scrollWidth - document.documentElement.clientWidth + ); + expect(overflow, 'siden kan scrolles vandret').toBeLessThanOrEqual(1); + + expect(trouble).toEqual([]); +}); + /** * The MCP connector address must not fall through to the app shell. * diff --git a/web/src/routes/projekt/[id]/+page.svelte b/web/src/routes/projekt/[id]/+page.svelte index 672c8ac..5bca5ee 100644 --- a/web/src/routes/projekt/[id]/+page.svelte +++ b/web/src/routes/projekt/[id]/+page.svelte @@ -548,7 +548,10 @@ ondrop={(e) => onSectionDrop(e, '')} > - {#if !unsectioned.length} + + {#if sections.length && !unsectioned.length}

Uden sektion

{/if} @@ -664,17 +667,28 @@ max-width: 1400px; } + /* Wraps, and the title keeps a floor. + * + * Without both, a phone squeezes the heading to its min-content — which with + * `overflow-wrap: anywhere` is *one character*, so "Skovvænget" came out as a + * vertical column of letters while the buttons ran off the right edge. Four + * controls beside a title is more than 390px has, so they belong on the next + * line rather than in a race for the same one. + */ header { display: flex; align-items: center; - gap: var(--s4); + gap: var(--s3); + flex-wrap: wrap; margin-bottom: var(--s5); } h1 { font-size: var(--text-2xl); - flex: 1; - min-width: 0; + /* 8ch rather than 0: the basis is what stops flex shrinking it past the + point where the wrapping below is the better answer. */ + flex: 1 1 8ch; + min-width: 8ch; overflow-wrap: anywhere; } @@ -694,8 +708,8 @@ /* Matches the heading it stands in for, so the swap does not move the page. */ .title { - flex: 1; - min-width: 0; + flex: 1 1 8ch; + min-width: 8ch; font-size: var(--text-2xl); font-weight: 560; letter-spacing: -0.015em; diff --git a/web/src/routes/upcoming/+page.svelte b/web/src/routes/upcoming/+page.svelte index f4942dc..e822da2 100644 --- a/web/src/routes/upcoming/+page.svelte +++ b/web/src/routes/upcoming/+page.svelte @@ -135,17 +135,21 @@ max-width: 1400px; } + /* Wraps, for the same reason the project header does: a title and a switcher + are more than a phone's width, and the switcher belongs underneath rather + than squeezing the heading. */ header { display: flex; align-items: center; - gap: var(--s4); + gap: var(--s3); + flex-wrap: wrap; margin-bottom: var(--s5); } h1 { font-size: var(--text-2xl); - flex: 1; - min-width: 0; + flex: 1 1 8ch; + min-width: 8ch; } .views {