mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-31 20:09:11 +00:00
bc9ae1f1a7
The sticky header was offset by minus the row margin, on the assumption that sticky insets constrain the margin box. They do not, so the header sat 2px above the scroll port and lost its top 2px to the overflow. That is exactly the width of the outline marking a group as active, so the top edge of the ring disappeared as soon as the header pinned. Anchoring at 0 leaves the header flush with no clipping. The follow offset now adds a full row gap rather than a single margin, so the running event lines up under the header on the same rhythm as every other row. The operator keeps its negative inset: its list carries a top padding which the header has to cover, and there is no clipping to avoid since the offset only cancels that padding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GjX7D56AW8cL3FXjvtnjtL
341 lines
7.9 KiB
SCSS
341 lines
7.9 KiB
SCSS
@use '@/theme/viewerDefs' as *;
|
|
|
|
$item-height: 3.5rem;
|
|
|
|
.countdown {
|
|
margin: 0;
|
|
box-sizing: border-box; /* reset */
|
|
overflow: hidden;
|
|
width: 100%; /* restrict the page width to viewport */
|
|
height: 100vh;
|
|
|
|
font-family: var(--font-family-override, $viewer-font-family);
|
|
background: var(--background-color-override, $viewer-background-color);
|
|
color: var(--color-override, $viewer-color);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: $view-element-gap;
|
|
padding: $view-outer-padding;
|
|
font-size: $base-font-size;
|
|
|
|
/* =================== HEADER + EXTRAS ===================*/
|
|
.project-header {
|
|
font-size: $header-font-size;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.project-header__brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
min-width: 0;
|
|
}
|
|
|
|
.project-header__brand--without-logo {
|
|
align-self: baseline;
|
|
}
|
|
|
|
.logo {
|
|
max-width: min(200px, 20vw);
|
|
}
|
|
|
|
.title {
|
|
line-height: 1.1em;
|
|
}
|
|
|
|
.clock-container {
|
|
margin-left: auto;
|
|
font-weight: 600;
|
|
|
|
.label {
|
|
font-size: $timer-label-size;
|
|
color: var(--label-color-override, $viewer-label-color);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.time {
|
|
font-size: $timer-value-size;
|
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
|
letter-spacing: 0.05em;
|
|
line-height: 0.95em;
|
|
}
|
|
}
|
|
|
|
.fab-container {
|
|
position: fixed;
|
|
bottom: 1.5rem;
|
|
bottom: calc(1.5rem + env(safe-area-inset-bottom));
|
|
right: $view-inline-padding;
|
|
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: end;
|
|
gap: 2rem;
|
|
|
|
transition-property: opacity;
|
|
transition-duration: 0.3s;
|
|
}
|
|
|
|
.fab-container--hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* ========================= LIST ========================*/
|
|
.empty-state {
|
|
min-height: min(56vh, 36rem);
|
|
padding-top: clamp(4rem, 14vh, 9rem);
|
|
text-align: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.list-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: auto;
|
|
|
|
padding-bottom: max(8rem, calc(5rem + env(safe-area-inset-bottom)));
|
|
}
|
|
|
|
/* a subscribed group and its subscribed events, bounds the sticky header to its own section.
|
|
flex rather than block so that the row margins do not collapse, keeping the spacing between
|
|
rows of a section equal to the spacing between sections */
|
|
.sub-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* scoped to the section, the select view shows the same cards in a flat list */
|
|
.sub-section .sub--group {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* ====================== LIST-ITEM ======================*/
|
|
.sub {
|
|
margin: 2px;
|
|
flex: 1;
|
|
display: grid;
|
|
grid-template-columns: 1rem 1fr auto;
|
|
grid-template-areas:
|
|
'binder schedule status'
|
|
'binder title timer'
|
|
'binder secondary secondary';
|
|
column-gap: 1rem;
|
|
background-color: var(--card-background-color-override, $viewer-card-bg-color);
|
|
padding-right: 1rem;
|
|
border: 1px solid transparent;
|
|
border-radius: $element-border-radius;
|
|
|
|
&:hover {
|
|
.sub__label {
|
|
color: $ui-white;
|
|
}
|
|
}
|
|
}
|
|
|
|
.sub--selected {
|
|
background: $blue-700;
|
|
box-shadow: inset 0 0 0 1px $blue-500;
|
|
}
|
|
|
|
.sub--live {
|
|
background-color: $active-green;
|
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, $active-green 75%, $ui-white);
|
|
}
|
|
|
|
.sub--armed {
|
|
background-color: $gray-1325;
|
|
}
|
|
|
|
.sub--in-group {
|
|
position: relative;
|
|
margin-left: 1rem;
|
|
|
|
&::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -0.5rem;
|
|
top: -0.25rem;
|
|
bottom: -0.25rem;
|
|
width: 2px;
|
|
border-radius: 1px;
|
|
background: var(--group-color, transparent);
|
|
}
|
|
|
|
.sub__binder {
|
|
box-shadow: inset 3px 0 0 var(--group-color, transparent);
|
|
}
|
|
}
|
|
|
|
.sub__binder {
|
|
background: var(--user-color, var(--card-background-color-override, $viewer-card-bg-color));
|
|
border-radius: $element-border-radius 0 0 $element-border-radius;
|
|
grid-area: binder;
|
|
}
|
|
|
|
.sub__schedule {
|
|
grid-area: schedule;
|
|
padding-top: 0.5rem;
|
|
|
|
display: flex;
|
|
gap: 0.25em;
|
|
font-size: $timer-label-size;
|
|
color: var(--label-color-override, $viewer-label-color);
|
|
white-space: nowrap;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.sub__schedule--delayed {
|
|
color: $ontime-delay-text;
|
|
}
|
|
|
|
.sub__schedule--strike {
|
|
text-decoration: line-through;
|
|
}
|
|
|
|
.sub__schedule--over {
|
|
color: $playback-over;
|
|
}
|
|
|
|
.sub__schedule--under {
|
|
color: $playback-under;
|
|
}
|
|
|
|
.sub--group {
|
|
box-shadow: inset 0 0 0 1px var(--user-color, $gray-1325);
|
|
/* the card colour is translucent, an opaque base keeps the rows from showing through the sticky header.
|
|
split into longhands since the shorthand only takes a colour in its last layer */
|
|
background-color: var(--background-color-override, $viewer-background-color);
|
|
background-image:
|
|
linear-gradient(90deg, color-mix(in srgb, var(--user-color, transparent) 18%, transparent), transparent 42%),
|
|
linear-gradient(
|
|
var(--card-background-color-override, $viewer-card-bg-color),
|
|
var(--card-background-color-override, $viewer-card-bg-color)
|
|
);
|
|
|
|
.sub__binder {
|
|
background: var(--user-color, var(--card-background-color-override, $viewer-card-bg-color));
|
|
box-shadow: none;
|
|
}
|
|
|
|
.sub__title {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.sub__schedule,
|
|
.sub__status,
|
|
.sub__label {
|
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
|
}
|
|
}
|
|
|
|
.sub--group.sub--selected {
|
|
box-shadow: inset 0 0 0 1px $blue-500;
|
|
background:
|
|
linear-gradient(90deg, color-mix(in srgb, var(--user-color, transparent) 22%, transparent), transparent 42%),
|
|
$blue-700;
|
|
|
|
.sub__schedule,
|
|
.sub__status,
|
|
.sub__label,
|
|
.sub__eyebrow {
|
|
color: $ui-white;
|
|
}
|
|
}
|
|
|
|
/* a group holding the running event is only outlined, the green fill belongs to the event row itself */
|
|
.sub--group.sub--live {
|
|
box-shadow: inset 0 0 0 2px $active-green;
|
|
}
|
|
|
|
/* loaded but not yet running, subtle enough to not compete with the live ring */
|
|
.sub--group.sub--armed {
|
|
box-shadow: inset 0 0 0 2px $gray-1000;
|
|
}
|
|
|
|
.sub__title {
|
|
grid-area: title;
|
|
padding-bottom: 0.5rem;
|
|
min-width: 0;
|
|
|
|
font-size: $title-font-size;
|
|
line-height: 1.1em;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.sub__eyebrow {
|
|
display: block;
|
|
margin-bottom: 0.15rem;
|
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
|
font-size: $timer-label-size;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.sub__secondary {
|
|
grid-area: secondary;
|
|
padding-bottom: 0.5rem;
|
|
min-width: 0;
|
|
|
|
font-size: $base-font-size;
|
|
line-height: 1.1em;
|
|
color: var(--label-color-override, $viewer-label-color);
|
|
// allow multi-line text but trim before
|
|
white-space: pre-line;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.sub__status {
|
|
grid-area: status;
|
|
padding-top: 0.5rem;
|
|
|
|
font-size: $timer-label-size;
|
|
text-align: right;
|
|
text-transform: uppercase;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.sub__label {
|
|
grid-area: status;
|
|
padding-top: 0.5rem;
|
|
|
|
font-size: $timer-label-size;
|
|
color: var(--label-color-override, $viewer-label-color);
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.sub__timer {
|
|
grid-area: timer;
|
|
|
|
font-size: $timer-value-size;
|
|
line-height: 1.1em;
|
|
font-weight: 600;
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.selection-count {
|
|
color: var(--label-color-override, $viewer-label-color);
|
|
font-size: $timer-label-size;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* ====================== MODIFIERS ======================*/
|
|
.subdued {
|
|
opacity: 0.6;
|
|
}
|
|
}
|