feat(frontend): mobile manga detail with hero + sticky CTA (0.58.0)
Phase 3 of the mobile redesign: the manga detail page gets a mobile-
native chrome — blurred-cover hero with a transparent app bar overlay,
secondary actions in an overflow Sheet, a 3-line description clamp
with Read more, and a sticky bottom CTA whose wording reflects read
progress. Desktop layout is untouched.
- /manga/[id] joins the layout's HIDE_MOBILE_CHROME route set so the
global AppBar and BottomNav step aside for the page-specific hero
chrome. A new `html[data-mobile-full-bleed]` opt-in zeroes main's
mobile top padding without touching horizontal/bottom gutters.
- The hero block (mobile-only via CSS) renders a blurred cover
backdrop, a transparent app bar (back / bookmark / overflow ⋯), and
a sharp cover thumbnail beside the title, authors, and status. The
bookmark icon swaps between Bookmark and BookmarkCheck based on the
current bookmark state — reusing the existing toggleBookmark flow.
- Secondary actions (Add to collection / Edit / Upload chapter /
Force resync) move into an overflow Sheet opened from the ⋯
button. The desktop action-row stays.
- Description gets a `.clamped` modifier (-webkit-line-clamp: 3) and
a Read more toggle on mobile, gated by a >200-char heuristic so
short blurbs don't render a dangling button. Desktop shows the
full description as before.
- Sticky bottom CTA bar reads "Continue {chapterLabel}" when
data.readProgress has a chapter_id, "Read first chapter" when
the manga has chapters but no progress, and hides otherwise. The
bar honors env(safe-area-inset-bottom) so it sits above the iOS
home indicator.
- New Playwright spec covers the CTA wording state machine
(no-progress → Read first chapter, has-progress → Continue),
Read more expand/collapse, overflow sheet contents, and a desktop
regression for the unchanged action-row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -87,9 +87,13 @@
|
||||
// is unaffected; it continues to follow the reader fullscreen slide
|
||||
// transform.
|
||||
const HIDE_MOBILE_CHROME_PATHS = new Set(['/login', '/register']);
|
||||
const HIDE_MOBILE_CHROME_ROUTES = new Set([
|
||||
'/manga/[id]',
|
||||
'/manga/[id]/chapter/[chapter_id]'
|
||||
]);
|
||||
const showMobileChrome = $derived.by(() => {
|
||||
if (HIDE_MOBILE_CHROME_PATHS.has($page.url.pathname)) return false;
|
||||
if ($page.route?.id === '/manga/[id]/chapter/[chapter_id]') return false;
|
||||
if (HIDE_MOBILE_CHROME_ROUTES.has($page.route?.id ?? '')) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -381,6 +385,18 @@
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
:global(html[data-mobile-full-bleed='true']) main {
|
||||
/* Mobile detail page surrenders the layout's top reservation
|
||||
so its hero can sit at the viewport top under a transparent
|
||||
page-specific app bar. Horizontal and bottom padding are
|
||||
preserved so the body content (description, chapter list)
|
||||
keeps its gutters; the hero negates the horizontal padding
|
||||
itself via negative margins. */
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile chrome (AppBar + BottomNav) is rendered always when the
|
||||
route opts in, but CSS-hides above the breakpoint so the desktop
|
||||
header is the only visible top-chrome. The ResizeObserver in the
|
||||
|
||||
Reference in New Issue
Block a user