diff --git a/docs/plan.md b/docs/plan.md index 5d9dda2..93c9e7a 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -89,7 +89,7 @@ Protection: a `scrypt` hash (from `node:crypto`, so no native build dependency) ### Customer design -The chrome is the shelf and the menus are the paper on it: each Section is a shelf row whose right edge is a sliver of that menu's first page, bleeding off the screen. The bar names the menu you are reading and opens a sheet listing the rest, which scales to any number of categories; a 2px brass line under it tracks how far through the pages you are. +The navigation is made of the restaurant's own paper: the landing is a row of cards, each one the first page of a menu with the category name over it and the page count on a strip beneath, scrolling sideways and snapping (wrapping into a grid on a wide screen). The bar links every menu as a filled segment, the current one the only teal thing on screen, and a 2px brass line under it tracks how far through the pages you are. ``` ┌────────────────────────────┐ ┌────────────────────────────┐ diff --git a/public/styles.css b/public/styles.css index bcbde4f..0e7fbc3 100644 --- a/public/styles.css +++ b/public/styles.css @@ -76,16 +76,19 @@ a { outline-offset: 3px; } -/* ---------- Landing: shelves holding menus ---------- */ +/* ---------- Landing: the menus as tap handles ---------- + Each category is a card whose face is the first page of that menu, so the + navigation is made of the restaurant's own paper. The row scrolls sideways + and snaps; on a wide screen it wraps into a grid instead. */ .landing { max-width: var(--content-max); margin: 0 auto; - padding: 0 1.1rem 3rem; + padding: 0 0 3rem; } .masthead { - padding: clamp(2.2rem, 10vh, 4.5rem) 0 1.6rem; + padding: clamp(2.2rem, 10vh, 4.5rem) 1.1rem 1.5rem; } .wordmark { @@ -105,45 +108,38 @@ a { .shelves { display: flex; - flex-direction: column; - margin-right: -1.1rem; + gap: 0.85rem; + padding: 0.15rem 0 1rem 1.1rem; + overflow-x: auto; + scroll-snap-type: x mandatory; + /* snap against the same inset the first card starts at, so nothing is ever + left half-scrolled under the screen edge */ + scroll-padding-left: 1.1rem; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} + +.shelves::-webkit-scrollbar { + display: none; } .shelf { position: relative; - display: flex; - flex-direction: column; - justify-content: flex-end; - gap: 0.3rem; - min-height: 6.6rem; - padding: 1.2rem 6.9rem 1.25rem 0.1rem; + flex: 0 0 10.5rem; + height: 15rem; overflow: hidden; - border-top: 1px solid var(--brass-edge); -} - -.shelf:last-child { - border-bottom: 1px solid var(--brass-edge); -} - -/* the shadow the shelf above casts onto what it holds */ -.shelf::after { - content: ''; - position: absolute; - top: 0; - right: 0; - left: 0; - height: 1.3rem; - background: linear-gradient(var(--sheet-shadow), transparent); - pointer-events: none; + background: var(--paper); + box-shadow: 0 1px 0 var(--sheet-shadow); + scroll-snap-align: start; } .shelf__peek { position: absolute; top: 0; right: 0; - bottom: 0; - width: 4.9rem; - box-shadow: -1px 0 0 var(--sheet-shadow); + bottom: 3.2rem; + left: 0; + display: block; } .shelf__peek img { @@ -152,37 +148,48 @@ a { height: 100%; object-fit: cover; object-position: top center; - transform: translateY(4%) scale(1.02); - transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1); -} - -.shelf:hover .shelf__peek img, -.shelf:focus-visible .shelf__peek img { - transform: translateY(0) scale(1.07); + transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1); } .shelf__name { - font-size: clamp(1.5rem, 6.4vw, 2rem); + position: absolute; + right: 0; + bottom: 3.2rem; + left: 0; + padding: 0.45rem 0.6rem; + background: color-mix(in srgb, var(--steel) 88%, transparent); + font-size: 1.05rem; font-weight: 700; - font-stretch: 112%; - letter-spacing: -0.02em; - line-height: 1.02; + letter-spacing: -0.01em; +} + +.shelf__pages { + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 3.2rem; + padding: 0.85rem 0.6rem; + background: var(--paper); + color: var(--ink-on-paper); + font-size: 0.8rem; + font-variant-numeric: tabular-nums; } .shelf:hover .shelf__name, .shelf:focus-visible .shelf__name { - color: var(--accent); + background: color-mix(in srgb, var(--accent) 90%, transparent); + color: var(--steel); } -.shelf__pages { - color: var(--ink-dim); - font-size: 0.85rem; - font-variant-numeric: tabular-nums; +.shelf:hover .shelf__peek img, +.shelf:focus-visible .shelf__peek img { + transform: scale(1.04); } .empty { max-width: 34ch; - margin: 1rem 0 0; + margin: 1rem 1.1rem 0; color: var(--ink-dim); font-size: var(--step-1); } @@ -226,20 +233,22 @@ a { } .bar__link { - padding: 0.35rem 0.6rem; + padding: 0.35rem 0.65rem; + background: color-mix(in srgb, var(--ink) 9%, transparent); color: var(--ink-dim); - font-size: 0.98rem; + font-size: 0.95rem; white-space: nowrap; } .bar__link:hover { color: var(--ink); + background: color-mix(in srgb, var(--ink) 15%, transparent); } .bar__link.is-current { - color: var(--steel); background: var(--accent); - font-weight: 600; + color: var(--steel); + font-weight: 700; } .pour { @@ -466,12 +475,14 @@ a { @media (min-width: 62rem) { .shelves { display: grid; - grid-template-columns: 1fr 1fr; - column-gap: 2.5rem; + grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr)); + gap: 1rem; + padding: 0.15rem 1.1rem 1rem; + overflow: visible; } .shelf { - padding-right: 5.6rem; + height: 16rem; } .pages {