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;