diff --git a/frontend/index.html b/frontend/index.html index 5727c4e..7a7bb44 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,13 @@ - + + Skip to notes +
-
+ +
diff --git a/frontend/src/components/NoteCard.vue b/frontend/src/components/NoteCard.vue index 4370a1b..6f95476 100644 --- a/frontend/src/components/NoteCard.vue +++ b/frontend/src/components/NoteCard.vue @@ -186,27 +186,6 @@ onBeforeUnmount(() => document.removeEventListener("mousedown", onDocMousedown)) ]" :data-note-id="note.id" > - - - document.removeEventListener("mousedown", onDocMousedown))
- -
- - -
- -
+ +
+ + +
+ + + + +
+
+
diff --git a/frontend/src/style.css b/frontend/src/style.css index 576a0d5..4041e69 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -8,6 +8,35 @@ body, height: 100%; } +/* Track the VISUAL viewport, not the layout viewport. + * + * On a phone the two diverge the moment the on-screen keyboard opens or the URL bar + * retracts: `100%` keeps the taller layout viewport, which slides the sticky header + * off screen and can park a focused field underneath the keyboard. `dvh` is the unit + * that follows the box actually being shown. + * + * Same three selectors rather than a fourth rule, so the box model is unchanged and + * only the number moves; behind @supports so a browser without `dvh` keeps the + * `100%` above instead of falling back to nothing. */ +@supports (height: 100dvh) { + html, + body, + #app { + height: 100dvh; + } +} + +/* Landscape on a notched phone puts the cutout down one SIDE of the page, and with + * `viewport-fit=cover` (index.html) the page is drawing under it. Applied to body so + * every view inherits it, rather than each container remembering; the insets are 0 + * on every device that has no cutout, and 0 in portrait, so this costs nothing where + * it isn't needed. Top and bottom are NOT done here — a sticky header and a scrolling + * board need them in their own boxes, not on the page. */ +body { + padding-left: env(safe-area-inset-left, 0px); + padding-right: env(safe-area-inset-right, 0px); +} + body { @apply bg-neutral-50 text-neutral-900 antialiased; } @@ -61,6 +90,78 @@ body { } } +/* A note card's own controls — the drag grip and the pin/colour/archive/trash set. + * ONE element each, in TWO placements, because "always visible" and "floating over + * the card's top corners" cannot both be true without the controls sitting on top of + * the note's own words. + * + * That is exactly what a phone got. `.hover-reveal` above made the pills permanent + * where nothing can hover (task 2697, correctly — they are the only way to pin or + * archive), but they were still absolutely positioned, so they covered the title: + * "thought sync tauri app" rendered as "ught sync tauri app" with the grip parked on + * the first three characters. + * + * So the default — no hover — is a footer row IN FLOW, which cannot overlap anything + * by construction. A pointer that can hover lifts them back out into the floating + * pills they have always been, where they cost no vertical space and appear only on + * approach. `display: contents` drops the flex row itself out of the way so each + * child positions against the card. + * + * Keyed on hover rather than width, for the reason `.hover-reveal` already gives: a + * narrow window on a laptop still hovers, and a wide tablet still doesn't. */ +.note-actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.125rem; + margin-top: 0.5rem; +} +.note-grip { + /* Pushes the action set to the far edge, so the two read as opposite ends of a + * footer rather than as a clump. */ + margin-right: auto; +} +.note-actions-set { + position: relative; /* the colour popover anchors here in both placements */ +} + +@media (hover: hover) { + .note-actions { + display: contents; + } + .note-grip { + position: absolute; + left: 0.375rem; + top: 0.375rem; + z-index: 10; + margin-right: 0; + } + .note-actions-set { + position: absolute; + right: 0.375rem; + top: 0.375rem; + } +} + +/* The per-card colour popover, anchored to whichever end of the card the action set + * currently occupies: it opens DOWNWARD from a floating top-corner pill, and UPWARD + * from a footer row, so in both cases it grows into the card rather than off it. */ +.note-swatches { + position: absolute; + right: 0; + bottom: 100%; + margin-bottom: 0.375rem; + z-index: 20; +} +@media (hover: hover) { + .note-swatches { + top: 100%; + bottom: auto; + margin-top: 0.375rem; + margin-bottom: 0; + } +} + /* Board motion (M7). Defined once here rather than three times in BoardView's * markup, because "how the board moves" is one idea even though the pinned, other * and non-board grids are three TransitionGroups. diff --git a/frontend/src/views/BoardView.vue b/frontend/src/views/BoardView.vue index d77b992..0e2d61d 100644 --- a/frontend/src/views/BoardView.vue +++ b/frontend/src/views/BoardView.vue @@ -258,16 +258,27 @@ async function onDrop(targetId: string) {