New table rotation flip
This commit is contained in:
fraxle
2026-08-06 00:48:07 +01:00
parent 7f3fbb18c7
commit 60eee8e4c1
4 changed files with 315 additions and 24 deletions
+277
View File
@@ -1732,6 +1732,20 @@
flex-shrink: 0;
}
/* Lays out the sky scope + "3pm" + any event tags on one line. Shared by
both orientations — it leads the row normally and heads the column when
the table is rotated. */
.utci-time-inner {
display: inline-flex;
align-items: center;
gap: 7px;
vertical-align: middle;
}
.utci-time-inner svg {
margin: -6px 0;
flex-shrink: 0;
}
@media (max-width: 800px) {
.utci-time {
font-size: 15px;
@@ -2509,3 +2523,266 @@
min-height: 48px;
}
}
/* ══════════════════════════════════════════════════════════════════════
ROTATED TABLE — hours along the top, metrics down the side
─────────────────────────────────────────────────────────────────────
Toggled by the rotate button in the table's top-left cell (state lives
in useAppState as tableRotated). Unlike the normal orientation — which
is two tables kept in step by useTableScroll — this is ONE table in a
single scroll pane, so both sticky edges are pure CSS.
The pane scrolls in both axes rather than letting the page scroll
vertically: sticky positioning resolves against the nearest scrolling
ancestor, and making the pane scroll horizontally makes it the
vertical scroll ancestor too. Scrolling both here is what lets the
hour row and the metric column stay pinned at the same time.
══════════════════════════════════════════════════════════════════════ */
.utci-rotated-scroll {
overflow: auto;
max-height: min(78vh, 900px);
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
cursor: grab; /* drag-to-scroll, same as the normal body scroller */
}
.utci-rotated-scroll:active {
cursor: grabbing;
}
/* Auto layout — the whole point of the rotated view is that each column
is only as wide as the hour it holds, so the fixed layout the normal
table needs for its JS width-sync is explicitly turned off here. */
.utci-table.utci-table-rotated {
table-layout: auto;
width: max-content;
min-width: 100%;
/* The separate border model is required, not cosmetic. Under
border-collapse:collapse the borders belong to the TABLE rather than
to the cells, so they do not travel with a sticky cell — the brass
now-column brackets and the row separators paint straight over the
pinned metric column and corner as you scroll. Separate borders
belong to their cell and move with it. */
border-collapse: separate;
border-spacing: 0;
}
/* Row separators have to move to the cells: a <tr> border never paints
in the separate model. */
.utci-table-rotated tbody td,
.utci-table-rotated tbody th {
border-bottom: 1px solid #ffffff;
}
/* ── SPACING ──────────────────────────────────────────────────────────
Cells carry icons alongside their numbers (sky scope, cloud, precip,
wind vane), so they need noticeably more room than a bare figure. */
.utci-table-rotated td,
.utci-table-rotated th {
padding: 11px 16px;
min-width: 92px;
white-space: nowrap;
}
/* ── STICKY HOUR ROW (top) ────────────────────────────────────────── */
.utci-table-rotated thead th {
position: sticky;
top: 0;
z-index: 4;
background: #f0e4c4;
vertical-align: middle;
}
/* Hour headings read as times, not as column labels — same display serif
as the time cells in the normal orientation. */
.utci-table-rotated .rot-hour-th {
font-family: Fraunces, serif;
font-weight: 500;
font-size: 16px;
text-transform: none;
letter-spacing: 0;
color: #1e1208;
min-width: 76px;
}
/* Stacked, not inline: rotated, every hour is its own COLUMN, so sitting
the scope beside the time would set the width of all of them. Stacking
puts the scope over the label and lets the columns stay narrow. The
negative svg margin that tightens the inline layout has to go — here it
would eat into the row above. */
.utci-table-rotated .rot-hour-th .utci-time-inner {
flex-direction: column;
gap: 3px;
}
.utci-table-rotated .rot-hour-th .utci-time-inner svg {
margin: 0;
}
.utci-table-rotated .rot-hour-th.is-night {
color: #7a5c30;
}
/* ── STICKY METRIC COLUMN (left) ──────────────────────────────────────
The column-group tints (.utci-table th.grp-*) are 50% transparent,
which is fine for a heading that has nothing behind it — but data
cells scroll UNDER this column, so any transparency lets them show
through. The tint is therefore composited over an opaque cream base:
--grp-tint carries the group colour, painted as a background-image
layer on top of an opaque background-color. */
/* Set on the group divider rows as well as the metric labels, so a group
heading and the metrics beneath it resolve to the SAME colour. Both
composite over the identical opaque base below — a shared tint over
two different backgrounds is what makes them drift apart. */
.utci-table-rotated .grp-felt { --grp-tint: rgba(223,243,236,0.50); }
.utci-table-rotated .grp-surface { --grp-tint: rgba(221,234,248,0.50); }
.utci-table-rotated .grp-ambient { --grp-tint: rgba(236,234,227,0.50); }
.utci-table-rotated .grp-precip { --grp-tint: rgba(247,228,238,0.50); }
.utci-table-rotated .grp-sky { --grp-tint: rgba(234,232,252,0.50); }
.utci-table-rotated .grp-wind { --grp-tint: rgba(250,230,224,0.50); }
.utci-table-rotated .grp-airqual { --grp-tint: rgba(229,240,216,0.50); }
.utci-table-rotated .grp-solar { --grp-tint: rgba(250,234,204,0.50); }
/* Over-qualified on purpose: the normal orientation's group rules
(.utci-table th.grp-felt, .utci-table .grp-label-row th.grp-felt) are
specific enough to win otherwise and would restore the transparency. */
.utci-table.utci-table-rotated tbody th.rot-metric-label,
.utci-table.utci-table-rotated tbody th.rot-metric-label.col-info-th:hover,
.utci-table.utci-table-rotated tbody .grp-label-row--rotated th {
background-color: #f0e4c4;
background-image: linear-gradient(var(--grp-tint, transparent), var(--grp-tint, transparent));
}
.utci-table.utci-table-rotated tbody th.rot-metric-label {
position: sticky;
left: 0;
z-index: 3;
text-align: left;
min-width: 136px;
/* box-shadow rather than border-right so the divider sits outside the
cell box and cannot add to the column's measured width. */
box-shadow: 1px 0 0 rgba(74, 50, 24, 0.18);
}
/* The corner cell sits in both sticky tracks, so it needs to win over
each of them. */
/* The selector is deliberately over-qualified: the normal orientation's
`.utci-table thead th:first-child` rule pins the Hour heading with
position:relative + a JS transform, and would otherwise out-specify
this and stop the corner sticking. */
.utci-table.utci-table-rotated thead th.rot-corner {
position: sticky;
top: 0;
left: 0;
z-index: 6;
background: #f0e4c4;
text-align: left;
transform: none;
box-shadow: 1px 0 0 rgba(74, 50, 24, 0.18);
}
.utci-table-rotated .rot-corner-date {
display: block;
font-size: 10px;
letter-spacing: 0.08em;
color: #a08040;
margin-bottom: 6px;
}
.utci-table-rotated .rot-corner-controls {
display: flex;
align-items: center;
gap: 8px;
}
@media (max-width: 640px) {
/* Icon-only on narrow screens — the toolbar is already tight. */
.table-rotate-btn-label {
display: none;
}
.table-rotate-btn {
padding: 6px 8px;
gap: 0;
}
}
/* ── GROUP DIVIDER ROWS ───────────────────────────────────────────────
Stand in for the spanning group labels that sit above the columns in
the normal orientation. The cell spans the full width, so the LABEL is
what gets pinned rather than the cell. */
.utci-table-rotated .grp-label-row--rotated th {
text-align: left;
padding: 5px 16px;
border-right: 0;
}
.utci-table-rotated .grp-label-pin {
position: sticky;
left: 16px;
display: inline-block;
}
/* ── NOW / NIGHT ──────────────────────────────────────────────────────
Rotated, "now" is a column rather than a row, so the brass bracket
that frames it runs down each side instead of across top and bottom. */
.utci-table-rotated td.is-now,
.utci-table-rotated th.rot-hour-th.is-now {
border-left: 2px solid #c8922a;
border-right: 2px solid #c8922a;
}
.utci-table-rotated th.rot-hour-th.is-now {
background: #fff8e4;
border-top: 2px solid #c8922a;
}
.utci-table-rotated td.is-night {
color: #7a5c30;
}
/* ── ROTATE BUTTON ────────────────────────────────────────────────────
Sits in the toolbar after the view switcher and Edit columns, and only
while the detailed table is showing. */
.table-rotate-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 11px;
border: 1px solid #c9b08a;
border-radius: 6px;
background: #fffcf2;
color: #8a6a34;
font-family: Manrope, sans-serif;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
line-height: 1;
cursor: pointer;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.table-rotate-btn-icon {
font-size: 14px;
line-height: 1;
}
.table-rotate-btn:hover {
background: #f6ead0;
color: #6b4f2a;
}
.table-rotate-btn.on {
background: #c8922a;
border-color: #c8922a;
color: #fffcf2;
}
/* ── MOBILE ───────────────────────────────────────────────────────── */
@media (max-width: 640px) {
.utci-rotated-scroll {
max-height: min(70vh, 620px);
}
.utci-table-rotated td,
.utci-table-rotated th {
padding: 9px 11px;
min-width: 80px;
}
.utci-table-rotated .rot-hour-th {
font-size: 14px;
min-width: 68px;
}
.utci-table-rotated .rot-metric-label {
min-width: 112px;
}
}