fix(reader): texture the scrolled-mode top inset mask, closes #4486 (#4563)

In scrolled mode the notch-area masks the top safe-area inset with
opaque bg-base-100 so content scrolling under the status bar is hidden,
but it painted over the background texture (.foliate-viewer::before at
the z-0 layer), leaving a flat untextured strip across the unsafe
header area.

Give the mask its own texture ::before (.notch-masked in textures.ts)
and make the element span the grid cell, clipped down to the inset
strip with clip-path — background-size cover/contain resolves against
the element box, so the full-cell box is what keeps the mask's tiles
aligned with the viewer's at the seam. clip-path also clips
hit-testing, so the click target stays the inset strip only.

Verified on a Xiaomi 13: the strip now renders the texture with a
pixel-continuous seam (row-to-row MAE at the boundary dropped from
11913 to 230, the level of ordinary texture rows), and
elementsFromPoint confirms the notch is hit-testable only inside the
strip.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-12 23:39:17 +08:00
committed by GitHub
parent 59d4f0aa33
commit ee01fcd123
4 changed files with 99 additions and 4 deletions
@@ -56,14 +56,20 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
<>
<div
className={clsx(
'notch-area absolute left-0 right-0 top-0 z-10',
isScrolled && !isVertical && 'bg-base-100',
// Spans the grid cell and clips down to the top inset strip so the
// texture ::before (.notch-masked, see styles/textures.ts) shares
// .foliate-viewer::before's paint box — background-size cover/contain
// resolves against the element box, so a strip-sized box would
// mis-tile at the seam (#4486). clip-path also clips hit-testing,
// keeping the click target the inset strip only.
'notch-area absolute inset-0 z-10',
isScrolled && !isVertical && 'notch-masked bg-base-100',
)}
role='none'
tabIndex={-1}
onClick={handleNotchClick}
style={{
height: `${topInset}px`,
clipPath: `inset(0 0 calc(100% - ${topInset}px) 0)`,
}}
/>
<div