Files
SignageHTML/public/css/main.css
kyle 57cd9809e0 Landscape kiosk overhaul: 3-column layout, resilient updates, visual polish
- Add 3-column balanced site distribution using greedy weight algorithm
- Build new DOM off-screen in DocumentFragment, swap atomically (no flash)
- Skip empty API responses and preserve display on transient errors
- Remove news ticker from UI and grid layout
- Add blue-to-red gradient on site header bars
- Bump font sizes: destinations 1.4em, countdowns 1.5em, line numbers 1.6em
- Add breathing pulse animation on daylight bar sun/moon icons
- Fix daylight bar indicator snapping to position on first render
- Make config button visible in landscape with semi-transparent background
- Add weather forecast strip as grid row 4 with compact styling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:07:10 +01:00

510 lines
11 KiB
CSS

/* ========================================
CSS Custom Properties
======================================== */
:root {
--color-primary: #0061a1;
--color-primary-dark: #004d80;
--color-primary-light: #0077cc;
--color-accent: #4fc3f7;
--color-bg: #f5f5f5;
--color-bg-dark: #222;
--color-text: #333;
--color-text-light: #f5f5f5;
--color-text-muted: #666;
--color-text-muted-dark: #aaa;
--color-urgent: #c41e3a;
--color-urgent-dark: #ff6b6b;
--color-soon: #e67e22;
--color-soon-dark: #f39c12;
--color-now: #00a651;
--color-now-dark: #4ecdc4;
--color-border: #ddd;
--color-border-dark: #555;
--color-surface: white;
--color-surface-dark: #333;
--color-surface-darker: #444;
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 8px;
--shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.2);
--gradient-blue: linear-gradient(135deg, #0061a1 0%, #004d80 100%);
--kiosk-gap: 8px;
--kiosk-countdown-size: 2em;
--ticker-height: 36px;
--ticker-speed: 30s;
--ticker-bg: rgba(0, 0, 0, 0.85);
--font-primary: 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
--font-numbers: 'Segoe UI', 'Roboto Mono', 'SF Mono', 'Consolas', monospace;
--color-bg-kiosk: #1a1a2e;
--color-surface-kiosk: rgba(255, 255, 255, 0.08);
--color-surface-kiosk-hover: rgba(255, 255, 255, 0.12);
--color-text-secondary: #bbb;
--color-text-tertiary: #888;
--color-bar-bg: rgba(0, 0, 0, 0.5);
--color-daylight-night: #191970;
--color-daylight-dawn: #FF6B35;
--color-daylight-day: #FFEB3B;
}
/* ========================================
Base Styles
======================================== */
body {
font-family: var(--font-primary);
margin: 0;
padding: 0;
background-color: var(--color-bg);
color: var(--color-text);
transition: background-color 0.5s ease, color 0.5s ease;
height: 100vh;
overflow: hidden;
}
/* For normal orientation on narrow screens, add padding */
@media (max-width: 1199px) {
body.normal {
padding: 20px;
}
}
/* Auto-apply wide layout for normal orientation on large screens */
@media (min-width: 1200px) {
body.normal {
max-width: 100%;
padding: 8px 12px 0 12px;
}
body.normal #content-wrapper {
display: grid;
grid-template-rows: auto 1fr auto;
gap: 8px;
height: 100vh;
max-height: 100vh;
overflow: hidden;
}
body.normal .clock-container {
grid-row: 1;
margin-bottom: 0;
padding: 6px 16px;
}
body.normal .main-content-grid {
grid-row: 2;
display: block;
overflow-y: auto;
overflow-x: hidden;
min-height: 0;
width: 100%;
}
body.normal .departure-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 6px;
margin-bottom: 0;
width: 100%;
box-sizing: border-box;
padding: 0;
}
body.normal .departure-container > * {
min-width: 0;
max-width: 100%;
}
body.normal .weather-section {
grid-row: 3;
position: sticky;
bottom: 35px;
background-color: inherit;
padding: 8px 0 0 0;
margin-top: 0;
}
body.normal .weather-container {
margin: 0;
max-width: 100%;
}
}
/* ========================================
Dark Mode - Layout-level overrides only
======================================== */
body.dark-mode {
background-color: var(--color-bg-dark);
color: var(--color-text-light);
}
body.dark-mode .departure-card {
background-color: var(--color-surface-dark);
border-left-color: var(--color-primary-light);
}
/* ========================================
Orientation: Normal
======================================== */
body.normal {
max-width: 800px;
}
body.normal .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* ========================================
Orientation: Landscape (Kiosk Mode)
======================================== */
body.landscape {
max-width: 100%;
padding: 10px 20px 0 20px;
background-color: var(--color-bg-kiosk);
color: var(--color-text-light);
}
/* Hide background overlay in landscape for maximum contrast */
body.landscape #background-overlay {
display: none !important;
}
body.landscape #content-wrapper {
display: grid;
grid-template-rows: auto auto 1fr auto auto;
gap: 4px;
height: 100vh;
max-height: 100vh;
overflow: hidden;
}
body.landscape .clock-container {
grid-row: 1;
margin-bottom: 0;
}
/* Compact weather bar in row 2 */
body.landscape #compact-weather-bar {
grid-row: 2;
font-size: 0.85em;
padding: 2px 16px;
}
body.landscape .main-content-grid {
grid-row: 3;
display: block;
overflow: hidden;
min-height: 0;
}
/* Weather forecast strip at bottom - row 4 */
body.landscape .weather-section {
grid-row: 4;
overflow: hidden;
}
body.landscape .weather-container {
overflow: hidden;
max-height: none;
position: static;
}
body.landscape #custom-weather {
background: var(--color-bar-bg);
border-radius: 4px;
padding: 2px 8px;
}
body.landscape #custom-weather .current-weather {
display: none;
}
body.landscape #custom-weather .forecast {
display: flex;
gap: 0;
overflow-x: auto;
overflow-y: hidden;
justify-content: center;
}
body.landscape #custom-weather .forecast-hour {
flex-shrink: 0;
padding: 2px 8px;
min-width: auto;
}
body.landscape #custom-weather .forecast-hour .time {
font-size: 0.65em;
}
body.landscape #custom-weather .forecast-hour .icon img {
width: 20px;
height: 20px;
}
body.landscape #custom-weather .forecast-hour .temp {
font-size: 0.65em;
}
body.landscape #custom-weather .attribution {
display: none;
}
body.landscape .departure-container {
display: flex;
gap: 12px;
overflow-y: auto;
overflow-x: hidden;
padding-right: 4px;
min-height: 0;
height: 100%;
}
body.landscape .departure-column {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
body.landscape .site-container {
margin-bottom: 0;
}
body.landscape .departure-card {
min-height: 0;
background-color: var(--color-surface-kiosk);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 3px;
margin-bottom: 0;
}
body.landscape .line-number-box {
min-width: 48px;
width: 48px;
padding: 2px;
}
body.landscape .line-number-large {
font-size: 1.6em;
}
body.landscape .transport-mode-icon .transport-icon {
width: 14px;
height: 14px;
}
body.landscape .site-container {
margin-bottom: 2px;
}
body.landscape .site-header {
font-size: 0.8em;
padding: 0;
margin-bottom: 2px;
}
/* Daylight bar in row 5 */
body.landscape #daylight-hours-bar {
grid-row: 5;
}
/* Dark card surfaces for landscape */
body.landscape .direction-destination {
color: var(--color-text-light);
}
body.landscape .countdown-large {
color: var(--color-text-light);
}
body.landscape .next-departures {
color: var(--color-text-secondary);
}
/* Compact card spacing in landscape */
body.landscape .directions-wrapper {
padding: 3px 6px;
gap: 1px;
}
body.landscape .direction-row {
min-height: 30px;
gap: 4px;
}
body.landscape .direction-destination {
font-size: 1.4em;
color: #eee;
}
body.landscape .direction-arrow-box {
width: 22px;
height: 22px;
font-size: 0.85em;
}
/* Countdown in landscape */
body.landscape .countdown-large {
font-size: 1.5em;
}
body.landscape .times-container {
min-width: 90px;
max-width: 140px;
}
body.landscape .next-departures {
font-size: 0.75em;
color: var(--color-text-secondary);
white-space: nowrap;
letter-spacing: 0.3px;
}
/* ========================================
Orientation: Vertical (90deg)
======================================== */
body.vertical {
max-width: 100%;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
body.vertical #content-wrapper {
transform: rotate(90deg);
transform-origin: center center;
position: absolute;
width: 100vh;
height: 100vw;
max-width: 800px;
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
background-color: transparent;
left: 50%;
top: 50%;
margin-left: -50vh;
margin-top: -50vw;
}
body.vertical .config-button {
transform: rotate(-90deg);
position: fixed;
right: 10px;
bottom: 10px;
z-index: 1000;
}
body.vertical .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* ========================================
Orientation: Upside Down (180deg)
======================================== */
body.upsidedown {
max-width: 100%;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
body.upsidedown #content-wrapper {
transform: rotate(180deg);
transform-origin: center center;
position: absolute;
width: 100%;
max-width: 800px;
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
background-color: transparent;
left: 50%;
top: 50%;
transform: rotate(180deg) translate(50%, 50%);
}
body.upsidedown .config-button {
transform: rotate(-180deg);
position: fixed;
right: 10px;
bottom: 10px;
z-index: 1000;
}
body.upsidedown .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
}
/* ========================================
Orientation: Vertical Reverse (270deg)
======================================== */
body.vertical-reverse {
max-width: 100%;
height: 100vh;
padding: 0;
margin: 0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
body.vertical-reverse #content-wrapper {
transform: rotate(270deg);
transform-origin: center center;
position: absolute;
width: 100vh;
height: 100vw;
max-width: none;
padding: 20px;
box-sizing: border-box;
overflow: visible;
background-color: transparent;
left: 50%;
top: 50%;
margin-left: -50vh;
margin-top: -50vw;
}
body.vertical-reverse .config-button {
transform: rotate(-270deg);
position: fixed;
right: 10px;
bottom: 10px;
z-index: 1000;
}
body.vertical-reverse .departure-container {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
width: 100%;
}
/* ========================================
Mode Indicator
======================================== */
.mode-indicator {
font-size: 0.7em;
color: var(--color-text-muted);
font-weight: normal;
display: inline;
}