Scale TV dashboard blocks and rules overlay to fit without scrolling

When there are more than 6 schedule blocks or 5 rules, font sizes,
padding, and gaps scale down proportionally via CSS custom properties
so all content fits on screen without requiring scroll.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 07:51:43 -07:00
parent fdd85d3df5
commit a67b325b01

View File

@@ -126,7 +126,7 @@
<!-- Right: schedule list --> <!-- Right: schedule list -->
<div class="tv-sidebar"> <div class="tv-sidebar">
<div class="tv-schedule-list"> <div class="tv-schedule-list" :style="blocksScaleStyle">
<ScheduleBlock <ScheduleBlock
v-for="block in scheduleStore.blocks" v-for="block in scheduleStore.blocks"
:key="block.id" :key="block.id"
@@ -175,7 +175,7 @@
<!-- Rules/Expectations full-screen overlay --> <!-- Rules/Expectations full-screen overlay -->
<transition name="tv-alert"> <transition name="tv-alert">
<div v-if="scheduleStore.showRulesOverlay" class="tv-rules-overlay" @click="scheduleStore.showRulesOverlay = false"> <div v-if="scheduleStore.showRulesOverlay" class="tv-rules-overlay" @click="scheduleStore.showRulesOverlay = false">
<div class="tv-rules-box"> <div class="tv-rules-box" :style="rulesScaleStyle">
<div class="tv-rules-header"> <div class="tv-rules-header">
<span class="tv-rules-icon">📋</span> <span class="tv-rules-icon">📋</span>
<span class="tv-rules-title">Rules &amp; Expectations</span> <span class="tv-rules-title">Rules &amp; Expectations</span>
@@ -288,6 +288,22 @@ const currentSubjectOptions = computed(() =>
scheduleStore.currentBlock?.subject?.options || [] scheduleStore.currentBlock?.subject?.options || []
) )
const rulesScaleStyle = computed(() => {
const count = scheduleStore.rulesOverlayItems?.length || 0
if (count <= 5) return {}
// Scale down: at 5 rules = 1.0, at 10 rules ≈ 0.65, capped at 0.5
const scale = Math.max(0.5, 5 / count)
return { '--rules-scale': scale }
})
const blocksScaleStyle = computed(() => {
const count = scheduleStore.blocks?.length || 0
if (count <= 6) return {}
// Scale down: at 6 blocks = 1.0, at 12 blocks ≈ 0.55, capped at 0.5
const scale = Math.max(0.5, 6 / count)
return { '--blocks-scale': scale }
})
function blockElapsed(block) { function blockElapsed(block) {
const currentId = scheduleStore.session?.current_block_id const currentId = scheduleStore.session?.current_block_id
if (block.id === currentId && scheduleStore.blockStartedAt && !scheduleStore.isPaused) { if (block.id === currentId && scheduleStore.blockStartedAt && !scheduleStore.isPaused) {
@@ -531,11 +547,10 @@ onMounted(async () => {
} }
.tv-schedule-list { .tv-schedule-list {
overflow-y: auto; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: calc(0.75rem * var(--blocks-scale, 1));
max-height: 60vh;
} }
/* Expand timer to fill the column */ /* Expand timer to fill the column */
@@ -546,10 +561,10 @@ onMounted(async () => {
.tv-timer-col :deep(.timer-time) { font-size: 5.5rem; } .tv-timer-col :deep(.timer-time) { font-size: 5.5rem; }
.tv-timer-col :deep(.timer-label) { font-size: 1.4rem; } .tv-timer-col :deep(.timer-label) { font-size: 1.4rem; }
/* Make schedule block text larger for TV viewing */ /* Make schedule block text larger for TV viewing, scaling with block count */
.tv-schedule-list :deep(.block-title) { font-size: 1.2rem; } .tv-schedule-list :deep(.block-title) { font-size: calc(1.2rem * var(--blocks-scale, 1)); }
.tv-schedule-list :deep(.block-time) { font-size: 1rem; } .tv-schedule-list :deep(.block-time) { font-size: calc(1rem * var(--blocks-scale, 1)); }
.tv-schedule-list :deep(.block-card) { padding: 0.85rem 1rem; } .tv-schedule-list :deep(.block-card) { padding: calc(0.85rem * var(--blocks-scale, 1)) calc(1rem * var(--blocks-scale, 1)); }
.tv-ws-status { .tv-ws-status {
position: fixed; position: fixed;
@@ -663,12 +678,12 @@ onMounted(async () => {
padding: 3.5rem 4.5rem; padding: 3.5rem 4.5rem;
max-width: 800px; max-width: 800px;
width: 90%; width: 90%;
max-height: 85vh; max-height: 90vh;
overflow-y: auto; overflow: hidden;
box-shadow: 0 0 80px rgba(16, 185, 129, 0.3); box-shadow: 0 0 80px rgba(16, 185, 129, 0.3);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 2rem; gap: calc(2rem * var(--rules-scale, 1));
} }
.tv-rules-header { .tv-rules-header {
display: flex; display: flex;
@@ -676,9 +691,9 @@ onMounted(async () => {
gap: 1rem; gap: 1rem;
justify-content: center; justify-content: center;
} }
.tv-rules-icon { font-size: 3rem; } .tv-rules-icon { font-size: calc(3rem * var(--rules-scale, 1)); }
.tv-rules-title { .tv-rules-title {
font-size: 2.5rem; font-size: calc(2.5rem * var(--rules-scale, 1));
font-weight: 800; font-weight: 800;
color: #34d399; color: #34d399;
text-transform: uppercase; text-transform: uppercase;
@@ -690,19 +705,19 @@ onMounted(async () => {
margin: 0; margin: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1.25rem; gap: calc(1.25rem * var(--rules-scale, 1));
counter-reset: rules-counter; counter-reset: rules-counter;
} }
.tv-rules-item { .tv-rules-item {
counter-increment: rules-counter; counter-increment: rules-counter;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 1.25rem; gap: calc(1.25rem * var(--rules-scale, 1));
font-size: 2rem; font-size: calc(2rem * var(--rules-scale, 1));
font-weight: 500; font-weight: 500;
color: #f1f5f9; color: #f1f5f9;
line-height: 1.3; line-height: 1.3;
padding-bottom: 1.25rem; padding-bottom: calc(1.25rem * var(--rules-scale, 1));
border-bottom: 1px solid #1e293b; border-bottom: 1px solid #1e293b;
} }
.tv-rules-item:last-child { border-bottom: none; padding-bottom: 0; } .tv-rules-item:last-child { border-bottom: none; padding-bottom: 0; }
@@ -711,12 +726,12 @@ onMounted(async () => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
min-width: 2.5rem; min-width: calc(2.5rem * var(--rules-scale, 1));
height: 2.5rem; height: calc(2.5rem * var(--rules-scale, 1));
background: #064e3b; background: #064e3b;
border: 2px solid #10b981; border: 2px solid #10b981;
border-radius: 50%; border-radius: 50%;
font-size: 1.2rem; font-size: calc(1.2rem * var(--rules-scale, 1));
font-weight: 800; font-weight: 800;
color: #34d399; color: #34d399;
flex-shrink: 0; flex-shrink: 0;