diff --git a/README.md b/README.md index 07bc58f..a896703 100644 --- a/README.md +++ b/README.md @@ -160,9 +160,12 @@ permanently. ## The dashboard -Six tabs, one viewport, no scrolling on desktop. Below 1024 px the constraint is -released, because pinning six panels into a phone viewport produces unreadable -eight-pixel type. +Six tabs, one viewport, no scrolling on desktop. Below 1024 px wide the tabs +become a dropdown and the page scrolls, because pinning six panels into a phone +viewport produces unreadable eight-pixel type. The same release applies below +700 px tall: a short screen gets a scrollbar rather than a one-pixel chart. + +Verified across ten viewports from 1920x1080 to 375x667, six tabs each. | Tab | Answers | | --- | --- | diff --git a/ashvale/dashboard.py b/ashvale/dashboard.py index a87bd82..817a9bc 100644 --- a/ashvale/dashboard.py +++ b/ashvale/dashboard.py @@ -14,6 +14,15 @@ """The dashboard: six tabs in the header, one viewport, no scrolling. +Responsive contract, in both axes. The no-scroll lock needs a floor on height +as well as width: at 1024x600 the forecast chart collapsed to 1 px, which is +worse than scrolling, so below 700 px tall the page scrolls and the charts keep +a readable minimum. Between 700 and 920 px tall the fixed furniture (stat +cards, outlook, diagnostics, the conditions column) compacts by media query so +the height goes to the chart rather than the chrome. Below 1024 px wide the +tab row becomes a native select, because six labels do not fit a phone header +and a clipped tab strip is worse than a dropdown. + Layout contract. The page is a fixed three-row grid pinned to the viewport height: header, tab bar, then a content region that takes the remaining space and never overflows the fold. Each tab lays its panels @@ -78,12 +87,72 @@ DASHBOARD_HTML = r""" .scroller::-webkit-scrollbar-thumb { background:rgba(148,163,184,.28); border-radius:8px; } .flash { animation: flash .5s ease-out; } @keyframes flash { from { color:#a5b4fc; } to { color:inherit; } } + .sr-only { position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0; } :focus-visible { outline:2px solid #818cf8; outline-offset:2px; border-radius:6px; } @media (prefers-reduced-motion: reduce) { *{animation:none!important;transition:none!important} } - @media (min-width:1024px) { + /* The no-scroll contract needs a floor on height as well as width. A 1024x600 + screen cannot show six panels legibly in one viewport: measured, the + forecast chart collapsed to 1 px, which is worse than scrolling. Below + 700 px tall the page scrolls and the charts keep a readable minimum. */ + @media (min-width:1024px) and (min-height:700px) { html,body { height:100%; overflow:hidden; } #shell { height:100dvh; } } + @media (min-width:1024px) and (max-height:699px) { + .pane.active { min-height:640px; } + } + + /* Nothing may push the page sideways. Flex and grid children default to + min-width:auto, so a long unbreakable subtitle inside a flex row refuses to + shrink and drags the whole layout wider than the viewport. Measured at 375 + px this forced the shell to 452 px. */ + html, body { max-width:100%; overflow-x:hidden; } + #shell, main, .pane, .glass { min-width:0; } + .pane > *, .glass > *, .glass p, .glass h2 { min-width:0; } + .glass p, .glass h2, .glass span { overflow-wrap:anywhere; } + + /* Height-aware compaction. A 13 inch laptop has the same width as a desktop + but 280 fewer vertical pixels, and the fixed rows (stat cards, outlook, + diagnostics) eat that difference out of the one row that should flex: the + chart. Measured at 1280x800 the chart fell to 124 px, which is unreadable. + These rules give the height back to the chart rather than letting the + furniture keep it. */ + @media (min-width:1024px) and (max-height:920px) { + .stat-card { padding:0.7rem 0.85rem; } + .stat-card .stat-big { font-size:2.1rem; line-height:1; } + .stat-card .stat-spark { height:1.4rem; } + .row-outlook { padding:0.55rem 1rem; } + .row-outlook .row-sub { display:none; } + .row-diag { padding-top:0.35rem; padding-bottom:0.35rem; } + .fc-strip { margin-top:0.35rem; padding-top:0.35rem; } + } + /* Conditions ahead is a fixed stack: icon, probability, the wet/dry buttons + and the tendency chart. At 1280x800 it needed 351 px into 267, so it + clipped its own chart. Same treatment as the stat cards: give the height + back by shrinking the furniture, not by hiding the reading. */ + @media (min-width:1024px) and (max-height:920px) { + .cond-cap { display:none; } + .cond-icon svg { width:36px; height:36px; } + .cond-label { padding:0.35rem 0.5rem; } + } + @media (min-width:1024px) and (max-height:830px) { + .cond-icon svg { width:30px; height:30px; } + /* 1366x768 is the most common laptop resolution in the world, so it has to + fit rather than nearly fit. These four give back about 36 px. */ + .cond-inner { gap:0.35rem; } + .cond-labstat, .cond-split { display:none; } + .cond-label { padding:0.3rem 0.45rem; } + .cond-label button { padding-top:0.2rem; padding-bottom:0.2rem; } + /* At 1024 the column is narrower, so the same text wraps taller. The + Zambretti number is already spelled out by the sky line above it. */ + .cond-z { display:none; } + #c-label { font-size:0.8rem; } + .stat-card { padding:0.55rem 0.75rem; } + .stat-card .stat-big { font-size:1.8rem; } + .stat-card .stat-spark { display:none; } + .stat-card .stat-detail { font-size:9px; } + .row-outlook { padding:0.4rem 0.85rem; } + }
@@ -114,11 +183,30 @@ DASHBOARD_HTML = r""" -