Which timers appear is the header's Timers ▾ menu's job, so the gear's source switches (ProPresenter / LTC / module timers, the Clock on/off) are gone. In their place the Show group offers the card's elements — Names, Status lines, Detail lines, Seconds, Leading zeros, Source headings; without seconds a countdown reads in whole minutes, rounded up. The clock's format select is a 24-hour switch (an older layout's "12h" choice is honoured); the clock card has no CLOCK label; Short day and Short month appear once Date is on, and the date fits its card — one line when it can, else a break after the day and a size that fits the longer half. Solo tiles get the same element switches. Shell: the settings window's grab bar is the accent colour and the gear that opened it wears the same colour while it is open; showWhen works in a tile's gear as it does on the admin page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
370 lines
12 KiB
CSS
370 lines
12 KiB
CSS
/* Timers module — scoped to its tiles, shell variables only.
|
|
A card's colour IS its state: running / warning / overrun / idle. The four
|
|
tones are custom properties on the tile root: they default to the shell's
|
|
palette (so a dashboard theme reaches them) and the tile's own colour
|
|
settings, when changed, are set inline on .tm-wrap and win — the
|
|
documented mechanism. Overrun must be unmistakable across the room:
|
|
danger fill + pulse. */
|
|
|
|
[data-module="propresenter-timers"] .tm-wrap {
|
|
--tm-ok: var(--accent);
|
|
--tm-warn: var(--warn);
|
|
--tm-danger: var(--danger);
|
|
--tm-muted: var(--muted);
|
|
--tm-meta-size: 11px;
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
user-select: none;
|
|
-webkit-user-select: none;
|
|
}
|
|
|
|
/* ── degraded-state banner (the ProdDash feed itself is down) ── */
|
|
|
|
[data-module="propresenter-timers"] .tm-banner {
|
|
flex: none;
|
|
margin: 6px 8px 0;
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
background: color-mix(in srgb, var(--danger) 14%, transparent);
|
|
color: var(--danger);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
text-align: center;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-banner[hidden] { display: none; }
|
|
|
|
/* Values still on screen while their source is down are stale — say so
|
|
visually, per group: the LTC card keeps ticking when only ProPresenter
|
|
is unreachable. When the ProdDash feed is down everything is old. */
|
|
[data-module="propresenter-timers"] .tm-group.is-stale .tm-card,
|
|
[data-module="propresenter-timers"] .tm-wrap.is-feed-down .tm-card { opacity: 0.45; }
|
|
|
|
[data-module="propresenter-timers"] .tm-hint {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 12px 16px;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
text-align: center;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-hint[hidden] { display: none; }
|
|
|
|
/* ── source groups: heading + card grid (column count set by fit()) ── */
|
|
|
|
[data-module="propresenter-timers"] .tm-groups {
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--tm-gap, 8px);
|
|
padding: var(--tm-pad, 10px);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-groups[hidden] { display: none; }
|
|
|
|
[data-module="propresenter-timers"] .tm-group {
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1 1 0;
|
|
}
|
|
|
|
[data-module="propresenter-timers"] .tm-head {
|
|
flex: none;
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 8px;
|
|
min-width: 0;
|
|
margin: 0 2px 4px;
|
|
font-size: 11px;
|
|
line-height: 16px;
|
|
color: var(--muted);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-head[hidden] { display: none; }
|
|
|
|
[data-module="propresenter-timers"] .tm-title {
|
|
flex: none;
|
|
font-weight: 800;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* The source's own word while it isn't OK ("Unreachable at …"). */
|
|
[data-module="propresenter-timers"] .tm-note {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-weight: 500;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-group.is-down .tm-note { color: var(--tm-warn); }
|
|
[data-module="propresenter-timers"] .tm-group.is-stale .tm-note { color: var(--tm-danger); }
|
|
|
|
/* Headings off: a source in trouble, or with nothing to show, still says so. */
|
|
[data-module="propresenter-timers"] .tm-empty {
|
|
flex: none;
|
|
padding: 2px 4px;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-empty[hidden] { display: none; }
|
|
[data-module="propresenter-timers"] .tm-group.is-down .tm-empty { color: var(--tm-warn); }
|
|
|
|
[data-module="propresenter-timers"] .tm-grid {
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: grid;
|
|
grid-auto-rows: 1fr;
|
|
gap: var(--tm-gap, 8px);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-grid:empty { display: none; }
|
|
|
|
/* Compact cards (short tiles) and "Status lines" off: the lines under the
|
|
digits go, the colour stays. A note that explains a missing value (no
|
|
LTC input, a solo timer no longer offered) must never be hidden. */
|
|
[data-module="propresenter-timers"] .tm-compact .tm-status:not(.is-note),
|
|
[data-module="propresenter-timers"] .tm-compact .tm-detail,
|
|
[data-module="propresenter-timers"] .tm-nostatus .tm-status:not(.is-note),
|
|
[data-module="propresenter-timers"] .tm-nodetail .tm-detail { display: none; }
|
|
/* "Names" off hides every title; the clock never carries one — its digits say what it is */
|
|
[data-module="propresenter-timers"] .tm-nonames .tm-name,
|
|
[data-module="propresenter-timers"] .tm-card-clock .tm-name { display: none; }
|
|
|
|
/* ── one timer card ── */
|
|
|
|
[data-module="propresenter-timers"] .tm-card {
|
|
min-width: 0;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 2%;
|
|
padding: 4px 8px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 10px;
|
|
background: color-mix(in srgb, var(--bg) 60%, transparent);
|
|
overflow: hidden;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
[data-module="propresenter-timers"] .tm-name {
|
|
flex: none;
|
|
max-width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5em;
|
|
font-size: var(--tm-meta-size);
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* The label is its own box so a long name ends in an ellipsis (a flex
|
|
container's bare text won't). */
|
|
[data-module="propresenter-timers"] .tm-label {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
[data-module="propresenter-timers"] .tm-time {
|
|
flex: none;
|
|
font-size: 32px; /* fit() sets the real size per card */
|
|
font-weight: 700;
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: 0.02em;
|
|
line-height: 1.05;
|
|
color: var(--text);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Admin "Timer font: Monospace" — steadier digits as the numbers change. */
|
|
[data-module="propresenter-timers"] .tm-mono .tm-time {
|
|
font-family: ui-monospace, "SF Mono", "Menlo", "Cascadia Mono", "Consolas", monospace;
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
[data-module="propresenter-timers"] .tm-status,
|
|
[data-module="propresenter-timers"] .tm-detail {
|
|
flex: none;
|
|
max-width: 100%;
|
|
font-size: calc(var(--tm-meta-size) * 0.8); /* smaller than the title; fit() budgets the same share */
|
|
font-weight: 700;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-status[hidden],
|
|
[data-module="propresenter-timers"] .tm-detail[hidden] { display: none; }
|
|
|
|
[data-module="propresenter-timers"] .tm-detail {
|
|
font-weight: 600;
|
|
letter-spacing: 0.04em;
|
|
text-transform: none;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
/* A provider's status line carries its own tone ("ahead 1:20" in green). */
|
|
[data-module="propresenter-timers"] .tm-status[data-tone="ok"] { color: var(--tm-ok); }
|
|
[data-module="propresenter-timers"] .tm-status[data-tone="warn"] { color: var(--tm-warn); }
|
|
[data-module="propresenter-timers"] .tm-status[data-tone="danger"] { color: var(--tm-danger); }
|
|
[data-module="propresenter-timers"] .tm-status[data-tone="muted"] { color: var(--tm-muted); }
|
|
|
|
/* The clock's date: one line when it fits, else the day on its own line
|
|
(renderDate() sizes it to the longer half). */
|
|
[data-module="propresenter-timers"] .tm-status.is-date {
|
|
white-space: normal;
|
|
overflow: visible;
|
|
text-overflow: clip;
|
|
text-align: center;
|
|
line-height: 1.15;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-status.is-date span { white-space: nowrap; }
|
|
[data-module="propresenter-timers"] .tm-status.is-date.is-wrap span { display: block; }
|
|
|
|
/* Explanatory notes read as sentences, not badges. */
|
|
[data-module="propresenter-timers"] .tm-status.is-note {
|
|
text-transform: none;
|
|
letter-spacing: 0.02em;
|
|
white-space: normal;
|
|
text-align: center;
|
|
}
|
|
|
|
/* ── state looks (feedbacks) ── */
|
|
|
|
[data-module="propresenter-timers"] .tm-card.is-ok {
|
|
border-color: color-mix(in srgb, var(--tm-ok) 55%, var(--border));
|
|
background: color-mix(in srgb, var(--tm-ok) 10%, transparent);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-card.is-ok .tm-time { color: var(--tm-ok); }
|
|
|
|
[data-module="propresenter-timers"] .tm-card.is-warn {
|
|
border-color: color-mix(in srgb, var(--tm-warn) 55%, var(--border));
|
|
background: color-mix(in srgb, var(--tm-warn) 10%, transparent);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-card.is-warn .tm-time { color: var(--tm-warn); }
|
|
|
|
[data-module="propresenter-timers"] .tm-card.is-muted .tm-time { color: var(--tm-muted); }
|
|
|
|
/* The wall clock is information, not a state — plain text colour. */
|
|
[data-module="propresenter-timers"] .tm-card.is-plain .tm-time { color: var(--text); }
|
|
|
|
/* Overrun: readable across the room even on a wall display. */
|
|
[data-module="propresenter-timers"] .tm-card.is-danger {
|
|
border-color: var(--tm-danger);
|
|
background: color-mix(in srgb, var(--tm-danger) 22%, transparent);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-card.is-danger .tm-time { color: var(--tm-danger); }
|
|
[data-module="propresenter-timers"] .tm-card.is-overrun {
|
|
animation: tm-overrun-pulse 1.2s ease-in-out infinite;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-card.is-overrun .tm-status { font-weight: 800; }
|
|
|
|
@keyframes tm-overrun-pulse {
|
|
50% { background: color-mix(in srgb, var(--tm-danger) 38%, transparent); }
|
|
}
|
|
|
|
/* A solo tile whose timer is no longer offered. */
|
|
[data-module="propresenter-timers"] .tm-card.is-missing { border-style: dashed; }
|
|
|
|
/* ── the LTC card: visually its own thing ── */
|
|
|
|
[data-module="propresenter-timers"] .tm-card-ltc { border-style: dashed; }
|
|
|
|
[data-module="propresenter-timers"] .tm-ltc-badge {
|
|
flex: none;
|
|
padding: 0.15em 0.55em;
|
|
border-radius: 5px;
|
|
background: color-mix(in srgb, var(--tm-ok) 24%, transparent);
|
|
color: var(--text);
|
|
font-size: 0.95em;
|
|
letter-spacing: 0.12em;
|
|
}
|
|
|
|
/* No signal: muted digits, a blinking warning line. */
|
|
[data-module="propresenter-timers"] .tm-card.is-nosignal {
|
|
border-color: color-mix(in srgb, var(--tm-warn) 55%, var(--border));
|
|
}
|
|
[data-module="propresenter-timers"] .tm-card.is-nosignal .tm-status {
|
|
animation: tm-nosignal-blink 1.6s steps(2) infinite;
|
|
}
|
|
|
|
@keyframes tm-nosignal-blink { 50% { opacity: 0.35; } }
|
|
|
|
/* ── the "Timers ▾" menu (container styled/positioned by the shell) ── */
|
|
|
|
[data-module="propresenter-timers"] .tm-menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 14px;
|
|
width: 100%;
|
|
padding: 6px 9px;
|
|
background: none;
|
|
border: none;
|
|
border-radius: 6px;
|
|
color: var(--text);
|
|
font-size: 12px;
|
|
font-family: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
white-space: nowrap;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-menu-item:hover { background: var(--border); }
|
|
[data-module="propresenter-timers"] .tm-menu-item .name { font-weight: 600; }
|
|
[data-module="propresenter-timers"] .tm-menu-item:not(.on) .name { color: var(--muted); }
|
|
|
|
/* A small switch: off = hollow pill, on = filled with the accent. (The menu
|
|
hangs off the tile's title bar, outside .tm-wrap, so the tile's own colour
|
|
properties don't reach it — the shell's palette does.) */
|
|
[data-module="propresenter-timers"] .tm-switch {
|
|
flex: none;
|
|
position: relative;
|
|
width: 26px;
|
|
height: 14px;
|
|
border-radius: 7px;
|
|
border: 1px solid var(--border);
|
|
background: color-mix(in srgb, var(--muted) 25%, transparent);
|
|
transition: background 0.15s ease, border-color 0.15s ease;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-switch::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 1px;
|
|
left: 1px;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: var(--text);
|
|
transition: transform 0.15s ease;
|
|
}
|
|
[data-module="propresenter-timers"] .tm-menu-item.on .tm-switch {
|
|
border-color: var(--accent);
|
|
background: var(--accent);
|
|
}
|
|
[data-module="propresenter-timers"] .tm-menu-item.on .tm-switch::after {
|
|
transform: translateX(12px);
|
|
background: var(--bg);
|
|
}
|
|
|
|
[data-module="propresenter-timers"] .tm-menu-note {
|
|
padding: 6px 9px;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
}
|