feat: add service window component and related functionality
- Introduced ServiceWindow component to display the last departure today and the first tomorrow. - Implemented cancellation notice parsing to handle route cancellations effectively. - Enhanced departure display logic to include cancellation alerts and status updates. - Created information blocks for displaying service information, weather, and QR codes. - Added tests for departure features, information handling, and service window functionality. - Updated fixtures for testing various scenarios including cancellations and long wait times.
This commit is contained in:
@@ -86,7 +86,7 @@ h1 { font-size: clamp(30px, 3.8vw, 76px); letter-spacing: -.05em; line-height: 1
|
||||
.information-tabs { display: none; gap: 8px; }
|
||||
.information-tabs button { font-size: 14px; border: 1px solid var(--outline); background: transparent; padding: 8px 12px; border-radius: 10px; color: var(--secondary); }
|
||||
.information-tabs button[aria-current='page'] { color: var(--accent-ink); border-color: var(--accent); background: var(--accent); }
|
||||
.info-grid { flex: 1 1 0; min-height: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
|
||||
.info-grid { flex: 1 1 0; min-height: 0; display: grid; grid-template-columns: repeat(var(--info-columns, 3), minmax(0, 1fr)); gap: 24px; }
|
||||
.info-card { min-width: 0; min-height: 0; border: 1px solid var(--line); border-radius: 24px; padding: clamp(18px, 1.7vw, 32px); display: flex; flex-direction: column; overflow: auto; container-type: size; }
|
||||
.info-card > * { flex-shrink: 0; }
|
||||
.info-card h3 { font-size: clamp(23px, 2vw, 38px); line-height: 1.15; font-weight: 600; letter-spacing: -.03em; margin: 0 0 18px; }
|
||||
@@ -101,6 +101,34 @@ h1 { font-size: clamp(30px, 3.8vw, 76px); letter-spacing: -.05em; line-height: 1
|
||||
.route-card-name { flex: 1 1 0; min-width: 0; height: 100%; display: flex; flex-direction: column; justify-content: center; }
|
||||
.route-list .destination-heading { width: 100%; font-size: clamp(22px, 3cqw, 30px); line-height: 1.2; margin: 0; font-weight: 550; letter-spacing: -.025em; overflow-wrap: anywhere; }
|
||||
.route-list-pages { display: flex; justify-content: flex-end; color: var(--muted); font-size: 16px; padding-top: 10px; }
|
||||
.service-info { container-name: service-card; }
|
||||
.service-window { flex: 1 1 0; min-height: 0; display: grid; grid-template-rows: repeat(2, minmax(0, 1fr)); gap: 20px; }
|
||||
.service-boundary { min-width: 0; min-height: 0; display: flex; flex-direction: column; justify-content: center; gap: 12px; }
|
||||
.service-boundary + .service-boundary { border-top: 1px solid var(--line); padding-top: 20px; }
|
||||
.service-label { display: flex; flex-wrap: wrap; gap: 4px 10px; color: var(--secondary); font-size: clamp(17px, 3cqw, 24px); line-height: 1.25; }
|
||||
.service-label span { color: var(--muted); font-size: .8em; align-self: center; }
|
||||
.service-boundary > time { display: block; color: var(--accent); font-size: clamp(44px, min(17cqw, 19cqh), 112px); font-weight: 600; line-height: 1; }
|
||||
.service-trip { min-width: 0; display: flex; align-items: center; gap: 12px; font-size: clamp(18px, 4cqw, 26px); line-height: 1.2; }
|
||||
.service-trip .route-badge { flex-shrink: 0; width: 72px; height: 48px; border-radius: 14px; font-size: 32px; padding: 0 8px; }
|
||||
.service-trip > span:last-child { overflow-wrap: anywhere; }
|
||||
.service-info .service-empty { margin: 0; font-size: 20px; }
|
||||
.service-info .service-unavailable { margin-block: auto; text-align: center; font-size: 22px; }
|
||||
@container service-card (min-width: 600px) {
|
||||
.service-window { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: minmax(0, 1fr); gap: 32px; }
|
||||
.service-boundary + .service-boundary { border-top: 0; border-left: 1px solid var(--line); padding-top: 0; padding-left: 32px; }
|
||||
}
|
||||
@container service-card (max-height: 380px) {
|
||||
.service-window { gap: 12px; }
|
||||
.service-boundary { gap: 8px; }
|
||||
.service-boundary + .service-boundary { padding-top: 12px; }
|
||||
.service-label { font-size: 17px; }
|
||||
.service-boundary > time { font-size: clamp(40px, min(14cqw, 25cqh), 72px); }
|
||||
.service-trip { font-size: 18px; }
|
||||
.service-trip .route-badge { width: 60px; height: 40px; font-size: 28px; border-radius: 12px; }
|
||||
}
|
||||
@container service-card (min-width: 600px) and (max-height: 380px) {
|
||||
.service-boundary + .service-boundary { padding-top: 0; padding-left: 20px; }
|
||||
}
|
||||
.weather-info { container-name: weather-card; }
|
||||
.weather-body { flex: 1 1 0; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr); align-content: center; align-items: center; justify-items: center; gap: 24px; }
|
||||
.weather-now { min-width: 0; display: flex; flex-direction: column; align-items: center; }
|
||||
@@ -404,7 +432,7 @@ h1 { font-size: clamp(30px, 3.8vw, 76px); letter-spacing: -.05em; line-height: 1
|
||||
.info-screen[data-compact='true'][data-cards='true'] { position: relative; }
|
||||
.info-screen[data-compact='true'][data-cards='true'] .info-intro { position: absolute; top: 27px; right: 20px; z-index: 1; margin: 0; }
|
||||
.info-screen[data-compact='true'][data-cards='true'] .info-intro h2 { display: none; }
|
||||
.info-screen[data-compact='true'][data-cards='true'] .info-card > h3 { padding-right: 290px; min-height: 36px; text-align: left; display: flex; align-items: center; }
|
||||
.info-screen[data-compact='true'][data-cards='true'] .info-card > h3 { padding-right: var(--info-tabs-width, 290px); min-height: 36px; text-align: left; display: flex; align-items: center; }
|
||||
.info-status { font-size: 13px; padding-top: 10px; }
|
||||
.persistent-next > .eyebrow { display: none; }
|
||||
.persistent-next { padding: 10px 0; }
|
||||
@@ -415,6 +443,69 @@ h1 { font-size: clamp(30px, 3.8vw, 76px); letter-spacing: -.05em; line-height: 1
|
||||
.weather-attribution { padding-top: 8px; font-size: 10px; }
|
||||
.weather-info { padding: 16px; }
|
||||
}
|
||||
/* Viewing distance raises type sizes; pagination preserves the available height. */
|
||||
.board { --reading-scale: 1; }
|
||||
.board[data-profile='street'], .board[data-distance='medium'], .board[data-distance='far'] { --hero-height: clamp(125px, calc(18vh * var(--reading-scale)), 270px); }
|
||||
.board h1 { font-size: clamp(30px, calc(3.8vw * var(--reading-scale)), 90px); }
|
||||
.board .clock { font-size: clamp(38px, calc(4.6vw * var(--reading-scale)), 108px); }
|
||||
.board .eyebrow { font-size: clamp(12px, calc(1.05vw * var(--reading-scale)), 26px); }
|
||||
.board .source-label { font-size: clamp(13px, calc(1.15vw * var(--reading-scale)), 28px); }
|
||||
.board .hero-destination h2 { font-size: min(calc(4.7vw * var(--reading-scale)), calc(var(--hero-height) * .34)); }
|
||||
.board .row-destination h3 { font-size: min(calc(54px * var(--reading-scale)), calc(3.5vw * var(--reading-scale)), calc(var(--row-height) * .4)); }
|
||||
.board .row-time { font-size: min(calc(42px * var(--reading-scale)), calc(var(--row-height) * .36)); }
|
||||
.board .info-card h3 { font-size: clamp(23px, calc(2vw * var(--reading-scale)), 44px); }
|
||||
.departure-content[data-next='true']:not([data-long-wait='true']) { --hero-height: clamp(134px, calc(18vh * var(--reading-scale) + 24px), 260px); }
|
||||
.board[data-row-count='5'] .departure-content[data-next='true']:not([data-long-wait='true']), .board[data-row-count='6'] .departure-content[data-next='true']:not([data-long-wait='true']) { --hero-height: clamp(118px, calc(15vh * var(--reading-scale) + 24px), 224px); }
|
||||
.hero-next { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin-top: 8px; color: var(--secondary); font-size: min(calc(22px * var(--reading-scale)), max(15px, calc(var(--hero-height) * .13))); line-height: 1.2; }
|
||||
.hero-next time { color: var(--accent); display: inline-flex; align-items: baseline; gap: 6px; font-weight: 650; }
|
||||
.departure-status { min-width: 0; display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 6px; }
|
||||
.departure-change { color: var(--warning); font-size: clamp(12px, calc(1.1vw * var(--reading-scale)), 20px); line-height: 1.2; }
|
||||
.cancelled-label { display: inline-flex; align-items: center; gap: 8px; color: var(--warning); font-size: min(calc(26px * var(--reading-scale)), calc(var(--row-height, 96px) * .31), 5.5vw); font-weight: 650; white-space: nowrap; }
|
||||
.cancelled-label svg { width: .9em; height: .9em; }
|
||||
.departure-row[data-status='cancelled'] .route-badge { color: var(--warning); border-color: var(--warning); }
|
||||
.departure-row[data-status='cancelled'] h3 { color: var(--secondary); }
|
||||
.empty-departures[data-cancelled='true'] > svg { display: none; }
|
||||
.empty-departures[data-cancelled='true'] h2 { color: var(--warning); font-size: clamp(22px, calc(2.8vw * var(--reading-scale)), 44px); }
|
||||
.departure-content[data-long-wait='true'] { --hero-height: clamp(142px, calc(24vh * var(--reading-scale)), 300px); }
|
||||
.long-wait-time { min-width: 0; display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 8px; }
|
||||
.long-wait-time > span:first-child { color: var(--secondary); font-size: clamp(17px, calc(2vw * var(--reading-scale)), 34px); }
|
||||
.long-wait-time > time { color: var(--accent); font-size: min(calc(144px * var(--reading-scale)), calc(9vw * var(--reading-scale)), calc(var(--hero-height) * .7)); line-height: 1; font-weight: 600; white-space: nowrap; }
|
||||
.board[data-motion='calm'] .numeric-old, .board[data-motion='calm'] .destination-old { display: none; }
|
||||
.board[data-motion='calm'] .numeric-current, .board[data-motion='calm'] .destination-current { transform: none !important; }
|
||||
.board[data-motion='calm'] .numeric-glyph[data-rolling] .numeric-current, .board[data-motion='calm'] .destination-current[data-changing='true'], .board[data-motion='calm'] .scene { animation: calm-in 180ms ease-out both; }
|
||||
@keyframes calm-in { from { opacity: .35; } to { opacity: 1; } }
|
||||
@media (max-width: 1000px) {
|
||||
.board .row-destination h3 { font-size: min(calc(42px * var(--reading-scale)), calc(4vw * var(--reading-scale)), calc(var(--row-height) * .36)); }
|
||||
.board .row-time-compact { font-size: min(calc(20px * var(--reading-scale)), calc(var(--row-height) * .21)); }
|
||||
}
|
||||
@media (max-height: 620px) and (min-width: 701px) {
|
||||
.board h1 { font-size: calc(32px * var(--reading-scale)); }
|
||||
.board .clock { font-size: calc(40px * var(--reading-scale)); }
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.board h1 { font-size: clamp(25px, calc(6.5vw * var(--reading-scale)), 54px); }
|
||||
.board .clock { font-size: clamp(27px, calc(6.8vw * var(--reading-scale)), 56px); }
|
||||
.board .eyebrow { font-size: calc(10px * var(--reading-scale)); }
|
||||
.board .source-label { font-size: calc(12px * var(--reading-scale)); }
|
||||
.board .hero-destination h2 { font-size: min(calc(6vw * var(--reading-scale)), calc(var(--hero-height) * .3)); }
|
||||
.board .row-destination h3 { font-size: min(calc(42px * var(--reading-scale)), calc(5.7vw * var(--reading-scale)), calc(var(--row-height) * .36)); }
|
||||
.board .info-card h3 { font-size: min(calc(24px * var(--reading-scale)), 7vw); }
|
||||
.hero-next { font-size: 15px; gap: 4px 7px; margin-top: 6px; }
|
||||
.departure-status { gap: 4px; }
|
||||
.departure-change { font-size: 12px; }
|
||||
.hero-departure[data-long-wait='true'] { grid-template-columns: auto minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; gap: 8px 12px; }
|
||||
.hero-departure[data-long-wait='true'] > .route-badge { grid-row: 1 / 3; width: min(calc(var(--hero-height) * .7), 110px); height: min(calc(var(--hero-height) - 10px), 160px); }
|
||||
.hero-departure[data-long-wait='true'] .transport-kind { display: none; }
|
||||
.long-wait-time { grid-column: 2; align-items: flex-start; gap: 4px; }
|
||||
.long-wait-time > span:first-child { font-size: 16px; }
|
||||
.long-wait-time > time { font-size: min(56px, 14vw); }
|
||||
.board[data-distance='far'] .hero-departure:not([data-long-wait='true']) { grid-template-columns: auto minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; gap: 8px 12px; }
|
||||
.board[data-distance='far'] .departure-content:not([data-long-wait='true']) { --hero-height: clamp(142px, calc(19vh * var(--reading-scale)), 220px); }
|
||||
.board[data-distance='far'] .hero-departure:not([data-long-wait='true']) > .route-badge { grid-row: 1 / 3; width: 110px; height: min(calc(var(--hero-height) - 10px), 160px); }
|
||||
.board[data-distance='far'] .hero-departure:not([data-long-wait='true']) .transport-kind { display: none; }
|
||||
.board[data-distance='far'] .hero-departure > .departure-status { grid-column: 2; align-items: flex-start; }
|
||||
.board[data-distance='far'] .hero-departure .countdown-large .countdown-value { font-size: 64px; }
|
||||
}
|
||||
::view-transition { pointer-events: none; }
|
||||
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 320ms; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
Reference in New Issue
Block a user