From c549870ab84fdbb0aa86ea03ad9d69ebd9fdda3e Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 27 May 2026 18:30:12 +0100 Subject: [PATCH] ui(tasks): adopt the setup-wizard checkbox style for select/select-all The custom-drawn green box + white tick was reading too utilitarian against the row's other buttons (and the tick itself had defaulted black against the dim green fill, hard to spot). Switches both .task-select-box (per-row) and the master Select-all to the same chrome the setup wizard uses for its app-pick cards: - accent gradient fill on :checked (was status-success) - 12px white SVG checkmark (inline data: URL, same one as .setup-app input[type=checkbox]:checked::after) - subtle inset border at rest, accent glow on hover/focus - 0.22s setupCheckPop / taskCheckPop pop-in on tick - indeterminate state on the master shows a horizontal dash, drawn from a second inline SVG (still white on accent) Sized to 18px so the row checkbox sits clean alongside the 22px-tall .task-btn buttons. The master in the action bar reuses the same box spec (no separate variant), matching the wizard's "one checkbox style, many places" pattern. Signed-off-by: librelad --- containers/libreportal/frontend/css/tasks.css | 116 ++++++++++-------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/containers/libreportal/frontend/css/tasks.css b/containers/libreportal/frontend/css/tasks.css index 1b650de..c8dd65b 100644 --- a/containers/libreportal/frontend/css/tasks.css +++ b/containers/libreportal/frontend/css/tasks.css @@ -383,25 +383,16 @@ } /* Multi-select checkbox sat to the right of the row's Delete button. - Sized + framed to match the surrounding .task-btn buttons so the row - reads as a single action group. Hides the native input and renders - a custom box; `cursor: pointer` on the wrapping label keeps the - whole 24×24 hit target clickable. */ + Mirrors the .setup-app checkbox from the setup wizard: accent fill + + white SVG check + pop-in, no separate frame. The label wrapper is just + a hit-target — visible chrome is all on .task-select-box. Sized down + to 18px so it sits inline with the 22px-tall .task-btn buttons. */ .task-select { display: inline-flex; align-items: center; justify-content: center; - width: 22px; - height: 22px; - border-radius: 6px; - border: 1px solid rgba(var(--text-rgb), 0.12); - background: rgba(var(--text-rgb), 0.04); cursor: pointer; - transition: background 0.18s ease, border-color 0.18s ease; -} -.task-select:hover { - background: var(--surface-hover); - border-color: var(--status-success); + padding: 2px; } .task-select input[type="checkbox"] { position: absolute; @@ -410,43 +401,54 @@ width: 0; height: 0; } .task-select-box { - width: 12px; - height: 12px; - border-radius: 3px; - border: 1.5px solid rgba(var(--text-rgb), 0.45); - background: transparent; + width: 18px; + height: 18px; + flex-shrink: 0; + border-radius: 5px; + background: rgba(var(--text-rgb), 0.04); + border: 1.5px solid rgba(var(--text-rgb), 0.18); + box-shadow: inset 0 0 0 1px rgba(var(--text-rgb), 0.02); position: relative; - transition: background 0.12s ease, border-color 0.12s ease; + transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease; +} +.task-select:hover .task-select-box { + border-color: rgba(var(--accent-rgb), 0.55); + box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.08); +} +.task-select input[type="checkbox"]:focus-visible + .task-select-box { + outline: none; + border-color: rgba(var(--accent-rgb), 0.85); + box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.20); } .task-select input[type="checkbox"]:checked + .task-select-box { - background: var(--status-success); - border-color: var(--status-success); + background: linear-gradient(135deg, var(--accent), var(--accent)); + border-color: rgba(var(--accent-rgb), 0.9); + box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.35); } .task-select input[type="checkbox"]:checked + .task-select-box::after { content: ""; position: absolute; - top: 1px; - left: 4px; - width: 3px; - height: 7px; - border: solid var(--text-on-accent, #fff); - border-width: 0 2px 2px 0; - transform: rotate(45deg); + inset: 0; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: center; + background-size: 12px 12px; + animation: taskCheckPop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1); } -.task-select input[type="checkbox"]:focus-visible + .task-select-box { - outline: 2px solid var(--accent); - outline-offset: 2px; +@keyframes taskCheckPop { + 0% { transform: scale(0.4); opacity: 0; } + 100% { transform: scale(1); opacity: 1; } } -/* Master "Select all" toggle in the status/action bar (left of Clear All). - Uses the same custom-box visual as the row checkbox so the two reinforce - each other; the label sits inline with the button text style. */ +/* Master "Select all" toggle in the action bar (right of Clear All). + Reuses .task-select-box so the two checkboxes are visually identical. + The wrapping label adds the inline "Select all" text + a hover lift. */ .task-select-all { display: inline-flex; align-items: center; - gap: 6px; + gap: 8px; cursor: pointer; - padding: 4px 8px; + padding: 4px 10px; border-radius: 6px; font-size: 11px; color: var(--text-secondary); @@ -463,22 +465,40 @@ pointer-events: none; width: 0; height: 0; } -/* The indeterminate state (some-but-not-all visible rows ticked) shows - a horizontal dash instead of a check. */ +.task-select-all:hover .task-select-box { + border-color: rgba(var(--accent-rgb), 0.55); + box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.08); +} +.task-select-all input[type="checkbox"]:checked + .task-select-box { + background: linear-gradient(135deg, var(--accent), var(--accent)); + border-color: rgba(var(--accent-rgb), 0.9); + box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.35); +} +.task-select-all input[type="checkbox"]:checked + .task-select-box::after { + content: ""; + position: absolute; + inset: 0; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: center; + background-size: 12px 12px; + animation: taskCheckPop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1); +} +/* Indeterminate state — some-but-not-all visible rows ticked. Swap the + check SVG for a horizontal dash, still white on accent. */ .task-select-all input[type="checkbox"]:indeterminate + .task-select-box { - background: var(--status-success); - border-color: var(--status-success); + background: linear-gradient(135deg, var(--accent), var(--accent)); + border-color: rgba(var(--accent-rgb), 0.9); + box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.35); } .task-select-all input[type="checkbox"]:indeterminate + .task-select-box::after { content: ""; position: absolute; - top: 4px; - left: 1px; - width: 8px; - height: 2px; - background: var(--text-on-accent, #fff); - border: none; - transform: none; + inset: 0; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: center; + background-size: 12px 12px; } .task-select-all-label { font-weight: 500;