)
diff --git a/client/src/components/Planner/dayPlanRows.css b/client/src/components/Planner/dayPlanRows.css
new file mode 100644
index 000000000..11d205959
--- /dev/null
+++ b/client/src/components/Planner/dayPlanRows.css
@@ -0,0 +1,69 @@
+/* Day plan rows: the controls that only show on hover take no room at rest
+ either, so a row's text runs to the edge of its card. A place gives way while
+ they are out (its one line truncates). A note's text wraps over several
+ lines, so its controls fade in over the end of the text instead of
+ reflowing it. Hover, keyboard focus and an open menu bring them out, the
+ focus a click leaves on a row does not; on touch they stay in the row, like
+ the reorder arrows (index.css). */
+@media (hover: hover) and (pointer: fine) {
+ .dp-row:not(:hover):not(:focus-visible):not(:has(:focus-visible)):not(:has([data-menu-open])) .dp-row-actions {
+ max-width: 0;
+ overflow: hidden;
+ padding-inline-start: 0;
+ }
+ /* The fade is the note's own surface: its tint over the day's tint (a plugin
+ may set one) over the card, drawn as layers because a tint alone is
+ see-through. All come from theme variables, so it matches in light and
+ dark and for every note colour. */
+ .dp-note-actions {
+ position: absolute;
+ inset-block: 0;
+ inset-inline-end: 0;
+ padding-inline: 32px 4px;
+ border-start-end-radius: 11px;
+ border-end-end-radius: 11px;
+ background:
+ linear-gradient(to left, var(--dp-note-tint) 60%, transparent),
+ linear-gradient(to left, var(--dp-day-tint, transparent) 60%, transparent),
+ linear-gradient(to left, var(--bg-card) 60%, transparent);
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.15s;
+ }
+ [dir='rtl'] .dp-note-actions {
+ background:
+ linear-gradient(to right, var(--dp-note-tint) 60%, transparent),
+ linear-gradient(to right, var(--dp-day-tint, transparent) 60%, transparent),
+ linear-gradient(to right, var(--bg-card) 60%, transparent);
+ }
+ .dp-row:hover .dp-note-actions,
+ .dp-row:focus-visible .dp-note-actions,
+ .dp-row:has(:focus-visible) .dp-note-actions,
+ .dp-row:has([data-menu-open]) .dp-note-actions {
+ opacity: 1;
+ pointer-events: auto;
+ }
+}
+
+/* The add-booking ticket beside a place's arrows and "...": out with the row's
+ hover in CSS like them. A React hover state re-rendered the whole day list
+ first, so it always came out a beat behind the others. A picked place shows
+ no controls of its own; on touch, with no hover, the ticket stays out. */
+.dp-add-booking {
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.15s;
+}
+.dp-row:hover .dp-add-booking,
+.dp-row:focus-visible .dp-add-booking,
+.dp-row:has(:focus-visible) .dp-add-booking,
+.dp-row:has([data-menu-open]) .dp-add-booking {
+ opacity: 1;
+ pointer-events: auto;
+}
+@media (hover: none) {
+ .dp-add-booking {
+ opacity: 1;
+ pointer-events: auto;
+ }
+}
diff --git a/client/src/components/Planner/noteSurface.ts b/client/src/components/Planner/noteSurface.ts
index 97862c1ca..66e0f4127 100644
--- a/client/src/components/Planner/noteSurface.ts
+++ b/client/src/components/Planner/noteSurface.ts
@@ -1,3 +1,5 @@
+import type { CSSProperties } from 'react'
+
/**
* The four surfaces a note colour produces (#1629).
*
@@ -34,3 +36,12 @@ export function noteSurface(color: string | null | undefined): NoteSurface {
iconColor: color,
}
}
+
+/**
+ * A day's activity area in the plan: its tint as the background, and named as
+ * a variable for the note fades drawn over it (dayPlanRows.css), so a fade
+ * matches a day a plugin has tinted too.
+ */
+export function dayBodyStyle(tint: string | null | undefined): CSSProperties {
+ return { background: tint ?? undefined, '--dp-day-tint': tint ?? 'transparent' } as CSSProperties
+}
diff --git a/client/src/components/Planner/planParts.tsx b/client/src/components/Planner/planParts.tsx
index a55d657c2..2179cf9e6 100644
--- a/client/src/components/Planner/planParts.tsx
+++ b/client/src/components/Planner/planParts.tsx
@@ -49,7 +49,9 @@ export interface MenuEntry {
/**
* The "…" of a row or card: the actions a right-click also offers, for the
* mouse that doesn't right-click and for the keyboard. Quiet at rest, it comes
- * forward with its row's hover or focus (the row carries the `group` class).
+ * forward with its row's hover or keyboard focus (the row carries the `group`
+ * class). Keyboard focus only: a row a click selected keeps the focus, and the
+ * button stayed out on it after the mouse had left.
*/
export function MoreButton({ label, items, size = 26, alwaysVisible = false, className = '' }: {
label: string
@@ -66,8 +68,8 @@ export function MoreButton({ label, items, size = 26, alwaysVisible = false, cla
<>
diff --git a/client/src/index.css b/client/src/index.css
index de5eb915b..2bf9bee9b 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -253,9 +253,9 @@ html.dark .bg-slate-50\/60, html.dark [class*="bg-slate-50/"] { background-color
background: none !important;
padding: 1px 2px;
}
-/* Reveal on row hover (and on keyboard focus, so the buttons are reachable by Tab). */
-.dp-row:hover .reorder-buttons,
-.dp-row:focus-within .reorder-buttons {
+/* Reveal on row hover and on keyboard focus (Tab), not on the focus a click leaves on the row. */
+.dp-row:hover .reorder-buttons, .dp-row:focus-visible .reorder-buttons,
+.dp-row:has(:focus-visible) .reorder-buttons {
opacity: 1;
pointer-events: auto;
}