diff --git a/web/e2e/smoke.spec.js b/web/e2e/smoke.spec.js index 7ba54ee..a0fab27 100644 --- a/web/e2e/smoke.spec.js +++ b/web/e2e/smoke.spec.js @@ -2030,7 +2030,12 @@ test('en passkey kan registreres og logges ind med', async ({ browser, browserNa // Now sign out and back in with it. No email typed: the device knows which // account its key belongs to, which is also why this page cannot be used to // find out who has an account here. - await page.getByRole('navigation', { name: 'Hovedmenu' }).getByText('Log ud').click(); + // Signing out asks first now: one click arms it, a second within a few seconds + // does it. The whole account row is the button; its label changes to the prompt. + const signOut = page.getByRole('navigation', { name: 'Hovedmenu' }).locator('.user'); + await signOut.click(); + await expect(signOut).toContainText('Tryk igen for at logge ud'); + await signOut.click(); // `exact`, because "Log ind" is a substring of "Log ind med en passkey" and // Playwright matches an accessible name loosely unless told otherwise. await expect(page.getByRole('button', { name: 'Log ind', exact: true })).toBeVisible(); diff --git a/web/src/app.css b/web/src/app.css index 40d29a4..7a3e702 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -210,6 +210,40 @@ --shadow-lg: 0 8px 32px rgb(0 0 0 / 0.4); } +/* Kul — a neutral dark grey, the greys of Apple Notes rather than the default's + faint green cast. The same green accent, so it still reads as verdande; only the + grounds, surfaces and inks turn to plain grey. */ +[data-theme='charcoal'] { + --ground: #1c1c1c; + --surface: #232323; + --surface-raised: #2b2b2b; + --surface-sunken: #171717; + --line: #363636; + --line-strong: #474747; + + --ink: #ededed; + --ink-muted: #a4a4a4; + /* Light enough to clear 4.5:1 against the ground — the quiet tier of dates and + counts is the first to disappear if it is too dim. */ + --ink-faint: #8e8e8e; + + --accent: #5fbf8f; + --accent-hover: #6fcf9f; + --accent-sunken: #23332b80; + --accent-ink: #0d1411; + + --p1: #cf6b6b; + --p2: #d69f5c; + --p3: #6b9ecf; + --p4: var(--ink-faint); + + --danger: #cf6b6b; + --danger-sunken: #33201f; + + --shadow: 0 1px 2px rgb(0 0 0 / 0.25); + --shadow-lg: 0 8px 32px rgb(0 0 0 / 0.4); +} + /* Papir — warm light, closer to paper than to a screen. */ [data-theme='paper'] { --ground: #f6f1e7; @@ -375,9 +409,13 @@ button { søgefelt. */ } +/* A translucent wash of the accent, not a solid block and not the old near-invisible + tint. --accent-sunken sat a hair off the editor's own surface in dark, so a + selection could not be seen; a solid accent could, but read as a heavy slab. Mixing + the accent part-way into transparent lays a clear green film over the text while it + keeps its own colour — visible on white and on the dark surfaces alike. */ ::selection { - background: var(--accent-sunken); - color: var(--ink); + background: color-mix(in oklab, var(--accent) 42%, transparent); } /* Scrollbars, made quiet. The default on Windows is a wide grey slab that diff --git a/web/src/lib/components/Sidebar.svelte b/web/src/lib/components/Sidebar.svelte index 2749ad8..4b27051 100644 --- a/web/src/lib/components/Sidebar.svelte +++ b/web/src/lib/components/Sidebar.svelte @@ -34,6 +34,23 @@ location.href = '/'; } + // Signing out is one click from a button the pointer passes over on its way to + // everything below it, and it drops you at the login screen — so it asks first. + // A second click within a few seconds confirms; anything else lets it lapse. Not + // a dialog: a two-step button is lighter and stays where the eye already is. + let confirmSignOut = $state(false); + let signOutTimer; + function onSignOutClick() { + if (confirmSignOut) { + clearTimeout(signOutTimer); + signOut(); + return; + } + confirmSignOut = true; + clearTimeout(signOutTimer); + signOutTimer = setTimeout(() => (confirmSignOut = false), 3500); + } + let filters = $state([]); let labels = $state([]); @@ -1050,12 +1067,12 @@ {#if !app.connected} {t('nav.offline')} {/if} - @@ -1091,14 +1108,16 @@ display: flex; flex-direction: column; gap: var(--s5); - padding: var(--s4) var(--s3); + /* A few pixels more air down each side than the rows strictly need, so the + menu breathes rather than sitting flush against its own edges. */ + padding: var(--s4) calc(var(--s3) + 3px); background: var(--surface-sunken); border-right: 1px solid var(--line); /* Selve bjælken ruller ikke længere — .scroller gør. `hidden` frem for ingenting, fordi den stadig er den yderste kant: løber en række over, skal den klippes her og ikke skubbe kolonnen bredere. */ overflow: hidden; - padding-left: max(var(--s3), env(safe-area-inset-left)); + padding-left: max(calc(var(--s3) + 3px), env(safe-area-inset-left)); position: relative; } @@ -1695,6 +1714,16 @@ background: var(--surface); } + /* Asking to confirm: the row goes to the danger tint and the name is replaced by + "click again", so the second click is plainly the one that signs out. */ + .user.confirming { + background: var(--danger-sunken); + } + + .user.confirming .user-name { + color: var(--danger); + } + .avatar { width: 22px; height: 22px; diff --git a/web/src/lib/locales/da.js b/web/src/lib/locales/da.js index e65a832..70163c7 100644 --- a/web/src/lib/locales/da.js +++ b/web/src/lib/locales/da.js @@ -119,6 +119,7 @@ export const da = { 'nav.calendar': 'Kalender', 'nav.settings': 'Indstillinger', 'nav.signOut': 'Log ud', + 'nav.signOutConfirm': 'Tryk igen for at logge ud', 'nav.main': 'Hovedmenu', 'nav.search': 'Søg', 'nav.searchLong': 'Søg i opgaver og projekter', @@ -843,6 +844,8 @@ export const da = { // --- the themes ------------------------------------------------------------------- 'theme.dark': 'Nordlys', 'theme.darkNote': 'Mørk med grøn accent.', + 'theme.charcoal': 'Kul', + 'theme.charcoalNote': 'Neutral mørkegrå.', 'theme.dusk': 'Skumring', 'theme.duskNote': 'Mørk og varm, med rav.', 'theme.light': 'Dagslys', diff --git a/web/src/lib/locales/en.js b/web/src/lib/locales/en.js index 3ca1db1..5501ecf 100644 --- a/web/src/lib/locales/en.js +++ b/web/src/lib/locales/en.js @@ -115,6 +115,7 @@ export const en = { 'nav.calendar': 'Calendar', 'nav.settings': 'Settings', 'nav.signOut': 'Sign out', + 'nav.signOutConfirm': 'Click again to sign out', 'nav.main': 'Main menu', 'nav.search': 'Search', 'nav.searchLong': 'Search tasks and projects', @@ -836,6 +837,8 @@ export const en = { // --- the themes ------------------------------------------------------------------- 'theme.dark': 'Northern lights', 'theme.darkNote': 'Dark, with a green accent.', + 'theme.charcoal': 'Charcoal', + 'theme.charcoalNote': 'Neutral dark grey.', 'theme.dusk': 'Dusk', 'theme.duskNote': 'Dark and warm, with amber.', 'theme.light': 'Daylight', diff --git a/web/src/lib/stores.svelte.js b/web/src/lib/stores.svelte.js index 9c26539..7e7a9ea 100644 --- a/web/src/lib/stores.svelte.js +++ b/web/src/lib/stores.svelte.js @@ -1006,6 +1006,7 @@ export const completedView = new CompletedView(); */ export const THEMES = [ { id: 'dark', name: 'theme.dark', note: 'theme.darkNote', dark: true }, + { id: 'charcoal', name: 'theme.charcoal', note: 'theme.charcoalNote', dark: true }, { id: 'dusk', name: 'theme.dusk', note: 'theme.duskNote', dark: true }, { id: 'light', name: 'theme.light', note: 'theme.lightNote', dark: false }, { id: 'paper', name: 'theme.paper', note: 'theme.paperNote', dark: false }, diff --git a/web/src/routes/noter/+page.svelte b/web/src/routes/noter/+page.svelte index 3981951..1a15b9b 100644 --- a/web/src/routes/noter/+page.svelte +++ b/web/src/routes/noter/+page.svelte @@ -1068,8 +1068,9 @@ against the sidebar's rule, which reads as a rendering fault rather than as a layout. */ /* Luft hele vejen rundt om arket. Højre og bund var nul, dengang editoren - ingen kant havde og ingenting kunne støde imod. */ - padding: var(--s3) var(--s4) var(--s3) var(--s4); + ingen kant havde og ingenting kunne støde imod. Et par pixels mere om + notelisten, så kortet ikke ligger klods op ad menuen. */ + padding: var(--s3) var(--s4) var(--s3) calc(var(--s4) + 3px); display: grid; grid-template-columns: minmax(220px, 300px) 1fr; gap: var(--s4); @@ -1083,13 +1084,15 @@ gap: var(--s2); min-height: 0; min-width: 0; - border-right: 1px solid var(--line); - padding-right: var(--s3); + /* A rounded card, the same corner and border as the editor beside it — the + list is its own sheet, not a column ruled off the page. */ + border: 1px solid var(--line); + border-radius: var(--radius); /* White in light, a nuance lighter than the ground in dark — the note list is a surface you read down, so it lifts off the page like the editor does. --surface-raised is #fff in the light themes and one step up in the dark. */ background: var(--surface-raised); - padding-left: var(--s3); + padding: var(--s3); } .head {