antd treats a Modal's width as a fixed pixel value and does not adapt it to the viewport, so the item editor's `width={720}` in Admin.tsx still laid out at 720px on a roughly 390px screen. The footer's OK and Cancel are right-aligned inside that width and the close X sits in the modal's top-right corner, so all three ended up off-screen, and antd sets `overflow: hidden` on the body while a modal is open, so the page behind could not be scrolled to reach them either. The dialog had no way out short of the browser's back button.
Capped in styles.css rather than at each call site, because `Modal.confirm` — used by the review queue when publishing at a price nobody chose — has no call site to edit and would have been left broken by a per-modal fix. The same rule covers the 640 and 480 widths in Customers.tsx and the preview drawer, all of which overflow a phone for the same reason. `max-width` beats the inline `width` antd writes on the element, so none of it needs `!important`.
Bounding `.ant-modal-body` rather than the modal is what actually keeps the buttons reachable: the footer is a sibling of the body, not a child, so a tall form scrolls inside the modal while the footer stays where it is. Sizing the modal alone would have moved the overflow rather than removed it.
Verified in the built bundle rather than only in source — `@media (max-width: 767px)` and all three rules are present in dist after a production build, which is the step that distinguishes a fix that shipped from one that merely compiled. Not verified visually on a device: this is a layout change with no unit coverage, and the five overlays worth checking at 390x844 are listed on the issue.
MDEditor's `preview="live"` still splits the pane in two and is cramped at this width. That is a behaviour change rather than a layout fix and is deliberately left out; it is recorded on the issue instead.
Closes #314
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
205 lines
4.8 KiB
CSS
Executable File
205 lines
4.8 KiB
CSS
Executable File
body { margin: 0; }
|
|
|
|
.item-card .card-cover-img { width: 100%; height: 220px; object-fit: cover; display: block; }
|
|
.item-card .card-cover-placeholder { width: 100%; height: 220px; background: #eee; }
|
|
.price { font-weight: 600; margin: 8px 0; font-size: 16px; }
|
|
.paypal-slot { margin-top: 8px; }
|
|
|
|
.carousel-wrap { position: relative; }
|
|
.carousel-arrow {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
z-index: 2;
|
|
opacity: 0.85;
|
|
}
|
|
.carousel-arrow-left { left: 8px; }
|
|
.carousel-arrow-right { right: 8px; }
|
|
.carousel-count {
|
|
position: absolute;
|
|
bottom: 8px;
|
|
right: 8px;
|
|
background: rgba(0,0,0,0.6);
|
|
color: #fff;
|
|
font-size: 11px;
|
|
padding: 2px 8px;
|
|
border-radius: 10px;
|
|
z-index: 2;
|
|
}
|
|
|
|
.markdown-body { font-size: 14px; }
|
|
.markdown-body p { margin: 4px 0; }
|
|
.markdown-body ul, .markdown-body ol { margin: 4px 0; padding-left: 20px; }
|
|
.markdown-body h1, .markdown-body h2, .markdown-body h3 { font-size: 15px; margin: 8px 0 4px; }
|
|
|
|
/* Site header — wraps to two rows on narrow viewports instead of overflowing */
|
|
.site-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
gap: 8px 16px;
|
|
height: auto;
|
|
min-height: 64px;
|
|
padding: 12px 24px;
|
|
line-height: normal;
|
|
}
|
|
|
|
.site-header-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* The mark holds its size while the wordmark absorbs the squeeze. */
|
|
.site-header-brand svg {
|
|
flex: none;
|
|
}
|
|
|
|
.site-header-title {
|
|
margin: 0 !important;
|
|
font-size: clamp(16px, 5vw, 24px);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
.site-header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.site-header {
|
|
padding: 10px 12px;
|
|
}
|
|
.site-header-actions .ant-btn {
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
font-size: 13px;
|
|
}
|
|
}
|
|
/* Filter bar: the Filters button sits inline with the active-filter chips on
|
|
desktop; on a narrow screen the chips wrap onto their own line beneath it. */
|
|
.filter-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.active-filter-chips {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 4px;
|
|
}
|
|
|
|
.active-filter-chips .ant-tag {
|
|
margin-inline-end: 0;
|
|
}
|
|
|
|
.item-category {
|
|
display: block;
|
|
font-size: 12px;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.item-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 4px 0;
|
|
margin: 8px 0 4px;
|
|
}
|
|
|
|
@media (max-width: 575px) {
|
|
.filter-bar {
|
|
align-items: flex-start;
|
|
}
|
|
.filter-bar > .ant-btn {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/* Admin inventory filter row — always visible above the table, wrapping onto
|
|
further lines on narrow screens rather than scrolling horizontally. */
|
|
.inventory-filters {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
/* Admin category tree node: name, item count, then actions pushed to the
|
|
right. Without this the three run together as one unbroken string. */
|
|
.admin-category-node {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
width: 100%;
|
|
}
|
|
|
|
.admin-category-node > span:first-child {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.admin-category-node .ant-space {
|
|
margin-left: auto;
|
|
}
|
|
|
|
/* Item card title row: name on the left, favorite toggle on the right. */
|
|
.item-card-title {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
}
|
|
|
|
.item-card-title .ant-typography {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* Admin overlays on a phone.
|
|
|
|
antd treats a Modal's `width` as a fixed pixel value and does not adapt it to
|
|
the viewport, so the item editor's `width={720}` in Admin.tsx still lays out
|
|
at 720px on a ~390px screen. The footer's OK and Cancel are right-aligned
|
|
inside that 720px and the close X sits in its top-right corner, so all three
|
|
land off-screen — and antd sets `overflow: hidden` on the body while a modal
|
|
is open, so the page behind cannot be scrolled to reach them either. That
|
|
left a dialog with no way out except the browser's back button (#314).
|
|
|
|
Capped here rather than at each call site so that `Modal.confirm`, which has
|
|
no call site to edit, is covered by the same rule. `max-width` beats the
|
|
inline `width` antd writes on the element, so none of this needs
|
|
`!important`. */
|
|
@media (max-width: 767px) {
|
|
.ant-modal {
|
|
max-width: calc(100vw - 16px);
|
|
margin: 8px auto;
|
|
top: 8px;
|
|
}
|
|
|
|
/* The footer is a sibling of the body, not inside it, so bounding the body is
|
|
what keeps OK and Cancel on screen: a tall form scrolls within the modal
|
|
instead of pushing the footer past the bottom of the viewport. */
|
|
.ant-modal-body {
|
|
max-height: 70vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.ant-drawer-content-wrapper {
|
|
max-width: 100vw;
|
|
}
|
|
}
|