feat(t-paliad-105): hide Überfällig card at zero, alarm at >0

Überfällig is an emergency category — never a normal-state tile. Replace the
prior `.dashboard-card-quiet` dim-but-visible behavior with two states:

- overdue === 0 → card removed from the layout (`*-overdue-hidden`).
  The Dashboard summary grid and the Fristen summary cards now use
  `repeat(auto-fit, minmax(180px, 1fr))` so the row re-flows to 3 cards
  instead of leaving an empty 4th column.
- overdue > 0 → saturated red surface, white text, soft pulsing red ring
  via `paliad-alarm-pulse`. Honors `prefers-reduced-motion`. Dark theme
  uses a slightly lighter red so the alarm still pops on midnight.

Applied on both surfaces (`#dashboard-card-overdue` and the Fristen
`.frist-summary-card[data-status="overdue"]`).
This commit is contained in:
m
2026-05-04 11:52:35 +02:00
parent 1a815979f8
commit 2bbbe562d7
3 changed files with 76 additions and 7 deletions

View File

@@ -133,10 +133,13 @@ function renderSummary(s: DeadlineSummary): void {
setCount("dashboard-count-upcoming", s.upcoming);
setCount("dashboard-count-completed", s.completed_this_week);
// Tone down the red card when there's nothing overdue — reduces alarm
// fatigue when the user has a clean slate.
// Überfällig is an emergency category — hide the card entirely on a clean
// slate (the .dashboard-summary-grid uses auto-fit so the row re-flows to
// 3 cards) and trip the alarm styling when there's anything overdue. See
// t-paliad-105.
const overdueCard = document.getElementById("dashboard-card-overdue")!;
overdueCard.classList.toggle("dashboard-card-quiet", s.overdue === 0);
overdueCard.classList.toggle("dashboard-card-overdue-hidden", s.overdue === 0);
overdueCard.classList.toggle("dashboard-card-alarm", s.overdue > 0);
}
function renderMatters(s: MatterSummary): void {

View File

@@ -95,11 +95,25 @@ async function loadSummary() {
setCount("sum-week", sum.this_week);
setCount("sum-upcoming", sum.upcoming);
setCount("sum-completed", sum.completed);
applyOverdueState(sum.overdue);
} catch {
/* non-fatal */
}
}
// Überfällig is an emergency category — hide the card on a clean slate (the
// .frist-summary-cards grid uses auto-fit so the row re-flows to 3 cards) and
// trip the alarm styling when there's anything overdue. Mirrors the Dashboard
// logic in client/dashboard.ts. See t-paliad-105.
function applyOverdueState(overdue: number) {
const card = document.querySelector<HTMLElement>(
'.frist-summary-card[data-status="overdue"]',
);
if (!card) return;
card.classList.toggle("frist-card-overdue-hidden", overdue === 0);
card.classList.toggle("frist-card-alarm", overdue > 0);
}
function setCount(id: string, n: number) {
const el = document.getElementById(id);
if (el) el.textContent = String(n);

View File

@@ -5115,8 +5115,11 @@ input[type="range"]::-moz-range-thumb {
}
.frist-summary-cards {
/* auto-fit so the row re-flows when a card is hidden (Überfällig is
hidden when the count is zero — see t-paliad-105). With explicit
`repeat(4, …)` a hidden card leaves an empty 4th column. */
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 0.75rem;
margin: 0 0 1.5rem;
}
@@ -5592,8 +5595,10 @@ input[type="range"]::-moz-range-thumb {
}
.dashboard-summary-grid {
/* auto-fit so the row re-flows when a card is hidden (Überfällig is
hidden when the count is zero — see t-paliad-105). */
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
}
@@ -5640,8 +5645,55 @@ input[type="range"]::-moz-range-thumb {
.dashboard-card-done .dashboard-card-count { color: var(--status-neutral-fg-2); }
.dashboard-card-done { border-left: 3px solid var(--status-neutral-fg-2); }
.dashboard-card-quiet .dashboard-card-count { color: var(--color-text-muted); }
.dashboard-card-quiet { border-left-color: var(--color-border); }
/* Überfällig alarm — see t-paliad-105.
When overdue > 0 the card flips from a calm border-left accent to a
saturated red surface with a soft pulsing ring. The matching JS toggles
`.dashboard-card-alarm` (Dashboard) and `.frist-card-alarm` (Fristen);
when overdue === 0 the card is hidden entirely instead of dimmed. */
@keyframes paliad-alarm-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgb(220 38 38 / 0.55); }
50% { box-shadow: 0 0 0 10px rgb(220 38 38 / 0); }
}
.dashboard-card-red.dashboard-card-alarm,
.frist-summary-card.frist-card-overdue.frist-card-alarm {
background: #dc2626;
color: #ffffff;
border-color: #991b1b;
border-left-color: #7f1d1d;
animation: paliad-alarm-pulse 1.8s ease-in-out infinite;
}
.dashboard-card-red.dashboard-card-alarm .dashboard-card-count,
.dashboard-card-red.dashboard-card-alarm .dashboard-card-label,
.frist-summary-card.frist-card-overdue.frist-card-alarm .frist-summary-count,
.frist-summary-card.frist-card-overdue.frist-card-alarm .frist-summary-label {
color: #ffffff;
}
.frist-summary-card.frist-card-overdue.frist-card-alarm .frist-summary-dot {
background: #ffffff;
opacity: 1;
}
.dashboard-card-overdue-hidden,
.frist-card-overdue-hidden {
display: none;
}
@media (prefers-reduced-motion: reduce) {
.dashboard-card-red.dashboard-card-alarm,
.frist-summary-card.frist-card-overdue.frist-card-alarm {
animation: none;
}
}
:root[data-theme="dark"] .dashboard-card-red.dashboard-card-alarm,
:root[data-theme="dark"] .frist-summary-card.frist-card-overdue.frist-card-alarm {
background: #ef4444;
border-color: #f87171;
border-left-color: #fca5a5;
}
/* --- Matter summary card --- */