wutzcalc/client/src/styles.css
iris cb316d16d1 tablet: fix cart scroll — flex-end + overflow-y:auto doesn't scroll in WebKit
Follow-up to the #39 fix in d274275, which mara reports still doesn't
scroll in practice.

The bug: overflow-y: auto and justify-content: flex-end on the *same* flex
column is a known WebKit/Safari interop gap — the flex-end-justified
overflow gets clipped at the container bounds instead of becoming a
scrollable region, so nothing actually changed from the user's side despite
the CSS being 'correct' per spec. This app targets iOS Safari 12+
(vite.config.ts legacy targets), squarely in the affected range.

Fix: split the concerns onto two elements. .cart-items is now a plain
overflow-y: auto scroll container with no alignment property. The new
inner .cart-items-inner wrapper carries display:flex/flex-direction:column/
justify-content:flex-end, with min-height: 100% (a floor, not a fixed
height) so it still bottom-anchors a short list but grows past 100% and
scrolls normally in the outer container once content overflows it.

Build clean.
2026-07-30 12:17:15 +02:00

329 lines
8.7 KiB
CSS

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
:root {
--bg: #111;
--fg: #eee;
--surface: #1a1a1a;
--surface-2: #2a2a2a;
--surface-3: #3a3a3a;
--border: #444;
--border-soft: #333;
--pfand: #9ab;
--danger: #ff8a8a;
}
:root[data-theme="light"] {
--bg: #f4f4f5;
--fg: #18181b;
--surface: #ffffff;
--surface-2: #e7e7ea;
--surface-3: #d8d8dc;
--border: #bcbcc2;
--border-soft: #dddde2;
--pfand: #4a6a8a;
--danger: #c0392b;
}
html, body, #app {
margin: 0;
padding: 0;
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg);
color: var(--fg);
font-weight: 600;
overscroll-behavior: none;
-webkit-user-select: none;
user-select: none;
}
button {
font: inherit;
font-weight: 700;
color: inherit;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 8px;
padding: 12px 16px;
cursor: pointer;
}
button:active { background: var(--surface-3); }
button:disabled { opacity: 0.4; }
button.danger { background: #5a2a2a; border-color: #7a3a3a; color: #fff; }
/* ---------- Tablet UI (portrait) ---------- */
.tablet {
display: flex;
flex-direction: column;
height: 100%;
max-width: 100vw;
overflow: hidden;
}
.bar-picker {
display: flex;
flex-direction: column;
gap: 16px;
justify-content: center;
align-items: center;
height: 100%;
padding: 24px;
}
.bar-picker h1 { margin: 0 0 16px; }
.bar-picker button {
width: 280px;
height: 96px;
font-size: 30px;
}
.theme-toggle {
margin-top: 24px;
background: transparent;
border: 1px solid var(--border);
font-size: 18px;
padding: 10px 18px;
}
.topbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px;
background: var(--surface);
border-bottom: 1px solid var(--border-soft);
flex: 0 0 auto;
}
.topbar .brand { display: flex; align-items: center; gap: 10px; min-width: 0; overflow: hidden; }
.topbar .logo { flex: 0 0 auto; }
.topbar .bar-name { font-size: 26px; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar .change { font-size: 17px; padding: 10px 14px; }
/* Drink grid: 3 equal columns. Rows no longer assume a fixed count (the old
"exactly 5 rows fill the area" formula squeezed every row into 1/5 of the
height regardless of how many rows actually existed — 4 rows of drinks
looked cramped, with a wasted empty row's worth of space below them).
`minmax(min-content, 1fr)`: the floor is each row's own natural content
height (name + price + Pfand at whatever font-size is active — this
already tracks the smaller mobile font sizes below with no separate
number needed) rather than a guessed pixel value, and 1fr means rows are
equal height and stretch to fill any leftover space when there's room —
but never shrink below that content-derived floor. When there are enough
rows that even their natural height doesn't fit the visible area, the
grid holds every row at that floor and the whole thing scrolls
(overflow-y: auto) instead of cramming rows smaller — "otherwise go for
what fits". */
.grid {
--gap: 8px;
flex: 1 1 0;
min-height: 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(min-content, 1fr);
align-content: start;
gap: var(--gap);
padding: var(--gap);
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
@media (max-width: 480px) {
.grid { grid-template-columns: repeat(2, 1fr); }
.topbar { padding: 8px 12px; }
.topbar .bar-name { font-size: 20px; }
.topbar .change { font-size: 15px; padding: 8px 10px; }
.drink { font-size: 17px; padding: 6px; }
.drink .price { font-size: 17px; }
.drink .pfand { font-size: 12px; }
.cart { flex: 0 0 34vh; padding: 6px 12px; }
.cart-line { font-size: 16px; }
.cart-total { font-size: 38px; padding: 2px 0 6px; }
.actions button { height: 52px; font-size: 18px; }
}
.drink {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 8px;
font-size: 22px;
overflow: hidden;
}
.drink .name { font-weight: 800; line-height: 1.25; max-width: 100%; word-break: break-word; overflow-wrap: break-word; }
.drink .price { font-size: 22px; font-weight: 700; margin-top: 8px; }
.drink .pfand { font-size: 15px; font-weight: 600; color: var(--pfand); margin-top: 4px; }
.drink.pfand-return {
background: #5a2a2a;
border-color: #7a3a3a;
color: #fff;
}
.picker-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.55);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.picker {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px;
width: min(320px, 86vw);
display: flex;
flex-direction: column;
align-items: stretch;
gap: 14px;
}
.picker-title {
font-size: 22px;
font-weight: 800;
text-align: center;
}
.picker-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
}
.picker-count {
height: 64px;
font-size: 26px;
font-weight: 800;
}
.picker-cancel {
background: transparent;
border-color: var(--border-soft);
}
.cart {
flex: 0 0 38vh;
background: var(--surface);
border-top: 1px solid var(--border-soft);
padding: 8px 16px;
display: flex;
flex-direction: column;
min-height: 0;
}
/* #39 follow-up: overflow-y: auto directly on a flex column that also has
justify-content: flex-end doesn't reliably scroll in WebKit/Safari (the
iOS 12+ targets this app builds for) — the flex-end-justified overflow
gets clipped instead of becoming a scrollable region, so from the user's
side it looked identical to the original overflow: hidden bug. Standard
workaround: the overflow/scroll and the flex-end alignment can't live on
the same element — .cart-items is now a plain scroll container (no
justify-content), and .cart-items-inner is the flex column that actually
bottom-anchors short lists via min-height: 100% (a floor, not a fixed
height, so it still grows past 100% and scrolls normally once content
overflows it). */
.cart-items {
flex: 1 1 0;
min-height: 0;
overflow-y: auto;
margin-bottom: 6px;
}
.cart-items-inner {
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.cart-line {
display: flex;
justify-content: space-between;
align-items: center;
padding: 3px 0;
font-size: 20px;
font-weight: 600;
}
.cart-line.pfand { color: var(--pfand); }
.cart-line.return { color: var(--danger); }
.cart-line.muted { opacity: 0.5; }
.cart-line-label {
display: flex;
align-items: center;
gap: 8px;
}
.qty-btn {
flex: 0 0 auto;
width: 28px;
height: 28px;
padding: 0;
line-height: 1;
font-size: 18px;
border-radius: 6px;
}
.cart-total {
font-size: 56px;
font-weight: 800;
text-align: right;
line-height: 1.1;
padding: 4px 0 8px;
}
.cart-total.negative { color: var(--danger); }
.actions {
display: flex;
gap: 8px;
}
.actions button {
flex: 1;
height: 64px;
font-size: 22px;
font-weight: 800;
color: #fff;
}
.actions .cancel { background: #5a2a2a; border-color: #7a3a3a; }
.actions .confirm { background: #2a5a2a; border-color: #3a7a3a; }
.actions .crew { background: #2a3a5a; border-color: #3a4a7a; }
/* ---------- Admin ---------- */
.admin {
max-width: 960px;
margin: 0 auto;
padding: 16px;
}
.admin h1, .admin h2 { margin-top: 24px; }
.admin table { width: 100%; border-collapse: collapse; margin-bottom: 16px; }
.admin th, .admin td { padding: 6px 8px; border-bottom: 1px solid var(--border-soft); text-align: left; }
.admin input, .admin select {
background: var(--surface); color: var(--fg); border: 1px solid var(--border); border-radius: 4px; padding: 6px;
}
.admin .row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.admin .muted { opacity: 0.6; }
.admin .login { max-width: 320px; margin: 80px auto; display: flex; flex-direction: column; gap: 12px; }
.admin .chart {
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 8px;
padding: 12px 12px 4px;
margin-bottom: 16px;
}
.dnd-list { list-style: none; padding: 0; margin: 0 0 8px; }
.dnd-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
margin-bottom: 4px;
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: 6px;
cursor: grab;
}
.dnd-item:active { cursor: grabbing; }
.dnd-item.drop-target { border-color: #6aa; background: var(--surface-2); }
.dnd-item .grip { opacity: 0.4; cursor: grab; user-select: none; }
.dnd-item .dnd-name { flex: 1; }
.dnd-item button { padding: 2px 8px; font-size: 13px; }
.dnd-list li.muted { padding: 6px 8px; }