Files
menu/public/styles.css
T
tanshu c31b73d553 Let the owner manage categories, and give the menus a shelf
Categories were baked into config/<site>.site.json, so adding one meant a
rebuild and a redeploy. They are now data: data/sections.json, seeded from the
config the first time, and managed in the admin (add, rename, reorder, remove).
A category's web address is fixed when it is created because that is what a QR
code points at, and a category cannot be removed while it still holds menus.

The landing and the top bar were the ugliest parts. The landing was a column of
hairlines with tiny meta — a broadsheet, and the paper that makes this app worth
looking at was nowhere on it. It is now a shelf: each category is a row whose
right edge is a sliver of that menu's first page, bleeding off the screen, with
the shelf edge in brass and the name in heavy tight Archivo. The bar was a row
of teal pills that would not survive more categories: it now names the menu you
are reading and opens a sheet listing the rest, each with its own paper sliver,
and the current one is the only teal thing on screen. The sheet is a link to the
landing page, so navigating still works without the script.

Also fixes the category manager's own layout, which I shipped unstyled, and the
markup contract the route tests described.
2026-09-22 22:24:53 +05:30

612 lines
11 KiB
CSS

/* Hops n Grains menus.
The chrome is the shelf; the menus are the paper standing on it. Brass is the
taproom's metal, the logo's teal is the only active colour, and the paper of
the photographed menus is the brightest thing on the page. */
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 100 900;
font-stretch: 62% 125%;
font-display: swap;
src: url('/assets/fonts/archivo-latin.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Archivo';
font-style: normal;
font-weight: 100 900;
font-stretch: 62% 125%;
font-display: swap;
src: url('/assets/fonts/archivo-latin-ext.woff2') format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
:root {
--steel: #101719;
--rail: #182125;
--paper: #efeae0;
--accent: #0088aa;
--copper: #c08544;
--ink: #e8e5dd;
--ink-dim: #8f979a;
--ink-on-paper: #1b1f22;
--brass-edge: color-mix(in srgb, var(--copper) 42%, transparent);
--hairline: color-mix(in srgb, var(--copper) 26%, transparent);
--hairline-soft: color-mix(in srgb, var(--copper) 14%, transparent);
--sheet-shadow: color-mix(in srgb, #000 45%, transparent);
--step-0: 1rem;
--step-1: 1.25rem;
--step-2: 1.55rem;
--page-max: 780px;
--content-max: 52rem;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
background: var(--steel);
color: var(--ink);
font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
font-size: var(--step-0);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
a {
color: inherit;
text-decoration: none;
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
/* ---------- Landing: shelves holding menus ---------- */
.landing {
max-width: var(--content-max);
margin: 0 auto;
padding: 0 1.1rem 3rem;
}
.masthead {
padding: clamp(2.2rem, 10vh, 4.5rem) 0 1.6rem;
}
.wordmark {
margin: 0;
font-size: clamp(1.95rem, 8.5vw, 2.9rem);
font-weight: 700;
font-stretch: 118%;
letter-spacing: -0.025em;
line-height: 1;
}
.masthead__tagline {
margin: 0.5rem 0 0;
color: var(--ink-dim);
font-size: 0.98rem;
}
.shelves {
display: flex;
flex-direction: column;
margin-right: -1.1rem;
}
.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;
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;
}
.shelf__peek {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 4.9rem;
box-shadow: -1px 0 0 var(--sheet-shadow);
}
.shelf__peek img {
display: block;
width: 100%;
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);
}
.shelf__name {
font-size: clamp(1.5rem, 6.4vw, 2rem);
font-weight: 700;
font-stretch: 112%;
letter-spacing: -0.02em;
line-height: 1.02;
}
.shelf:hover .shelf__name,
.shelf:focus-visible .shelf__name {
color: var(--accent);
}
.shelf__pages {
color: var(--ink-dim);
font-size: 0.85rem;
font-variant-numeric: tabular-nums;
}
.empty {
max-width: 34ch;
margin: 1rem 0 0;
color: var(--ink-dim);
font-size: var(--step-1);
}
/* ---------- Section page: the bar and the switcher ---------- */
.bar {
position: sticky;
top: 0;
z-index: 20;
display: flex;
align-items: center;
gap: 0.7rem;
padding: 0.5rem 0.85rem 0.45rem;
background: color-mix(in srgb, var(--rail) 94%, transparent);
backdrop-filter: blur(6px);
}
.bar__brand {
display: flex;
align-items: center;
}
.bar__brand img {
display: block;
width: 24px;
height: 24px;
}
.switcher {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.3rem 0.5rem;
border: 1px solid transparent;
color: var(--ink);
font-size: 1.05rem;
font-weight: 600;
letter-spacing: -0.01em;
}
.switcher:hover {
border-color: var(--brass-edge);
}
.switcher__caret {
width: 0.45rem;
height: 0.45rem;
border-right: 1.5px solid var(--copper);
border-bottom: 1.5px solid var(--copper);
transform: rotate(45deg) translate(-0.1rem, -0.1rem);
transition: transform 0.2s ease;
}
.switcher[aria-expanded='true'] .switcher__caret {
transform: rotate(-135deg) translate(-0.05rem, -0.05rem);
}
.pour {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 2px;
background: var(--hairline-soft);
}
.pour__fill {
display: block;
width: 0;
height: 100%;
background: var(--copper);
}
/* ---------- The switcher sheet ---------- */
.sheet {
position: fixed;
inset: 0;
z-index: 40;
display: flex;
align-items: flex-end;
background: color-mix(in srgb, #05090a 74%, transparent);
}
.sheet[hidden] {
display: none;
}
.sheet__inner {
width: 100%;
max-height: 88vh;
overflow-y: auto;
padding: 1.1rem 1rem calc(1.2rem + env(safe-area-inset-bottom));
background: var(--rail);
border-top: 2px solid var(--copper);
animation: sheet-rise 0.24s cubic-bezier(0.2, 0.7, 0.2, 1);
}
@keyframes sheet-rise {
from {
transform: translateY(1.6rem);
opacity: 0.5;
}
to {
transform: none;
opacity: 1;
}
}
.sheet__title {
margin: 0 0 0.5rem;
color: var(--ink-dim);
font-size: 0.85rem;
}
.sheet__list {
margin: 0;
padding: 0;
list-style: none;
}
.sheet__item {
display: grid;
grid-template-columns: 2.4rem 1fr auto;
align-items: center;
gap: 0.85rem;
padding: 0.5rem 0.4rem;
border-bottom: 1px solid var(--hairline-soft);
}
.sheet__item.is-current {
background: color-mix(in srgb, var(--accent) 13%, transparent);
}
.sheet__peek {
width: 2.4rem;
height: 3rem;
overflow: hidden;
background: var(--paper);
}
.sheet__peek img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
}
.sheet__name {
font-size: 1.12rem;
font-weight: 600;
letter-spacing: -0.01em;
}
.sheet__state {
color: var(--ink-dim);
font-size: 0.8rem;
font-variant-numeric: tabular-nums;
}
.sheet__item.is-current .sheet__state {
color: var(--accent);
}
.sheet__close {
margin-top: 0.9rem;
padding: 0.5rem 0.85rem;
border: 1px solid var(--brass-edge);
background: none;
color: var(--ink);
font: inherit;
font-size: 0.9rem;
cursor: pointer;
}
/* ---------- The pages themselves ---------- */
.section-heading {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
max-width: var(--page-max);
margin: 1.2rem auto 0.75rem;
padding: 0 0.9rem;
}
.section-heading__label {
font-size: var(--step-2);
font-weight: 700;
letter-spacing: -0.02em;
}
.section-heading__date {
color: var(--ink-dim);
font-size: 0.88rem;
font-variant-numeric: tabular-nums;
}
.notice {
max-width: var(--page-max);
margin: 0 auto 1rem;
padding: 0.6rem 0.8rem;
border-left: 2px solid var(--copper);
background: var(--rail);
font-size: 0.95rem;
}
.notice a {
color: var(--accent);
text-decoration: underline;
}
.notice--admin {
max-width: none;
border-left-color: var(--accent);
background: color-mix(in srgb, var(--accent) 20%, var(--rail));
}
.pages {
max-width: var(--page-max);
margin: 0 auto;
padding: 0 0.7rem 1.25rem;
}
.page {
margin: 0 0 0.6rem;
background: var(--paper);
box-shadow: 0 1px 0 color-mix(in srgb, #000 35%, transparent);
}
.page__zoom {
display: block;
}
.page img {
display: block;
width: 100%;
height: auto;
}
/* ---------- Footer: page index and older menus ---------- */
.foot {
max-width: var(--page-max);
margin: 0 auto 4.5rem;
padding: 0 0.9rem 1rem;
border-top: 1px solid var(--hairline-soft);
}
.fold {
padding: 0.7rem 0;
border-bottom: 1px solid var(--hairline-soft);
}
.fold summary {
color: var(--ink-dim);
font-size: 0.92rem;
cursor: pointer;
}
.fold[open] summary {
color: var(--ink);
}
.index {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin: 0;
padding: 0.85rem 0 0;
list-style: none;
}
.index a {
display: block;
min-width: 2.3rem;
padding: 0.3rem 0.45rem;
background: var(--rail);
font-variant-numeric: tabular-nums;
text-align: center;
}
.index a:hover,
.index a:focus-visible {
background: var(--accent);
color: var(--steel);
}
.older {
margin: 0.5rem 0 0;
padding: 0;
list-style: none;
}
.older a {
display: flex;
justify-content: space-between;
gap: 1rem;
padding: 0.45rem 0.15rem;
}
.older__label {
font-weight: 500;
}
.older__date {
color: var(--ink-dim);
font-size: 0.88rem;
font-variant-numeric: tabular-nums;
}
.fold__back {
margin: 0.7rem 0 0;
}
.fold__back a {
color: var(--accent);
text-decoration: underline;
}
/* ---------- Counter and zoom ---------- */
.counter {
position: fixed;
right: 0.85rem;
bottom: calc(0.85rem + env(safe-area-inset-bottom));
z-index: 30;
display: inline-flex;
align-items: baseline;
gap: 0.3rem;
padding: 0.45rem 0.7rem;
border: 1px solid var(--brass-edge);
background: var(--paper);
color: var(--ink-on-paper);
font: inherit;
font-size: 0.88rem;
font-variant-numeric: tabular-nums;
cursor: pointer;
}
.counter[hidden] {
display: none;
}
.counter__total {
color: color-mix(in srgb, var(--ink-on-paper) 55%, transparent);
}
.zoom {
position: fixed;
inset: 0;
z-index: 50;
overflow: auto;
background: rgba(6, 9, 10, 0.96);
-webkit-overflow-scrolling: touch;
}
.zoom img {
display: block;
width: min(100%, 1600px);
height: auto;
margin: 0 auto;
}
.zoom__close {
position: fixed;
top: 0.6rem;
right: 0.6rem;
z-index: 1;
padding: 0.5rem 0.75rem;
border: 1px solid var(--brass-edge);
background: var(--paper);
color: var(--ink-on-paper);
font: inherit;
cursor: pointer;
}
/* ---------- Wider screens ---------- */
@media (min-width: 62rem) {
.shelves {
display: grid;
grid-template-columns: 1fr 1fr;
column-gap: 2.5rem;
}
.shelf {
padding-right: 5.6rem;
}
.pages {
max-width: 820px;
}
}
@media print {
.bar,
.sheet,
.counter,
.fold,
.notice {
display: none;
}
body {
background: #fff;
color: #000;
}
.page {
break-inside: avoid;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
transition: none !important;
scroll-behavior: auto !important;
}
}