fix(frontend): mobile layout polish + overflow handling (0.60.1)
Visual cleanup pass after wiring up real data — the placeholder
content used during the five mobile phases hid a few sharp edges
that became obvious as soon as user-supplied tags / author handles
/ descriptions hit the screen.
- Mobile gutter unified at 16px (was 12px) — matches iOS / Material
standard and gives chips, description text, and chapter rows
visible breathing room from the screen edge. Hero negative margin
in /manga/[id] tracks the change so the bleed still hits viewport.
- Catalog grid switched from 2 columns to 4 with
`repeat(4, minmax(0, 1fr))` (per user preference). The `minmax(0,
...)` is load-bearing — without it the implicit `minmax(auto, 1fr)`
lets a long single-word card title push the column past the
viewport edge. MangaCard's `.author` and `.genres` lines hide
below 640px so every card in the 4-col layout has identical height
(cover + 2-line title clamp).
- Same minmax(0, 1fr) trick applied to the detail page's
`.overview` grid track, plus defensive `min-width: 0` /
`max-width: 100%` on `.meta` and `.chip-row` so a long
unbreakable tag or romanized URL can't drag the metadata column
past the article gutter.
- BottomNav swapped from `grid-auto-columns` (= `minmax(auto, 1fr)`)
to an explicit `repeat(4, minmax(0, 1fr))`, plus `min-width: 0` on
`.tab` and an ellipsizing `.label` so a long tab label stays
inside the bar. The Browse placeholder is replaced with Upload —
a real action, no longer a no-op duplicate of Home.
- Hero appbar gets 16px all-around inset (was 8px) so the back / ⋯
circle buttons aren't kissing the screen edge or the hero top.
Hero content gets 20px horizontal padding so the cover thumb +
title sit visibly off the hero scrim. Description gains
`overflow-wrap: anywhere` for long URLs / romanized titles.
- Chip now allows `overflow-wrap: anywhere` on its label and uses
`max-width: 100%` + `min-width: 0` so a long token wraps to its
own line within the chip-row and ellipsizes inside the chip.
- Mobile catalog search-row gains `flex-basis: calc(100% - 36px -
--space-2)` so the search input takes its own line — the Filter
and Sort chips wrap to a second row instead of squeezing the
input down to a few characters.
- Global safety net: `html, body { overflow-x: hidden }` below
640px so any future bug that lets an element overflow the
viewport doesn't expose touch-pan that shifts the whole layout.
Fixed descendants (bottom nav, CTA bar) are unaffected.
- audit.mjs — small dev helper that walks all 12 user-facing
routes at 390px and reports horizontal overflow, internal
scrollers, and edge-bleeding controls. Used to drive these fixes
and useful for future mobile changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,9 +73,12 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: var(--z-sticky);
|
||||
/* Explicit columns with `minmax(0, 1fr)` so a single wide tab
|
||||
label can't push the bar past the viewport edge. `grid-auto-
|
||||
columns: 1fr` resolves to `minmax(auto, 1fr)`, whose auto
|
||||
min == intrinsic content width — exactly what we don't want. */
|
||||
display: grid;
|
||||
grid-auto-columns: 1fr;
|
||||
grid-auto-flow: column;
|
||||
grid-template-columns: repeat(var(--bottom-nav-tabs, 4), minmax(0, 1fr));
|
||||
background: var(--surface);
|
||||
border-top: 1px solid var(--border);
|
||||
/* Safe-area inset lifts the bar above the iOS home indicator
|
||||
@@ -101,6 +104,9 @@
|
||||
gap: 2px;
|
||||
padding: var(--space-2) var(--space-1);
|
||||
min-height: 56px;
|
||||
/* Mirror the grid-column min-width:0 escape so a long label
|
||||
clips with the cell rather than expanding it. */
|
||||
min-width: 0;
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition);
|
||||
@@ -116,8 +122,12 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
max-width: 100%;
|
||||
font-size: var(--font-xs);
|
||||
font-weight: var(--weight-medium);
|
||||
line-height: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -69,7 +69,11 @@
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
/* Cap the chip at its row width so a long author handle or
|
||||
tag slug doesn't push past the chip-row's edge. min-width: 0
|
||||
is the flex-item escape so it can actually shrink. */
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
a.chip:hover {
|
||||
@@ -91,8 +95,14 @@
|
||||
}
|
||||
|
||||
.chip-label {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
/* `anywhere` lets a no-space token (a romanized title, a tag
|
||||
slug) break before it ellipsizes — keeps the chip inside
|
||||
its row even when the label has no soft-break points. */
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.chip-remove {
|
||||
|
||||
@@ -93,6 +93,11 @@
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
list-style: none;
|
||||
/* Grid items default to min-width: auto which equals the
|
||||
intrinsic content size — long author / title strings then
|
||||
push the cell past `1fr`. Forcing 0 lets the column control
|
||||
the width and the children ellipsize inside it. */
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cover-link {
|
||||
@@ -172,6 +177,10 @@
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
/* A title made of a single unbreakable string would push the
|
||||
card past its 1fr column. `anywhere` keeps normal whitespace
|
||||
breaks preferred but lets the renderer cut a long word. */
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.title:hover {
|
||||
@@ -187,4 +196,15 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* On phones the catalog packs 4 covers per row — at that width
|
||||
the author + genre lines just become two strips of ellipsized
|
||||
noise, and dropping them keeps every cell exactly the same
|
||||
height (cover + 2-line title clamp). */
|
||||
@media (max-width: 640px) {
|
||||
.author,
|
||||
.genres {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -282,6 +282,19 @@ img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Horizontal-overflow safety net on phones. Manga apps never have a
|
||||
legitimate need for horizontal page scroll, and an off-screen
|
||||
element here often means another bug — but it's better to clip
|
||||
silently than to expose touch-pan that shifts the whole layout
|
||||
under the user's thumb. position:fixed descendants are unaffected,
|
||||
so the bottom nav / sticky CTAs still span edge-to-edge. */
|
||||
@media (max-width: 640px) {
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
|
||||
Reference in New Issue
Block a user